Skip to content

Commit 8dfef67

Browse files
authored
[FEATURE]: Show context usage in OpenCode Desktop Context usage (#5979)
1 parent 1b1b73b commit 8dfef67

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

packages/desktop/src/components/prompt-input.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useProviders } from "@/hooks/use-providers"
2222
import { useCommand } from "@/context/command"
2323
import { persisted } from "@/utils/persist"
2424
import { Identifier } from "@/utils/id"
25+
import { SessionContextUsage } from "@/components/session-context-usage"
2526

2627
const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]
2728
const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"]
@@ -1034,6 +1035,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
10341035
</Tooltip>
10351036
</Match>
10361037
</Switch>
1038+
<SessionContextUsage />
10371039
</div>
10381040
<div class="flex items-center gap-1 absolute right-2 bottom-2">
10391041
<input
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { createMemo, Show } from "solid-js"
2+
import { Tooltip } from "@opencode-ai/ui/tooltip"
3+
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
4+
import { useSync } from "@/context/sync"
5+
import { useParams } from "@solidjs/router"
6+
import { AssistantMessage } from "@opencode-ai/sdk/v2"
7+
8+
export function SessionContextUsage() {
9+
const sync = useSync()
10+
const params = useParams()
11+
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
12+
13+
const cost = createMemo(() => {
14+
const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
15+
return new Intl.NumberFormat("en-US", {
16+
style: "currency",
17+
currency: "USD",
18+
}).format(total)
19+
})
20+
21+
const context = createMemo(() => {
22+
const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage
23+
if (!last) return
24+
const total =
25+
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
26+
const model = sync.data.provider.all.find((x) => x.id === last.providerID)?.models[last.modelID]
27+
return {
28+
tokens: total.toLocaleString(),
29+
percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null,
30+
}
31+
})
32+
33+
return (
34+
<Show when={context?.()}>
35+
{(ctx) => (
36+
<Tooltip
37+
openDelay={300}
38+
value={
39+
<div class="flex flex-col gap-1 p-2">
40+
<div class="flex justify-between gap-4">
41+
<span class="text-text-weaker">Tokens</span>
42+
<span class="text-text-strong">{ctx().tokens}</span>
43+
</div>
44+
<div class="flex justify-between gap-4">
45+
<span class="text-text-weaker">Usage</span>
46+
<span class="text-text-strong">{ctx().percentage ?? 0}%</span>
47+
</div>
48+
<div class="flex justify-between gap-4">
49+
<span class="text-text-weaker">Cost</span>
50+
<span class="text-text-strong">{cost()}</span>
51+
</div>
52+
</div>
53+
}
54+
placement="top"
55+
>
56+
<div class="flex items-center gap-1">
57+
<span class="text-12-medium text-text-weak">{`${ctx().percentage ?? 0}%`}</span>
58+
<ProgressCircle size={16} strokeWidth={2} percentage={ctx().percentage ?? 0} />
59+
</div>
60+
</Tooltip>
61+
)}
62+
</Show>
63+
)
64+
}

0 commit comments

Comments
 (0)