| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- import { Match, Show, Switch, createMemo } from "solid-js"
- import { Tooltip, type TooltipProps } from "@opencode-ai/ui/tooltip"
- import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
- import { Button } from "@opencode-ai/ui/button"
- import { useFile } from "@/context/file"
- import { useLayout } from "@/context/layout"
- import { useSync } from "@/context/sync"
- import { useLanguage } from "@/context/language"
- import { useProviders } from "@/hooks/use-providers"
- import { getSessionContextMetrics } from "@/components/session/session-context-metrics"
- import { useSessionLayout } from "@/pages/session/session-layout"
- import { createSessionTabs } from "@/pages/session/helpers"
- interface SessionContextUsageProps {
- variant?: "button" | "indicator"
- placement?: TooltipProps["placement"]
- }
- function openSessionContext(args: {
- view: ReturnType<ReturnType<typeof useLayout>["view"]>
- layout: ReturnType<typeof useLayout>
- tabs: ReturnType<ReturnType<typeof useLayout>["tabs"]>
- }) {
- if (!args.view.reviewPanel.opened()) args.view.reviewPanel.open()
- if (args.layout.fileTree.opened() && args.layout.fileTree.tab() !== "all") args.layout.fileTree.setTab("all")
- void args.tabs.open("context")
- args.tabs.setActive("context")
- }
- export function SessionContextUsage(props: SessionContextUsageProps) {
- const sync = useSync()
- const file = useFile()
- const layout = useLayout()
- const language = useLanguage()
- const providers = useProviders()
- const { params, tabs, view } = useSessionLayout()
- const variant = createMemo(() => props.variant ?? "button")
- const tabState = createSessionTabs({
- tabs,
- pathFromTab: file.pathFromTab,
- normalizeTab: (tab) => (tab.startsWith("file://") ? file.tab(tab) : tab),
- })
- const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
- const usd = createMemo(
- () =>
- new Intl.NumberFormat(language.intl(), {
- style: "currency",
- currency: "USD",
- }),
- )
- const metrics = createMemo(() => getSessionContextMetrics(messages(), providers.all()))
- const context = createMemo(() => metrics().context)
- const cost = createMemo(() => {
- return usd().format(metrics().totalCost)
- })
- const openContext = () => {
- if (!params.id) return
- if (tabState.activeTab() === "context") {
- tabs().close("context")
- return
- }
- openSessionContext({
- view: view(),
- layout,
- tabs: tabs(),
- })
- }
- const circle = () => (
- <div class="flex items-center justify-center">
- <ProgressCircle size={16} strokeWidth={2} percentage={context()?.usage ?? 0} />
- </div>
- )
- const tooltipValue = () => (
- <div>
- <Show when={context()}>
- {(ctx) => (
- <>
- <div class="flex items-center gap-2">
- <span class="text-text-invert-strong">{ctx().total.toLocaleString(language.intl())}</span>
- <span class="text-text-invert-base">{language.t("context.usage.tokens")}</span>
- </div>
- <div class="flex items-center gap-2">
- <span class="text-text-invert-strong">{ctx().usage ?? 0}%</span>
- <span class="text-text-invert-base">{language.t("context.usage.usage")}</span>
- </div>
- </>
- )}
- </Show>
- <div class="flex items-center gap-2">
- <span class="text-text-invert-strong">{cost()}</span>
- <span class="text-text-invert-base">{language.t("context.usage.cost")}</span>
- </div>
- </div>
- )
- return (
- <Show when={params.id}>
- <Tooltip value={tooltipValue()} placement={props.placement ?? "top"}>
- <Switch>
- <Match when={variant() === "indicator"}>{circle()}</Match>
- <Match when={true}>
- <Button
- type="button"
- variant="ghost"
- class="size-6"
- onClick={openContext}
- aria-label={language.t("context.usage.view")}
- >
- {circle()}
- </Button>
- </Match>
- </Switch>
- </Tooltip>
- </Show>
- )
- }
|