@cassius0924/dsh-usage-dashboard 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/index.tsx", "../src/client/boundary.tsx", "../src/client/cache.ts", "../src/contract.ts", "../src/client/api.ts", "../src/client/dashboard.tsx", "../src/client/budget.ts", "../src/client/charts.tsx", "../src/client/chart-focus.ts", "../src/client/i18n.tsx", "../src/client/locales.ts", "../src/client/dom.ts", "../src/client/export.ts", "../src/client/freshness.ts", "../src/client/metric.ts", "../src/client/prefs.ts", "../src/client/store.ts", "../src/client/message-cost.tsx", "../src/client/styles.ts", "../src/client/widget.tsx", "../src/client/views.ts"],
4
- "sourcesContent": ["/**\n * dsh-usage-dashboard client half: mounts the floating balance widget\n * (`shell.overlay`) and the \u300C\u989D\u5EA6\u300D view tab (`conversation.view`).\n * Data arrives through the package's own host API via same-origin fetch.\n */\nimport { ErrorBoundary } from './boundary.tsx'\nimport type { ClientContext } from './context.ts'\nimport { fetchBalance, fetchUsage } from './api.ts'\nimport { BalanceDashboard } from './dashboard.tsx'\nimport { LocaleProvider, type Translate } from './i18n.tsx'\nimport { en, NS, zh } from './locales.ts'\nimport { MessageCost } from './message-cost.tsx'\nimport { css } from './styles.ts'\nimport { QuotaWidget, type UseSessionsHook } from './widget.tsx'\nimport { conversationViewsSource } from './views.ts'\n\n/** Plugin identity for the client bundle. */\nexport const name = 'dsh-usage-dashboard'\n\n/** Services required before load. */\nexport const inject = ['slots', 'locale']\n\n/**\n * Mount the two surfaces.\n * @param ctx - client Cordis context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-usage-dashboard: browser dictionaries')\n const t = ctx.locale.bind(NS)\n const views = conversationViewsSource(ctx.slots)\n // One stylesheet for the whole plugin; the module loader claims and removes\n // plugin-owned style tags on unload.\n if (typeof document !== 'undefined') {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-usage-dashboard'\n tag.textContent = css\n document.head.appendChild(tag)\n }\n\n // Warm both caches the moment the client bundle loads, rather than waiting\n // for the \u300C\u989D\u5EA6\u300D tab to mount. Usage aggregation alone takes seconds (it\n // replays every session log), so without this the tab's first open always\n // paid that latency; with it, by the time someone actually clicks the tab\n // the fetch has usually already landed and the tab renders from cache. Not\n // awaited and not surfaced: a failure here just leaves the widget/tab to\n // fetch (and report) it themselves on mount, same as before this existed.\n void fetchBalance().catch(() => {})\n void fetchUsage().catch(() => {})\n\n ctx.slots.inject('shell.overlay', () => ctx.slots.register(\n { name: 'shell.overlay', id: 'deepseek-quota', order: 1000, locale: NS, label: () => t('widget.title') },\n ({ t: slotT, useSessions }: { t: Translate; useSessions: UseSessionsHook }) => (\n <LocaleProvider t={slotT}>\n <ErrorBoundary t={slotT} silent><QuotaWidget useSessions={useSessions} views={views} /></ErrorBoundary>\n </LocaleProvider>\n ),\n ))\n\n ctx.slots.inject('conversation.chat.assistant-actions', () => ctx.slots.register(\n {\n name: 'conversation.chat.assistant-actions',\n id: 'dsh-usage-turn-cost',\n order: 5,\n locale: NS,\n label: () => t('messageCost.label'),\n },\n ({ t: slotT, sessionId, messageId }: { t: Translate; sessionId: string; messageId: string }) => (\n <LocaleProvider t={slotT}>\n <ErrorBoundary t={slotT} silent>\n <MessageCost sessionId={sessionId} messageId={messageId} />\n </ErrorBoundary>\n </LocaleProvider>\n ),\n ))\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register(\n { name: 'conversation.view', id: 'balance', order: 20, locale: NS, label: () => t('nav.quota') },\n // `conversation.view` is a session-scoped slot, so the host injects its\n // standard session props (including `sessionId`) alongside `t` \u2014 this\n // plugin only destructures the two fields it needs, per the existing\n // \"hand-write the minimal structural type\" convention (see context.ts).\n // `sessionId` is typed optional defensively even though the host is\n // expected to always supply it: BalanceDashboard renders the current-\n // session card only when it actually has one, never throws either way.\n ({ t: slotT, sessionId }: { t: Translate; sessionId?: string }) => (\n <LocaleProvider t={slotT}>\n <ErrorBoundary t={slotT}><BalanceDashboard sessionId={sessionId} views={views} /></ErrorBoundary>\n </LocaleProvider>\n ),\n ))\n}\n", "/**\n * Error boundary for the plugin's two slot entries.\n *\n * A slot component that throws while rendering takes its whole subtree down \u2014\n * for the \u300C\u989D\u5EA6\u300D view that meant a blank tab, and the only thing that fixed it\n * was a browser profile with no localStorage. A crash should degrade to\n * something the user can act on, and it should never be the tab's silent\n * default state.\n */\nimport { Component, type ErrorInfo, type ReactNode } from 'react'\nimport type { Translate } from './i18n.tsx'\n\nconst PREFIX = 'dsh-usage-dashboard:'\n\n/** Drop every key this plugin owns \u2014 caches and preferences alike. */\nfunction clearPluginStorage(): void {\n if (typeof localStorage === 'undefined') return\n try {\n for (const key of Object.keys(localStorage)) {\n if (key.startsWith(PREFIX)) localStorage.removeItem(key)\n }\n } catch {\n // Nothing better to do; the reload below is still worth attempting.\n }\n}\n\ninterface Props {\n /** Shown instead of the fallback UI when the surface has no room for it. */\n silent?: boolean\n t: Translate\n children: ReactNode\n}\n\ninterface State {\n message: string | null\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n state: State = { message: null }\n\n static getDerivedStateFromError(error: unknown): State {\n return { message: (error as { message?: string } | null)?.message ?? String(error) }\n }\n\n componentDidCatch(error: unknown, info: ErrorInfo): void {\n console.error('[dsh-usage-dashboard] render failed', error, info.componentStack)\n }\n\n private readonly reset = (): void => {\n clearPluginStorage()\n location.reload()\n }\n\n render(): ReactNode {\n const { message } = this.state\n if (message === null) return this.props.children\n if (this.props.silent === true) return null\n const { t } = this.props\n return (\n <div className=\"dq-balance\">\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('boundary.title')}</div>\n <p className=\"dq-empty\">{t('boundary.body')}</p>\n <pre className=\"dq-crash\">{message}</pre>\n <button type=\"button\" className=\"dq-refresh-btn\" onClick={this.reset}>\n {t('boundary.reset')}\n </button>\n </div>\n </div>\n )\n }\n}\n", "/**\n * Package-local TTL caches shared by the widget, the dashboard, and every\n * tab switch. Cached values outlive the TTL: `get()` still returns them\n * (marked by `at`) so a reopening tab can render instantly and refresh in\n * the background, while `getFresh()` is the cache-aware fetch path.\n *\n * When a `storageKey` is given, every `put()` also mirrors the entry to\n * localStorage so a page refresh keeps the instant first paint (the data\n * shown is the last known good value; a background refresh updates it).\n */\n\n/** Bump whenever the persisted shape changes in a way `isUsable` cannot catch. */\nconst LS_VERSION = 2\n\nexport interface CacheHit<T> {\n data: T\n at: number\n}\n\nexport interface ResourceCache<T> {\n /** Last cached value even when stale; null when never cached. */\n get(): CacheHit<T> | null\n /** Value only when it is still within the TTL. */\n getFresh(): T | null\n put(data: T): void\n}\n\n/**\n * Read a persisted entry, but only trust it if it still matches the shape the\n * current code reads. A payload written by an older build can be missing fields\n * this one dereferences, and because the first render is seeded straight from\n * here, an unchecked stale entry crashes the view \u2014 for the user it looks like\n * a blank tab that only a fresh browser profile fixes.\n */\nfunction loadPersisted<T>(storageKey: string, isUsable?: (data: T) => boolean): CacheHit<T> | null {\n if (typeof localStorage === 'undefined') return null\n const key = `${storageKey}:v${LS_VERSION}`\n try {\n const raw = localStorage.getItem(key)\n if (raw === null) return null\n const parsed = JSON.parse(raw) as { data?: T; at?: number }\n if (parsed.data === undefined || typeof parsed.at !== 'number') return null\n if (isUsable !== undefined && !isUsable(parsed.data)) {\n localStorage.removeItem(key)\n return null\n }\n return { data: parsed.data, at: parsed.at }\n } catch {\n return null\n }\n}\n\n/** Drop entries this key wrote under earlier versions; nothing reads them again. */\nfunction pruneOldVersions(storageKey: string): void {\n if (typeof localStorage === 'undefined') return\n try {\n const current = `${storageKey}:v${LS_VERSION}`\n for (const key of Object.keys(localStorage)) {\n if (key.startsWith(`${storageKey}:v`) && key !== current) localStorage.removeItem(key)\n }\n } catch {\n // Leaving stale keys behind is harmless; they are never read.\n }\n}\n\nfunction persist<T>(storageKey: string, hit: CacheHit<T>): void {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(`${storageKey}:v${LS_VERSION}`, JSON.stringify(hit))\n } catch {\n // Quota / private-mode failures must never break the in-memory path.\n }\n}\n\nexport function createCache<T>(ttlMs: number, storageKey?: string, isUsable?: (data: T) => boolean): ResourceCache<T> {\n if (storageKey !== undefined) pruneOldVersions(storageKey)\n let hit: CacheHit<T> | null = storageKey === undefined ? null : loadPersisted<T>(storageKey, isUsable)\n return {\n get: () => hit,\n getFresh: () => (hit !== null && Date.now() - hit.at < ttlMs ? hit.data : null),\n put: (data) => {\n hit = { data, at: Date.now() }\n if (storageKey !== undefined) persist(storageKey, hit)\n },\n }\n}\n", "/** Wire contracts shared by the host API and the client fetchers. */\n\nexport interface BalanceInfo {\n currency: string\n total: string\n granted: string\n toppedUp: string\n}\n\nexport interface BalanceData {\n isAvailable: boolean\n balances: BalanceInfo[]\n}\n\nexport interface BalanceResponse {\n ok: boolean\n error?: string\n data?: BalanceData\n}\n\nexport interface DailyUsage {\n date: string\n input: number\n output: number\n cache: number\n total: number\n cost: number\n calls: number\n}\n\nexport interface HourlyUsage {\n hour: number\n total: number\n cost: number\n calls: number\n}\n\nexport interface HeatmapUsage {\n date: string\n total: number\n cost: number\n calls: number\n}\n\nexport interface UsageTotals {\n input: number\n output: number\n cache: number\n reasoning: number\n total: number\n cost: number\n calls: number\n /**\n * What the cache-hit tokens would have cost at cache-miss rates, minus what\n * they actually cost \u2014 i.e. the money prefix caching saved. Folded per\n * record so it respects each record's model and time-of-day rates.\n */\n cacheSavings: number\n}\n\n/** One per-day / per-hour point of a single model's series. */\nexport interface ModelSeriesPoint {\n total: number\n cost: number\n calls: number\n}\n\n/** Per-model usage aggregation: overall totals plus 30-day and 24-hour series. */\nexport interface ModelUsage {\n provider: string\n model: string\n input: number\n output: number\n cache: number\n total: number\n cost: number\n calls: number\n daily: ModelSeriesPoint[]\n hourly: ModelSeriesPoint[]\n}\n\n/** Usage rolled up over one time window. */\nexport interface PeriodUsage {\n total: number\n cost: number\n calls: number\n}\n\n/**\n * Time-anchored rollups. \"Accumulated since forever\" answers no question a\n * user actually has; these do: what did today cost, what has this month cost,\n * and is that more or less than the comparable window before it.\n */\nexport interface UsageSummary {\n today: PeriodUsage\n yesterday: PeriodUsage\n /** Current calendar month, to date. */\n month: PeriodUsage\n /** Previous calendar month up to the same day-of-month, so a mid-month\n * comparison is against a same-length window rather than a full month. */\n lastMonthToDate: PeriodUsage\n}\n\n/** CNY per 1M tokens for one model tier. */\nexport interface PricingRates {\n cacheHit: number\n input: number\n output: number\n}\n\nexport interface PricingTier {\n model: string\n peak: PricingRates\n offPeak: PricingRates\n}\n\n/** What the cost column was computed with, so the estimate can be audited. */\nexport interface PricingInfo {\n currency: string\n /** Date peak/off-peak pricing took effect; earlier usage is costed at the\n * flat rates that were actually charged then. */\n switchDate: string\n inPeakNow: boolean\n peakWindows: string[]\n tiers: PricingTier[]\n}\n\n/**\n * How usage divides between DeepSeek's peak and off-peak windows, plus the\n * same usage re-priced as if everything had landed off-peak \u2014 answering\n * \"what would shifting batch work off-peak save me\" under the peak/off-peak\n * table in effect since 2026-08-17.\n */\nexport interface PeakSplit {\n peak: PeriodUsage\n offPeak: PeriodUsage\n /** Every record priced under the peak/off-peak table, as actually classified. */\n peakEraCost: number\n /** Every record priced under that table as if it had landed off-peak. */\n offPeakEraCost: number\n}\n\n/** One session's share of the bill. */\nexport interface SessionCost {\n id: string\n /** The session's title from its log; falls back to a short id. */\n title: string\n total: number\n cost: number\n calls: number\n /** Epoch ms of the last usage record in this session. */\n lastActive: number\n}\n\n/**\n * Multimodal (image) usage, folded from the same log replay: image blocks in\n * user messages and tool results are counted and sized, then estimated with\n * the official resizing rule (see `estimateImageTokens`). Each image is\n * attributed to the next usage record \u2014 the model call whose prompt carried\n * it \u2014 and priced at that call's input (cache-miss) rate, so the share answer\n * \"how much of the bill came from pictures\" has a real number.\n */\nexport interface VisionStats {\n /** Image blocks counted in the scope. */\n images: number\n /** Estimated image tokens under the official conversion rule. */\n imageTokens: number\n /** Estimated CNY cost of those image tokens at the consuming calls' rates. */\n cost: number\n /** Total image payload bytes when the attachment refs carried a size. */\n bytes: number\n}\n\n/** One day's image usage for the per-window / per-day series. */\nexport interface VisionDayPoint {\n date: string\n images: number\n imageTokens: number\n cost: number\n}\n\n/** One session's image usage, ranked for \"which run kept sending pictures\". */\nexport interface VisionSession {\n id: string\n title: string\n images: number\n imageTokens: number\n cost: number\n}\n\n/**\n * What the local log replay could actually inspect. Totals can otherwise look\n * authoritative even when one corrupt session was skipped, so this metadata\n * travels with every usage response and lets the UI disclose partial results.\n */\nexport interface UsageCoverage {\n scope: 'local-dsh-session-logs'\n /** Session headers with a usable id returned by the local persistence. */\n listedSessions: number\n /** Session logs read successfully, including logs with no usage. */\n scannedSessions: number\n /** Valid assistant usage records folded into the totals. */\n usageRecords: number\n /** Usage records rejected because their time or token data was invalid. */\n skippedRecords: number\n /** Session logs that could not be read at all. */\n failedSessions: number\n /** Epoch ms of the first/last valid usage record across all local logs. */\n earliestAt: number | null\n latestAt: number | null\n}\n\nexport const USAGE_WINDOW_DAYS = [7, 30, 90, 365] as const\nexport type UsageWindowDays = (typeof USAGE_WINDOW_DAYS)[number]\n\n/**\n * Session ids observed in this codebase (host-issued, e.g.\n * `session-484a1c14-c6fe-4d6a-abfd-a2d8d2f664d5`) are always plain\n * ASCII-safe tokens \u2014 no documented format contract exists for them outside\n * this repo (`SessionPersistenceFace` is implemented by the DSH host, not\n * here), so this is a defensive charset whitelist rather than an\n * exact-format check: letters, digits, `-`, `_` only, with a generous length\n * cap. It exists specifically to stop path-separator / `..` / null-byte\n * payloads from ever reaching the session-log read \u2014 `id` on the\n * `/session` route arrives verbatim from the browser-controlled `?id=` query\n * parameter, unlike `fetchUsage()`'s session ids, which the host itself\n * enumerates via `persistence.list()`.\n */\nexport const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/\n\nexport const isValidSessionId = (id: string): boolean => SESSION_ID_PATTERN.test(id)\n\n/** One internally consistent view of the same local logs over a calendar window. */\nexport interface UsageWindow {\n days: UsageWindowDays\n daily: DailyUsage[]\n hourly: HourlyUsage[]\n totals: UsageTotals\n models: ModelUsage[]\n /** Most expensive sessions inside this window, capped at SESSION_TOP_N. */\n sessions: SessionCost[]\n /** All sessions that contributed usage inside this window. */\n sessionCount: number\n vision: VisionStats\n visionDaily: VisionDayPoint[]\n /** Sessions that sent the most images inside this window, capped. */\n visionSessions: VisionSession[]\n}\n\nexport interface UsageData {\n daily: DailyUsage[]\n hourly: HourlyUsage[]\n heatmap: HeatmapUsage[]\n totals: UsageTotals\n models: ModelUsage[]\n summary: UsageSummary\n pricing: PricingInfo\n peakSplit: PeakSplit\n /** Most expensive sessions first, capped \u2014 see SESSION_TOP_N. */\n sessions: SessionCost[]\n /** How many sessions contributed usage in total. */\n sessionCount: number\n coverage: UsageCoverage\n windows: UsageWindow[]\n vision: VisionStats\n visionDaily: VisionDayPoint[]\n /** Sessions that sent the most images, capped \u2014 see SESSION_TOP_N. */\n visionSessions: VisionSession[]\n}\n\nexport interface UsageResponse {\n ok: boolean\n error?: string\n data?: UsageData\n}\n\n/**\n * One model's contribution to a single session's bill \u2014 the same field\n * shape as `ModelUsage` minus the `daily`/`hourly` series, which only make\n * sense aggregated across many sessions.\n */\nexport interface SessionModelUsage {\n provider: string\n model: string\n input: number\n output: number\n cache: number\n total: number\n cost: number\n calls: number\n}\n\n/** One completed conversation turn's usage. `messageId` is the durable id of\n * the turn's final assistant message \u2014 the same identity the chat action-strip\n * slot exposes to client plugins. Tool-call steps inside the turn are folded\n * into this one figure rather than shown as misleading separate charges. */\nexport interface TurnUsage {\n messageId: string\n total: number\n cost: number\n calls: number\n}\n\n/**\n * Usage for exactly one session \u2014 what the \u300C\u989D\u5EA6\u300Dtab's own conversation\n * is costing, as opposed to `UsageData`'s account-wide totals. Fetched on\n * demand (see `fetchSessionUsage`), not part of the 5-minute `usage` memo:\n * a single session read is cheap and must reflect the session's live state.\n */\nexport interface SessionUsageData {\n sessionId: string\n /** The session's own title from its log; falls back to a short id. */\n title: string\n total: number\n cost: number\n calls: number\n /** Epoch ms of the first/last valid usage record in this session, or null\n * when the session has produced no usage yet. */\n firstActive: number | null\n lastActive: number | null\n /** Most expensive model first. */\n models: SessionModelUsage[]\n /** Completed turns, in log order, addressed by their final message id. */\n turns: TurnUsage[]\n}\n\nexport interface SessionUsageResponse {\n ok: boolean\n error?: string\n data?: SessionUsageData\n}\n", "/**\n * Same-origin fetchers for the host half's JSON API, backed by package-local\n * TTL caches (see ./cache.ts). A fresh cache hit resolves without any network\n * work, so tab switches render instantly; `force` bypasses both the memory\n * cache and the HTTP cache (fetch cache: 'no-store') and asks the host to\n * bypass its own memo via `?refresh=1`.\n */\nimport { createCache } from './cache.ts'\nimport { USAGE_WINDOW_DAYS } from '../contract.ts'\nimport type { BalanceResponse, SessionUsageResponse, UsageResponse } from '../contract.ts'\n\n/** Mirror the host memos: balance 60s, usage 5min. */\nconst BALANCE_TTL_MS = 60_000\nconst USAGE_TTL_MS = 5 * 60_000\n\nasync function getJson<T>(path: string, cache: RequestCache): Promise<T> {\n const res = await fetch(path, { headers: { accept: 'application/json' }, cache })\n if (!res.ok) {\n return { ok: false, error: `HTTP ${res.status}` } as unknown as T\n }\n return res.json() as Promise<T>\n}\n\n/**\n * Every field the UI dereferences off a cached payload. A persisted entry that\n * predates one of them would otherwise be rendered as if it were current and\n * take the whole view down; listing them here keeps that failure impossible\n * instead of merely unlikely. Add a line whenever the UI starts reading a new\n * top-level field.\n */\nconst usageIsUsable = (res: UsageResponse): boolean => {\n if (res.ok !== true) return false\n const data = res.data\n return data !== undefined\n && Array.isArray(data.daily) && Array.isArray(data.hourly) && Array.isArray(data.heatmap)\n && Array.isArray(data.models) && Array.isArray(data.sessions)\n && typeof data.sessionCount === 'number'\n && data.coverage?.scope === 'local-dsh-session-logs'\n && typeof data.coverage.scannedSessions === 'number'\n && typeof data.coverage.skippedRecords === 'number'\n && Array.isArray(data.windows) && data.windows.length === USAGE_WINDOW_DAYS.length\n && USAGE_WINDOW_DAYS.every(days => data.windows.some(window => window.days === days))\n && data.windows.every(window => Array.isArray(window.daily)\n && Array.isArray(window.hourly) && Array.isArray(window.models) && Array.isArray(window.sessions))\n && data.vision !== undefined && typeof data.vision.images === 'number'\n && typeof data.vision.imageTokens === 'number' && typeof data.vision.cost === 'number'\n && Array.isArray(data.visionDaily) && Array.isArray(data.visionSessions)\n && data.windows.every(window => window.vision !== undefined\n && Array.isArray(window.visionDaily) && Array.isArray(window.visionSessions))\n && data.totals !== undefined && typeof data.totals.cacheSavings === 'number'\n && data.summary?.today !== undefined\n && Array.isArray(data.pricing?.tiers)\n && data.peakSplit?.peak !== undefined\n}\n\nconst balanceIsUsable = (res: BalanceResponse): boolean =>\n res.ok === true && Array.isArray(res.data?.balances)\n\nconst balanceCache = createCache<BalanceResponse>(BALANCE_TTL_MS, 'dsh-usage-dashboard:balance', balanceIsUsable)\nconst usageCache = createCache<UsageResponse>(USAGE_TTL_MS, 'dsh-usage-dashboard:usage', usageIsUsable)\n\n/** Last cached value (possibly stale), for an instant first render. */\nexport const getCachedBalance = (): BalanceResponse | null => balanceCache.get()?.data ?? null\nexport const getCachedUsage = (): UsageResponse | null => usageCache.get()?.data ?? null\n/** When the cached usage was fetched, so a consumer can say how old it is. */\nexport const getCachedUsageAt = (): number | null => usageCache.get()?.at ?? null\n\n/**\n * Notified whenever usage is (re)fetched. The floating widget reads usage\n * straight from this cache rather than fetching on its own: aggregating every\n * session log takes seconds, and a background widget must not pay that\n * repeatedly. It subscribes here so it still updates when the dashboard \u2014 or\n * its own refresh button \u2014 brings fresh data in.\n */\nconst usageListeners = new Set<() => void>()\n\nexport const subscribeUsage = (fn: () => void): (() => void) => {\n usageListeners.add(fn)\n return () => {\n usageListeners.delete(fn)\n }\n}\n\n// Non-force calls made while an identical one is already in flight join it\n// instead of firing a second request \u2014 the case that matters is the\n// preload kicked off at plugin load (see index.tsx) landing moments before\n// the widget's or the dashboard's own mount-time fetch. `force` (the\n// refresh button) always fires its own request, since the user asked for a\n// bypass specifically.\nlet balanceInflight: Promise<BalanceResponse> | null = null\nlet usageInflight: Promise<UsageResponse> | null = null\n\nexport async function fetchBalance(force = false): Promise<BalanceResponse> {\n if (!force) {\n const hit = balanceCache.getFresh()\n if (hit !== null) return hit\n if (balanceInflight !== null) return balanceInflight\n }\n const suffix = force ? '?refresh=1' : ''\n const task = getJson<BalanceResponse>(`/api/dsh-usage-dashboard/balance${suffix}`, force ? 'no-store' : 'default')\n .then(res => {\n if (res.ok) balanceCache.put(res)\n return res\n })\n if (!force) {\n balanceInflight = task.finally(() => { balanceInflight = null })\n return balanceInflight\n }\n return task\n}\n\nexport async function fetchUsage(force = false): Promise<UsageResponse> {\n if (!force) {\n const hit = usageCache.getFresh()\n if (hit !== null) return hit\n if (usageInflight !== null) return usageInflight\n }\n const suffix = force ? '?refresh=1' : ''\n // `cache: 'no-store'` on every path, not just force: the host memoizes the\n // expensive replay anyway, and our own TTL cache prevents repeat fetches \u2014\n // but the browser's HTTP cache would happily serve a payload captured before\n // a host upgrade (the old body lacks fields the current UI reads, e.g. the\n // vision block) for up to the max-age it was tagged with.\n const task = getJson<UsageResponse>(`/api/dsh-usage-dashboard/usage${suffix}`, 'no-store')\n .then(res => {\n // Only shapes the current UI dereferences may enter the cache. A host\n // still serving a pre-vision payload (not yet restarted) is therefore\n // never persisted \u2014 the dashboard renders it defensively instead of\n // crashing on the next mount.\n if (res.ok && usageIsUsable(res)) {\n usageCache.put(res)\n for (const listener of [...usageListeners]) listener()\n }\n return res\n })\n if (!force) {\n usageInflight = task.finally(() => { usageInflight = null })\n return usageInflight\n }\n return task\n}\n\n/**\n * Session usage stays memory-only: the widget, dashboard, and every visible\n * turn-cost cell share the latest read without persisting one entry per\n * session. A newly completed turn whose message id is absent from this cache\n * triggers one fresh read; the in-flight map coalesces all cells mounting in\n * the same transcript render.\n */\nconst sessionUsageInflight = new Map<string, Promise<SessionUsageResponse>>()\nconst sessionUsageCache = new Map<string, SessionUsageResponse>()\nconst sessionUsageListeners = new Map<string, Set<() => void>>()\n\nexport const getCachedSessionUsage = (sessionId: string): SessionUsageResponse | null =>\n sessionUsageCache.get(sessionId) ?? null\n\nexport const subscribeSessionUsage = (sessionId: string, fn: () => void): (() => void) => {\n let listeners = sessionUsageListeners.get(sessionId)\n if (listeners === undefined) {\n listeners = new Set()\n sessionUsageListeners.set(sessionId, listeners)\n }\n listeners.add(fn)\n return () => {\n listeners?.delete(fn)\n if (listeners?.size === 0) sessionUsageListeners.delete(sessionId)\n }\n}\n\nconst publishSessionUsage = (sessionId: string, response: SessionUsageResponse): void => {\n if (!response.ok) return\n sessionUsageCache.set(sessionId, response)\n for (const listener of [...(sessionUsageListeners.get(sessionId) ?? [])]) listener()\n}\n\nexport async function fetchSessionUsage(sessionId: string, force = false): Promise<SessionUsageResponse> {\n if (typeof sessionId !== 'string' || sessionId === '') {\n return { ok: false, error: '\u7F3A\u5C11\u4F1A\u8BDD id' }\n }\n if (!force) {\n const inflight = sessionUsageInflight.get(sessionId)\n if (inflight !== undefined) return inflight\n }\n const task = getJson<SessionUsageResponse>(`/api/dsh-usage-dashboard/session?id=${encodeURIComponent(sessionId)}`, 'no-store')\n .then(response => {\n publishSessionUsage(sessionId, response)\n return response\n })\n if (force) return task\n const tracked = task.finally(() => {\n if (sessionUsageInflight.get(sessionId) === tracked) sessionUsageInflight.delete(sessionId)\n })\n sessionUsageInflight.set(sessionId, tracked)\n return tracked\n}\n", "/** The \u300C\u989D\u5EA6\u300D conversation view tab: full balance & usage dashboard.\n *\n * Caching: the first render comes straight from the package-local cache\n * (see ./api.ts), so reopening the tab shows data instantly. A background\n * refresh then updates it without blocking; a full-screen \u52A0\u8F7D\u4E2D only\n * appears when nothing has ever been cached.\n */\nimport { Fragment, useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type ReactElement, type ReactNode } from 'react'\nimport { USAGE_WINDOW_DAYS } from '../contract.ts'\nimport { fetchBalance, fetchSessionUsage, fetchUsage, getCachedBalance, getCachedUsage, getCachedUsageAt } from './api.ts'\nimport { budgetSnapshot } from './budget.ts'\nimport { Bars, GroupedBars, Heatmap, MODEL_COLORS, fmt, fmtCompact, fmtInt } from './charts.tsx'\nimport { getComposerElement, getComposerInputElement, getShellFrame } from './dom.ts'\nimport { dailyUsageCsv, downloadText, exportDateStamp, fullUsageJson, modelUsageCsv } from './export.ts'\nimport { syncStatusText, type SyncState } from './freshness.ts'\nimport { fallbackT, localizeApiError, useI18n, type Translate } from './i18n.tsx'\nimport { CHART_METRICS, chartMetricName, chartMetricValue, type ChartMetric } from './metric.ts'\nimport type { BalanceData, ModelUsage, PeakSplit, PeriodUsage, PricingInfo, SessionCost, SessionUsageData, UsageCoverage, UsageData, UsageWindowDays, VisionDayPoint, VisionSession, VisionStats } from '../contract.ts'\nimport { chartMetricStore, lowBalanceStore, monthlyBudgetStore, quotaViewActiveStore, usageWindowStore, widgetTabIdsStore, widgetVisibleStore } from './store.ts'\nimport type { ConversationViewTab, ConversationViewsSource } from './views.ts'\n\n/** `<package name> v<version>`, injected by build.mjs from package.json so the\n * footer on the \u989D\u5EA6 page reports exactly the version that was built. */\ndeclare const __PLUGIN_LABEL__: string\n\n/** Fallback composer height for the narrow-screen bottom safe area, used only\n * until the real measurement below lands (or if the host markup ever stops\n * exposing the `conversation.composer.dock` slot). DSH has no `--dsw-*`\n * variable for composer height \u2014 this number is not a guess: it is the\n * measured `[data-slot=\"conversation.composer.dock\"]` seat height on a live\n * DSH instance at 620 / 480 / 390px viewports (126px, stable across all\n * three; see LEARNINGS.md). Kept in sync with the small margin added in\n * styles.ts's `.dq-balance` narrow media query. */\nconst COMPOSER_FALLBACK_PX = 126\n\n/** One shimmering placeholder block, sized by the caller. */\nfunction Skel(props: { w: number; h: number }): ReactElement {\n return <div className=\"dq-skel\" style={{ width: `${props.w}px`, height: `${props.h}px` }} />\n}\n\n/** Placeholder for the balance card while the first fetch is in flight. */\nfunction BalanceSkeleton(): ReactElement {\n return (\n <div className=\"dq-balance-grid\">\n <div className=\"dq-stat\"><Skel w={56} h={11} /><Skel w={150} h={22} /></div>\n <div className=\"dq-stat\"><Skel w={28} h={11} /><Skel w={48} h={22} /></div>\n </div>\n )\n}\n\n/** Placeholder for the current-session card: same three-`Stat` grammar as\n * the balance card's skeleton above, just one more column. */\nfunction SessionUsageSkeleton(): ReactElement {\n return (\n <div className=\"dq-balance-grid\">\n <div className=\"dq-stat\"><Skel w={64} h={11} /><Skel w={96} h={18} /></div>\n <div className=\"dq-stat\"><Skel w={70} h={11} /><Skel w={80} h={18} /></div>\n <div className=\"dq-stat\"><Skel w={64} h={11} /><Skel w={40} h={18} /></div>\n </div>\n )\n}\n\n/** Placeholder for the usage trend card: replaying every session log takes\n * seconds, and an empty-state string there would read as \"you have no usage\". */\nfunction UsageSkeleton(): ReactElement {\n return (\n <>\n <div className=\"dq-usage-totals\">\n {[44, 44, 62, 62].map((w, i) => (\n <div key={i} className=\"dq-stat\"><Skel w={w} h={11} /><Skel w={w > 70 ? 84 : 56} h={20} /></div>\n ))}\n </div>\n <Skel w={140} h={12} />\n <div className=\"dq-skel-bars\">\n {Array.from({ length: 30 }, (_, i) => (\n <div key={i} className=\"dq-skel dq-skel-bar\" style={{ height: `${18 + ((i * 37) % 82)}px` }} />\n ))}\n </div>\n </>\n )\n}\n\n/** Placeholder for the heatmap card, same loading grammar as the trend card's\n * bar skeleton above \u2014 the heatmap has no separate fetch, it just renders\n * once `usage` lands, so the two cards share one loading vocabulary. */\nfunction HeatmapSkeleton(): ReactElement {\n return (\n <>\n <Skel w={140} h={12} />\n <div className=\"dq-skel-bars\" style={{ height: '88px' }}>\n {Array.from({ length: 52 }, (_, i) => (\n <div key={i} className=\"dq-skel dq-skel-bar\" style={{ height: `${14 + ((i * 29) % 60)}px` }} />\n ))}\n </div>\n </>\n )\n}\n\n/** Percentage chip comparing a window against the one before it. */\nfunction Delta(props: { current: number; previous: number; label: string }): ReactElement | null {\n const { t } = useI18n()\n const { current, previous, label } = props\n if (previous <= 0) {\n if (current <= 0) return null\n return <span className=\"dq-delta dq-delta--new\" title={t('delta.noBaselineTitle', { label })}>{t('delta.noBaseline')}</span>\n }\n const percent = Math.round(((current - previous) / previous) * 100)\n const title = `${label} \u00A5${fmt(previous)}`\n if (percent === 0) return <span className=\"dq-delta dq-delta--flat\" title={title}>{t('delta.flat')}</span>\n const up = percent > 0\n return (\n <span className={`dq-delta ${up ? 'dq-delta--up' : 'dq-delta--down'}`} title={title}>\n {up ? '\u2191' : '\u2193'}{Math.abs(percent)}% <span className=\"dq-delta-label\">{label}</span>\n </span>\n )\n}\n\n/** One window of the \u6D88\u8017\u6982\u89C8 card: cost headline, volume underneath. */\nfunction Period(props: {\n label: string\n period: PeriodUsage\n previous?: PeriodUsage\n compare?: string\n}): ReactElement {\n const { t, locale } = useI18n()\n const { period, previous, compare } = props\n return (\n <div className=\"dq-period\">\n <div className=\"dq-period-head\">\n <span className=\"dq-period-label\">{props.label}</span>\n {previous !== undefined && compare !== undefined && (\n <Delta current={period.cost} previous={previous.cost} label={compare} />\n )}\n </div>\n <div className=\"dq-period-cost\">\u00A5 {fmt(period.cost)}</div>\n <div className=\"dq-period-sub\">{t('period.volume', {\n tokens: fmtCompact(period.total, locale),\n calls: t('common.calls', { count: fmtInt(period.calls) }),\n })}</div>\n </div>\n )\n}\n\n/** Monthly guardrail, kept inside the overview rather than adding another card. */\nfunction BudgetMeter(props: { spent: number; budget: number; onConfigure: () => void }): ReactElement {\n const { t } = useI18n()\n if (props.budget <= 0) {\n return (\n <div className=\"dq-budget dq-budget--unset\">\n <span>{t('budget.unset')}</span>\n <button type=\"button\" className=\"dq-budget-action\" onClick={props.onConfigure}>{t('budget.configure')}</button>\n </div>\n )\n }\n\n const snapshot = budgetSnapshot(props.spent, props.budget)\n const usedPercent = snapshot.ratio * 100\n const barPercent = Math.min(100, usedPercent)\n const forecastText = snapshot.status === 'over'\n ? t('budget.over', { amount: fmt(Math.abs(snapshot.remaining)) })\n : snapshot.forecastOver > 0\n ? t('budget.forecastOver', { amount: fmt(snapshot.forecastOver) })\n : t('budget.forecast', { amount: fmt(snapshot.forecast) })\n\n return (\n <div className={`dq-budget dq-budget--${snapshot.status}`}>\n <div className=\"dq-budget-head\">\n <span className=\"dq-budget-title\">{t('budget.title')}</span>\n <span className=\"dq-budget-amount\">\u00A5 {fmt(snapshot.spent)} / \u00A5 {fmt(snapshot.budget)}</span>\n </div>\n <div\n className=\"dq-budget-track\"\n role=\"progressbar\"\n aria-label={t('budget.progressLabel')}\n aria-valuemin={0}\n aria-valuemax={snapshot.budget}\n aria-valuenow={Math.min(snapshot.spent, snapshot.budget)}\n aria-valuetext={t('budget.used', { percent: usedPercent.toFixed(1), forecast: forecastText })}\n >\n <div className=\"dq-budget-fill\" style={{ transform: `scaleX(${barPercent / 100})` }} />\n </div>\n <div className=\"dq-budget-meta\">\n <span>{snapshot.remaining >= 0\n ? t('budget.remaining', { amount: fmt(snapshot.remaining) })\n : t('budget.exceeded', { amount: fmt(Math.abs(snapshot.remaining)) })}</span>\n <span>{forecastText}</span>\n </div>\n </div>\n )\n}\n\nconst rate = (value: number): string => `\u00A5${value.toLocaleString(undefined, { maximumFractionDigits: 3 })}`\n\n/** Auditable breakdown of the rates the cost estimate was computed with \u2014\n * a `<details>` so it is keyboard-reachable without any extra state. */\nfunction PricingNote(props: { pricing: PricingInfo }): ReactElement {\n const { t } = useI18n()\n const { pricing } = props\n return (\n <details className=\"dq-pricing\">\n <summary className=\"dq-pricing-summary\">\n {t('pricing.title')}\n <span className={`dq-pricing-now${pricing.inPeakNow ? ' dq-pricing-now--peak' : ''}`}>\n {pricing.inPeakNow ? t('pricing.currentPeak') : t('pricing.currentOffPeak')}\n </span>\n </summary>\n <div className=\"dq-pricing-body\">\n <table className=\"dq-pricing-table\">\n <thead>\n <tr>\n <th>{t('pricing.model')}</th>\n <th>{t('pricing.period')}</th>\n <th>{t('pricing.inputCacheHit')}</th>\n <th>{t('pricing.inputCacheMiss')}</th>\n <th>{t('pricing.output')}</th>\n </tr>\n </thead>\n <tbody>\n {pricing.tiers.map(tier => (\n <Fragment key={tier.model}>\n <tr>\n <td rowSpan={2}>{tier.model}</td>\n <td>{t('pricing.peak')}</td>\n <td>{rate(tier.peak.cacheHit)}</td>\n <td>{rate(tier.peak.input)}</td>\n <td>{rate(tier.peak.output)}</td>\n </tr>\n <tr>\n <td>{t('pricing.offPeak')}</td>\n <td>{rate(tier.offPeak.cacheHit)}</td>\n <td>{rate(tier.offPeak.input)}</td>\n <td>{rate(tier.offPeak.output)}</td>\n </tr>\n </Fragment>\n ))}\n </tbody>\n </table>\n <p className=\"dq-pricing-foot\">\n {t('pricing.unit', { currency: pricing.currency })}\n {t('pricing.splitNote', { windows: pricing.peakWindows.join(t('common.listSeparator')) })}\n {t('pricing.visionNote')}\n {t('pricing.modelNote')}\n {t('pricing.unknown')}\n </p>\n </div>\n </details>\n )\n}\n\n/** Make a locally-computed bill honest about which logs it could inspect. */\nfunction CoverageDiagnostics(props: { coverage: UsageCoverage }): ReactElement {\n const { t, locale } = useI18n()\n const { coverage } = props\n const hasGaps = coverage.failedSessions > 0\n || coverage.skippedRecords > 0\n || coverage.scannedSessions < coverage.listedSessions\n const coverageTime = new Intl.DateTimeFormat(locale === 'en' ? 'en-US' : 'zh-CN', {\n timeZone: 'Asia/Shanghai', year: 'numeric', month: 'numeric', day: 'numeric',\n hour: '2-digit', minute: '2-digit', hour12: false,\n })\n const range = coverage.earliestAt === null || coverage.latestAt === null\n ? t('coverage.noRange')\n : `${coverageTime.format(coverage.earliestAt)} \u2013 ${coverageTime.format(coverage.latestAt)}`\n\n return (\n <details className={`dq-coverage${hasGaps ? ' dq-coverage--warn' : ''}`}>\n <summary className=\"dq-coverage-summary\">\n <span className=\"dq-coverage-title\">{t('coverage.title')}</span>\n <span className=\"dq-coverage-status\">{hasGaps ? t('coverage.gaps') : t('coverage.complete')}</span>\n <span className=\"dq-coverage-brief\">{t('coverage.brief', {\n scanned: fmtInt(coverage.scannedSessions), listed: fmtInt(coverage.listedSessions), records: fmtInt(coverage.usageRecords),\n })}</span>\n </summary>\n <div className=\"dq-coverage-body\">\n <dl className=\"dq-coverage-metrics\">\n <div><dt>{t('coverage.scanned')}</dt><dd>{t('common.sessions', { count: fmtInt(coverage.scannedSessions) })}</dd></div>\n <div><dt>{t('coverage.failed')}</dt><dd>{t('common.sessions', { count: fmtInt(coverage.failedSessions) })}</dd></div>\n <div><dt>{t('coverage.skipped')}</dt><dd>{t('common.records', { count: fmtInt(coverage.skippedRecords) })}</dd></div>\n </dl>\n <p className=\"dq-coverage-range\">{t('coverage.time', { range })}</p>\n <p className=\"dq-coverage-note\">{t('coverage.scope')}</p>\n {hasGaps && (\n <p className=\"dq-coverage-warning\">{t('coverage.warning')}</p>\n )}\n </div>\n </details>\n )\n}\n\nfunction Stat(props: { label: string; children: ReactNode }): ReactElement {\n return (\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{props.label}</div>\n {props.children}\n </div>\n )\n}\n\nfunction Link(props: { href: string; children: ReactNode }): ReactElement {\n return (\n <a className=\"dq-link\" href={props.href} target=\"_blank\" rel=\"noreferrer\">{props.children}</a>\n )\n}\n\n/** Download menu kept compact in the usage card header. */\nfunction ExportMenu(props: { usage: UsageData }): ReactElement {\n const { t } = useI18n()\n const [open, setOpen] = useState(false)\n const [done, setDone] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const triggerRef = useRef<HTMLButtonElement | null>(null)\n const menuRef = useRef<HTMLDivElement | null>(null)\n const doneTimerRef = useRef<number | null>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current !== null && !rootRef.current.contains(event.target as Node)) setOpen(false)\n }\n const onKey = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n setOpen(false)\n triggerRef.current?.focus()\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n window.requestAnimationFrame(() => menuRef.current?.querySelector<HTMLButtonElement>('button')?.focus())\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n useEffect(() => () => {\n if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current)\n }, [])\n\n const finish = (): void => {\n setOpen(false)\n setDone(true)\n if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current)\n doneTimerRef.current = window.setTimeout(() => setDone(false), 1400)\n }\n\n const exportDaily = (): void => {\n const stamp = exportDateStamp()\n downloadText(`dsh-usage-${stamp}.csv`, dailyUsageCsv(props.usage), 'text/csv;charset=utf-8')\n finish()\n }\n const exportModels = (): void => {\n const stamp = exportDateStamp()\n downloadText(`dsh-usage-models-${stamp}.csv`, modelUsageCsv(props.usage), 'text/csv;charset=utf-8')\n finish()\n }\n const exportJson = (): void => {\n const stamp = exportDateStamp()\n downloadText(`dsh-usage-${stamp}.json`, fullUsageJson(props.usage), 'application/json;charset=utf-8')\n finish()\n }\n\n return (\n <div className=\"dq-export\" ref={rootRef}>\n <button\n ref={triggerRef}\n type=\"button\"\n className={`dq-export-btn${done ? ' dq-export-btn--done' : ''}`}\n aria-haspopup=\"true\"\n aria-expanded={open}\n title={t('export.title')}\n onClick={() => setOpen(value => !value)}\n >\n {done ? t('export.done') : t('export.action')}\n </button>\n {open && (\n <div ref={menuRef} className=\"dq-export-menu\" aria-label={t('export.menuLabel')}>\n <button type=\"button\" onClick={exportDaily}>\n <span>{t('export.dailyCsv')}</span><small>{t('common.rows', { count: props.usage.daily.length })}</small>\n </button>\n <button type=\"button\" onClick={exportModels}>\n <span>{t('export.modelCsv')}</span><small>{t('common.rows', { count: props.usage.models.length })}</small>\n </button>\n <button type=\"button\" onClick={exportJson}>\n <span>{t('export.fullJson')}</span><small>{t('export.jsonHint')}</small>\n </button>\n </div>\n )}\n </div>\n )\n}\n\nexport const modelKeyOf = (m: ModelUsage): string => (m.provider !== '' ? `${m.provider}/${m.model}` : m.model)\n\nexport const modelNameOf = (m: ModelUsage, t: Translate = fallbackT): string => (m.model !== '' ? m.model : t('common.unknownModel'))\n\n/**\n * Usage for exactly the session the \u300C\u989D\u5EA6\u300D tab is opened inside \u2014 the one\n * dimension every other card on this page does not answer (they are all\n * account-wide, replayed across every local session log). Deliberately\n * reuses the same `.dq-balance-grid`/`Stat` headline layout as the balance\n * card above it and the same `.dq-rank`/`MODEL_COLORS` breakdown as the\n * model cost ranking card further down, rather than inventing a third\n * visual language for numbers that are structurally the same kind of thing.\n */\nfunction SessionUsageCard(props: { data: SessionUsageData }): ReactElement {\n const { t, locale } = useI18n()\n const { data } = props\n if (data.calls === 0) return <div className=\"dq-empty\">{t('sessionUsage.empty')}</div>\n\n const rangeTime = new Intl.DateTimeFormat(locale === 'en' ? 'en-US' : 'zh-CN', {\n timeZone: 'Asia/Shanghai', year: 'numeric', month: 'numeric', day: 'numeric',\n hour: '2-digit', minute: '2-digit', hour12: false,\n })\n const range = data.firstActive !== null && data.lastActive !== null\n ? (data.firstActive === data.lastActive\n ? rangeTime.format(data.firstActive)\n : `${rangeTime.format(data.firstActive)} \u2013 ${rangeTime.format(data.lastActive)}`)\n : null\n\n return (\n <>\n <div className=\"dq-balance-grid\">\n <Stat label={t('sessionUsage.cost')}><div className=\"dq-stat-value\">\u00A5 {fmt(data.cost)}</div></Stat>\n <Stat label={t('sessionUsage.tokens')}><div className=\"dq-stat-value\">{fmtCompact(data.total, locale)}</div></Stat>\n <Stat label={t('sessionUsage.calls')}><div className=\"dq-stat-value\">{fmtInt(data.calls)}</div></Stat>\n </div>\n {range !== null && <p className=\"dq-session-sub\">{t('sessionUsage.range', { range })}</p>}\n {data.models.length > 0 && (\n <div className=\"dq-rank\">\n {data.models.map((m, index) => {\n const key = m.provider !== '' ? `${m.provider}/${m.model}` : (m.model !== '' ? m.model : `model-${index}`)\n const name = m.model !== '' ? m.model : t('common.unknownModel')\n const color = MODEL_COLORS[index % MODEL_COLORS.length]\n const share = data.cost > 0 ? m.cost / data.cost : 0\n return (\n <div key={key} className=\"dq-rank-row\">\n <div className=\"dq-rank-head\">\n <span className=\"dq-rank-name\" title={key}>\n <span className=\"dq-legend-swatch\" style={{ background: color }} />\n {name}\n </span>\n <span className=\"dq-rank-cost\">\n \u00A5 {fmt(m.cost)}\n <span className=\"dq-rank-share\">{(share * 100).toFixed(1)}%</span>\n </span>\n </div>\n <div\n className=\"dq-rank-track\"\n role=\"img\"\n aria-label={t('models.shareAria', { name, percent: (share * 100).toFixed(1) })}\n >\n <div className=\"dq-rank-fill\" style={{ width: `${Math.max(share * 100, 1.5)}%`, background: color }} />\n </div>\n <div className=\"dq-rank-sub\">\n {t('models.detail', {\n tokens: fmtCompact(m.total, locale),\n calls: t('common.calls', { count: fmtInt(m.calls) }),\n input: fmtCompact(m.input, locale), output: fmtCompact(m.output, locale), cache: fmtCompact(m.cache, locale),\n })}\n </div>\n </div>\n )\n })}\n </div>\n )}\n </>\n )\n}\n\n/**\n * What prefix caching is doing for the bill. Cache-hit tokens are priced at a\n * small fraction of cache-miss tokens, so on a DSH workload this is usually the\n * single biggest lever on cost \u2014 and it was invisible before.\n */\nfunction CacheCard(props: { totals: UsageData['totals'] }): ReactElement {\n const { t, locale } = useI18n()\n const { totals } = props\n const prompt = totals.input + totals.cache\n if (prompt === 0) return <div className=\"dq-empty\">{t('cache.empty')}</div>\n const rate = totals.cache / prompt\n const wouldHaveCost = totals.cost + totals.cacheSavings\n const low = rate < 0.6\n return (\n <div className=\"dq-cache\">\n <div className=\"dq-cache-figures\">\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('cache.rate')}</div>\n <div className={`dq-stat-value${low ? ' dq-stat-value--warn' : ' dq-stat-value--ok'}`}>\n {(rate * 100).toFixed(1)}%\n </div>\n </div>\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('cache.saved')}</div>\n <div className=\"dq-stat-value\">\u00A5 {fmt(totals.cacheSavings)}</div>\n </div>\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('cache.allMiss')}</div>\n <div className=\"dq-stat-value dq-muted-value\">\u00A5 {fmt(wouldHaveCost)}</div>\n </div>\n </div>\n <div className=\"dq-cache-track\" role=\"img\" aria-label={t('cache.aria', { hit: (rate * 100).toFixed(1), miss: ((1 - rate) * 100).toFixed(1) })}>\n <div className=\"dq-cache-fill\" style={{ width: `${rate * 100}%` }} />\n </div>\n <div className=\"dq-cache-legend\">\n <span><span className=\"dq-legend-swatch dq-legend-swatch--hit\" />{t('cache.hitLegend', { tokens: fmtCompact(totals.cache, locale) })}</span>\n <span><span className=\"dq-legend-swatch dq-legend-swatch--miss\" />{t('cache.missLegend', { tokens: fmtCompact(totals.input, locale) })}</span>\n </div>\n <p className=\"dq-cache-foot\">\n {low\n ? t('cache.lowHint')\n : t('cache.goodHint')}\n </p>\n </div>\n )\n}\n\n/**\n * Image / multimodal usage: how much of the bill came from pictures. Image\n * blocks are collected from user messages and tool results during the same\n * log replay that feeds the rest of the dashboard (no attachment bytes are\n * read \u2014 the refs already carry width/height), so this dimension costs the\n * replay nothing extra. Tokens are estimated with the official resizing rule\n * and priced at the consuming call's rates; shares are against the window's\n * reported input tokens and cost.\n */\nfunction VisionCard(props: {\n vision: VisionStats\n daily: VisionDayPoint[]\n sessions: VisionSession[]\n windowLabel: string\n inputTokens: number\n costTotal: number\n}): ReactElement {\n const { t, locale } = useI18n()\n const { vision, daily, sessions, windowLabel, inputTokens, costTotal } = props\n if (vision.images === 0) return <div className=\"dq-empty\">{t('vision.empty', { window: windowLabel })}</div>\n const tokensShare = inputTokens > 0 ? (vision.imageTokens / inputTokens) * 100 : 0\n const costShare = costTotal > 0 ? (vision.cost / costTotal) * 100 : 0\n const labelEvery = daily.length <= 7 ? 1 : daily.length <= 30 ? 5 : daily.length <= 90 ? 15 : 30\n const bars = daily.map(d => ({\n label: d.date.slice(8, 10),\n value: d.images,\n title: t('vision.barTitle', { date: d.date, images: fmtInt(d.images), tokens: fmtCompact(d.imageTokens, locale), cost: fmt(d.cost) }),\n }))\n return (\n <>\n <div className=\"dq-usage-totals\">\n <Stat label={t('vision.images')}><div className=\"dq-stat-value\">{fmtInt(vision.images)}</div></Stat>\n <Stat label={t('vision.tokens')}>\n <div className=\"dq-stat-value\">\n {fmtCompact(vision.imageTokens, locale)}\n {tokensShare > 0 && <span className=\"dq-vision-share\">{t('vision.tokensShare', { percent: tokensShare.toFixed(1) })}</span>}\n </div>\n </Stat>\n <Stat label={t('vision.cost')}>\n <div className=\"dq-stat-value\">\n \u00A5 {fmt(vision.cost)}\n {costShare > 0 && <span className=\"dq-vision-share\">{t('vision.costShare', { percent: costShare.toFixed(1) })}</span>}\n </div>\n </Stat>\n </div>\n <div className=\"dq-chart-title\">{t('vision.dailyTitle', { window: windowLabel })}</div>\n <Bars data={bars} height={80} labelEvery={labelEvery} />\n {sessions.length > 0 && (\n <div className=\"dq-vision-sessions\">\n <div className=\"dq-chart-title\">{t('vision.sessionsTitle')}</div>\n {sessions.map(session => (\n <div key={session.id} className=\"dq-session\">\n <div className=\"dq-session-head\">\n <span className=\"dq-session-title\" title={session.id}>{session.title}</span>\n <span className=\"dq-session-cost\">\n {t('vision.sessionRow', { images: fmtInt(session.images), cost: fmt(session.cost) })}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n <p className=\"dq-cache-foot\">{t('vision.note')}</p>\n </>\n )\n}\n\n/**\n * Peak vs off-peak: which side of DeepSeek's price windows the usage falls on,\n * and what shifting the peak share off-peak would have saved.\n */\nfunction PeakCard(props: { split: PeakSplit; pricing: PricingInfo }): ReactElement {\n const { t, locale } = useI18n()\n const { split, pricing } = props\n const total = split.peak.total + split.offPeak.total\n if (total === 0) return <div className=\"dq-empty\">{t('peak.empty')}</div>\n const peakShare = split.peak.total / total\n const shiftSaving = split.peakEraCost - split.offPeakEraCost\n return (\n <div className=\"dq-peak\">\n <div\n className=\"dq-peak-track\"\n role=\"img\"\n aria-label={t('peak.aria', { peak: (peakShare * 100).toFixed(1), offPeak: ((1 - peakShare) * 100).toFixed(1) })}\n >\n <div className=\"dq-peak-fill\" style={{ width: `${peakShare * 100}%` }} />\n </div>\n <div className=\"dq-peak-legend\">\n <span><span className=\"dq-legend-swatch dq-legend-swatch--peak\" />{t('peak.peakLegend', {\n share: (peakShare * 100).toFixed(1), tokens: fmtCompact(split.peak.total, locale), calls: t('common.callsShort', { count: fmtInt(split.peak.calls) }),\n })}</span>\n <span><span className=\"dq-legend-swatch dq-legend-swatch--offpeak\" />{t('peak.offPeakLegend', {\n share: ((1 - peakShare) * 100).toFixed(1), tokens: fmtCompact(split.offPeak.total, locale), calls: t('common.callsShort', { count: fmtInt(split.offPeak.calls) }),\n })}</span>\n </div>\n <div className=\"dq-peak-figures\">\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('peak.shift')}</div>\n <div className=\"dq-stat-value\">\n \u00A5 {fmt(split.offPeakEraCost)}\n <span className=\"dq-peak-delta dq-peak-delta--save\">{t('peak.saving', { amount: fmt(shiftSaving) })}</span>\n </div>\n </div>\n </div>\n <p className=\"dq-peak-foot\">\n {t('peak.schedule', { windows: pricing.peakWindows.join(t('common.listSeparator')) })}\n {t('peak.activeHint')}\n </p>\n </div>\n )\n}\n\n/** Coarse \"how long ago\", enough to tell a live session from last week's. */\nfunction agoText(ms: number, t: Translate): string {\n const minutes = Math.floor((Date.now() - ms) / 60_000)\n if (minutes < 1) return t('common.justNow')\n if (minutes < 60) return t('common.minutesAgo', { count: minutes })\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return t('common.hoursAgo', { count: hours })\n return t('common.daysAgo', { count: Math.floor(hours / 24) })\n}\n\n/** Per-row fetch state for the ranking's on-demand expand \u2014 mirrors the\n * three-way {loading, error, data} shape `loadSessionUsage` already uses\n * for the current-session card, just keyed per row instead of per tab. */\ninterface SessionDetailState {\n error: string | null\n data: SessionUsageData | null\n}\n\n/** Body of one expanded ranking row: same loading/error/data three-way as the\n * current-session card, and the same `SessionUsageCard` rendering the data \u2014\n * this is deliberately not a second implementation of that layout. */\nfunction SessionDetail(props: { state: SessionDetailState | undefined }): ReactElement {\n const { t } = useI18n()\n const { state } = props\n if (state === undefined) return <SessionUsageSkeleton />\n if (state.data !== null) return <SessionUsageCard data={state.data} />\n if (state.error !== null) return <div className=\"dq-empty dq-empty--error\">{localizeApiError(state.error, t, 'error.query')}</div>\n return <SessionUsageSkeleton />\n}\n\n/**\n * Which sessions burned the money. Per-model and per-day views say what and\n * when; this says *which run*, which is the one an agent user can act on \u2014\n * a single expensive session is a prompt or a loop worth looking at.\n *\n * Each row is a native `<details>` (same disclosure grammar as `.dq-pricing`/\n * `.dq-coverage` above \u2014 no custom open/close state machine, keyboard and\n * touch come for free). Opening a row for the first time fires an on-demand\n * `fetchSessionUsage(s.id)` and renders the result with the exact same\n * `SessionUsageCard` the \u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300Dcard uses \u2014 the row only has the\n * summary fields (`SessionCost`), the full per-model breakdown only exists\n * once that fetch lands. `startedRef` guards against re-fetching on a\n * collapse/re-expand: once a row's fetch has been kicked off, later toggles\n * just show whatever is already cached in `details`.\n */\nfunction SessionRanking(props: { sessions: SessionCost[]; count: number; totalCost: number }): ReactElement {\n const { t, locale } = useI18n()\n const { sessions, count, totalCost } = props\n const [details, setDetails] = useState<Record<string, SessionDetailState>>({})\n const startedRef = useRef<Set<string>>(new Set())\n\n const loadDetail = (id: string): void => {\n if (startedRef.current.has(id)) return\n startedRef.current.add(id)\n fetchSessionUsage(id).then(res => {\n setDetails(prev => ({\n ...prev,\n [id]: res.ok && res.data !== undefined\n ? { error: null, data: res.data }\n : { error: res.error ?? null, data: null },\n }))\n }, err => {\n const message = (err as { message?: string } | null)?.message ?? String(err)\n setDetails(prev => ({ ...prev, [id]: { error: message, data: null } }))\n })\n }\n\n if (sessions.length === 0) return <div className=\"dq-empty\">{t('sessions.empty')}</div>\n const top = sessions[0].cost\n return (\n <>\n <ol className=\"dq-sessions\">\n {sessions.map(s => (\n <li key={s.id} className=\"dq-session\">\n <details\n className=\"dq-session-details\"\n onToggle={e => { if (e.currentTarget.open) loadDetail(s.id) }}\n >\n <summary className=\"dq-session-summary\">\n <span className=\"dq-session-chevron\" aria-hidden=\"true\">\u25B8</span>\n <div className=\"dq-session-summary-body\">\n <div className=\"dq-session-head\">\n <span className=\"dq-session-title\" title={`${s.title}\\n${s.id}`}>{s.title || t('sessions.fallbackTitle', { id: s.id })}</span>\n <span className=\"dq-session-cost\">\u00A5 {fmt(s.cost)}</span>\n </div>\n <div className=\"dq-session-track\">\n <div className=\"dq-session-fill\" style={{ width: `${Math.max((s.cost / (top || 1)) * 100, 1.5)}%` }} />\n </div>\n <div className=\"dq-session-sub\">\n {t('sessions.detail', {\n tokens: fmtCompact(s.total, locale),\n calls: t('common.calls', { count: fmtInt(s.calls) }),\n ago: agoText(s.lastActive, t),\n })}\n {totalCost > 0 && <> \u00B7 {t('sessions.share', { percent: ((s.cost / totalCost) * 100).toFixed(1) })}</>}\n </div>\n </div>\n </summary>\n <div className=\"dq-session-expand\">\n <SessionDetail state={details[s.id]} />\n </div>\n </details>\n </li>\n ))}\n </ol>\n {count > sessions.length && (\n <p className=\"dq-session-foot\">{t('sessions.more', { count: fmtInt(count), shown: sessions.length })}</p>\n )}\n </>\n )\n}\n\n/** Which model the money actually goes to, most expensive first. */\nfunction ModelRanking(props: {\n models: ModelUsage[]\n totalCost: number\n colorOf: (key: string) => string\n}): ReactElement {\n const { t, locale } = useI18n()\n const { models, totalCost } = props\n if (models.length === 0) return <div className=\"dq-empty\">{t('models.empty')}</div>\n return (\n <div className=\"dq-rank\">\n {models.map(m => {\n const key = modelKeyOf(m)\n const share = totalCost > 0 ? m.cost / totalCost : 0\n return (\n <div key={key} className=\"dq-rank-row\">\n <div className=\"dq-rank-head\">\n <span className=\"dq-rank-name\" title={key}>\n <span className=\"dq-legend-swatch\" style={{ background: props.colorOf(key) }} />\n {modelNameOf(m, t)}\n </span>\n <span className=\"dq-rank-cost\">\n \u00A5 {fmt(m.cost)}\n <span className=\"dq-rank-share\">{(share * 100).toFixed(1)}%</span>\n </span>\n </div>\n <div\n className=\"dq-rank-track\"\n role=\"img\"\n aria-label={t('models.shareAria', { name: modelNameOf(m, t), percent: (share * 100).toFixed(1) })}\n >\n <div className=\"dq-rank-fill\" style={{ width: `${Math.max(share * 100, 1.5)}%`, background: props.colorOf(key) }} />\n </div>\n <div className=\"dq-rank-sub\">\n {t('models.detail', {\n tokens: fmtCompact(m.total, locale),\n calls: t('common.calls', { count: fmtInt(m.calls) }),\n input: fmtCompact(m.input, locale), output: fmtCompact(m.output, locale), cache: fmtCompact(m.cache, locale),\n })}\n </div>\n </div>\n )\n })}\n </div>\n )\n}\n\n/** Multi-select model dropdown feeding the grouped bar chart. */\nfunction ModelPicker(props: {\n models: ModelUsage[]\n selected: string[]\n colorOf: (key: string) => string\n onChange: (keys: string[]) => void\n}): ReactElement | null {\n const { t, locale } = useI18n()\n const { models } = props\n const [open, setOpen] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const triggerRef = useRef<HTMLButtonElement | null>(null)\n const menuRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent): void => {\n if (rootRef.current !== null && !rootRef.current.contains(e.target as Node)) setOpen(false)\n }\n const onKey = (e: KeyboardEvent): void => {\n if (e.key !== 'Escape') return\n e.preventDefault()\n setOpen(false)\n triggerRef.current?.focus()\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n window.requestAnimationFrame(() => menuRef.current?.querySelector<HTMLInputElement>('input')?.focus())\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n if (models.length === 0) return null\n\n const toggle = (key: string): void => {\n props.onChange(\n props.selected.includes(key)\n ? props.selected.filter(k => k !== key)\n : [...props.selected, key],\n )\n }\n\n const label = props.selected.length === 0 ? t('models.all') : t('models.selected', { count: props.selected.length })\n\n return (\n <div className=\"dq-model-picker\" ref={rootRef}>\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"dq-model-btn\"\n aria-haspopup=\"true\"\n aria-expanded={open}\n aria-controls=\"dq-model-menu\"\n title={t('models.filterTitle')}\n onClick={() => setOpen(value => !value)}\n >\n {label}\n </button>\n {open && (\n <div id=\"dq-model-menu\" ref={menuRef} className=\"dq-model-menu\" role=\"group\" aria-label={t('models.filterLabel')}>\n <label className=\"dq-model-item dq-model-item--all\">\n <input\n type=\"checkbox\"\n checked={props.selected.length === 0}\n onChange={() => props.onChange([])}\n />\n <span>{t('models.all')}</span>\n </label>\n {models.map(m => {\n const key = modelKeyOf(m)\n return (\n <label key={key} className=\"dq-model-item\">\n <input type=\"checkbox\" checked={props.selected.includes(key)} onChange={() => toggle(key)} />\n <span className=\"dq-legend-swatch\" style={{ background: props.colorOf(key) }} />\n <span>{modelNameOf(m, t)}</span>\n <span className=\"dq-model-tag\">{fmtCompact(m.total, locale)}</span>\n </label>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\nconst windowName = (days: UsageWindowDays, t: Translate = fallbackT): string => days === 365\n ? t('window.year')\n : t('window.days', { days })\n\n/** One range choice controls every metric whose title carries that range. */\nfunction UsageWindowPicker(props: { value: UsageWindowDays; onChange: (days: UsageWindowDays) => void }): ReactElement {\n const { t } = useI18n()\n return (\n <div className=\"dq-window-switch\" role=\"group\" aria-label={t('window.groupLabel')}>\n {USAGE_WINDOW_DAYS.map(days => (\n <button\n key={days}\n type=\"button\"\n className={`dq-window-btn${props.value === days ? ' dq-window-btn--on' : ''}`}\n aria-pressed={props.value === days}\n title={t('window.viewTitle', { window: windowName(days, t) })}\n onClick={() => props.onChange(days)}\n >\n {days === 365 ? t('window.yearButton') : t('window.daysButton', { days })}\n </button>\n ))}\n </div>\n )\n}\n\nconst chartMetricLabel = (metric: ChartMetric, t: Translate = fallbackT): string => metric === 'tokens'\n ? t('metric.tokens')\n : chartMetricName(metric, t)\n\nfunction ChartMetricPicker(props: { value: ChartMetric; onChange: (metric: ChartMetric) => void }): ReactElement {\n const { t } = useI18n()\n return (\n <div className=\"dq-chart-switch\" role=\"group\" aria-label={t('metric.groupLabel')}>\n {CHART_METRICS.map(metric => (\n <button\n key={metric}\n type=\"button\"\n className={`dq-chart-switch-btn${props.value === metric ? ' dq-chart-switch-btn--on' : ''}`}\n aria-pressed={props.value === metric}\n title={t('metric.compareTitle', { metric: chartMetricLabel(metric, t) })}\n onClick={() => props.onChange(metric)}\n >\n {chartMetricLabel(metric, t)}\n </button>\n ))}\n </div>\n )\n}\n\nexport function BalanceDashboard(props: { sessionId?: string; views: ConversationViewsSource }): ReactElement {\n const { t, locale } = useI18n()\n // Seed state from the cache so the tab never waits on the network when\n // data has been fetched before (stale-while-revalidate).\n const cachedBalance = getCachedBalance()\n const cachedUsage = getCachedUsage()\n const cachedUsageAt = getCachedUsageAt()\n const [balance, setBalance] = useState<BalanceData | null>(cachedBalance?.data ?? null)\n const [usage, setUsage] = useState<UsageData | null>(cachedUsage?.data ?? null)\n const [error, setError] = useState<string | null>(null)\n const [notice, setNotice] = useState<{ kind: 'all' | 'balance' | 'usage'; error: string } | null>(null)\n // Balance answers in well under a second, usage replays every session log and\n // takes seconds \u2014 so they get their own loading flags and paint independently\n // instead of the fast one waiting on the slow one.\n const [loadingBalance, setLoadingBalance] = useState(cachedBalance === null)\n const [loadingUsage, setLoadingUsage] = useState(cachedUsage === null)\n const [refreshing, setRefreshing] = useState(false)\n const [usageUpdatedAt, setUsageUpdatedAt] = useState<number | null>(cachedUsageAt)\n const [syncState, setSyncState] = useState<SyncState>(cachedUsageAt === null ? 'syncing' : 'cached')\n const [freshnessNow, setFreshnessNow] = useState(Date.now())\n const hadDataRef = useRef(cachedBalance !== null || cachedUsage !== null)\n const [widgetOn, setWidgetOn] = useState(widgetVisibleStore.get())\n const [widgetTabIds, setWidgetTabIds] = useState<string[] | null>(widgetTabIdsStore.get())\n const [viewTabs, setViewTabs] = useState<ConversationViewTab[]>(() => props.views.list())\n useEffect(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views])\n const [lowBalance, setLowBalance] = useState(lowBalanceStore.get())\n const [monthlyBudget, setMonthlyBudget] = useState(monthlyBudgetStore.get())\n const [barMode, setBarMode] = useState<'daily' | 'hourly'>('daily')\n const [selectedModels, setSelectedModels] = useState<string[]>([])\n const [windowDays, setWindowDays] = useState<UsageWindowDays>(usageWindowStore.get())\n const [chartMetric, setChartMetric] = useState<ChartMetric>(chartMetricStore.get())\n // \"\u9884\u8BA1\u53EF\u7528\" hover preview: an unopened <details> skips rendering its\n // non-summary children in Chromium (a content-visibility-like platform\n // behavior), so CSS-only `:hover{display:block}` never paints anything\n // when the element isn't actually open. Drive `open` from state instead \u2014\n // hovering opens it for a live preview; a manual click/keyboard/touch open\n // keeps it open independent of hover.\n const [runwayHovering, setRunwayHovering] = useState(false)\n const [runwayManuallyOpened, setRunwayManuallyOpened] = useState(false)\n // \u8F6E\u6B21 47: same platform limitation applies to \"\u4F59\u989D\u660E\u7EC6\" \u2014 it used the same\n // CSS-only `:hover{display:flex}` on an uncontrolled <details> since round 29,\n // which never actually painted in current Chromium either. Same fix, same shape.\n const [remainingHovering, setRemainingHovering] = useState(false)\n const [remainingManuallyOpened, setRemainingManuallyOpened] = useState(false)\n const budgetInputRef = useRef<HTMLInputElement | null>(null)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const [composerHeight, setComposerHeight] = useState<number | null>(null)\n // The composer's measured width caps the dashboard column: on narrower\n // viewports the fixed 860px max-width would otherwise overhang the input\n // box the cards visually belong to (user-facing parity requirement).\n const [composerWidth, setComposerWidth] = useState<number | null>(null)\n // The current-session card: fetched per session id, not through the\n // account-wide balance/usage caches (see DESIGN_NOTES \u2014 session-scoped\n // data does not go through the localStorage-persisted cache). No cached\n // seed on first render since there is nothing to seed from.\n const [sessionUsage, setSessionUsage] = useState<SessionUsageData | null>(null)\n const [loadingSessionUsage, setLoadingSessionUsage] = useState(props.sessionId !== undefined && props.sessionId !== '')\n const [sessionUsageError, setSessionUsageError] = useState<string | null>(null)\n const sessionUsageIdRef = useRef<string | null>(null)\n\n // Narrow viewports only: keep the last card scrollable clear of the host's\n // fixed composer, which otherwise overlaps the bottom of a long dashboard\n // (see LEARNINGS.md). Reuses the same `conversation.composer.dock` slot\n // widget.tsx already excludes from its own drag bounds, so both readings of\n // \"where is the composer\" stay in sync. Desktop layout never reads the\n // height \u2014 the CSS variable is only consumed inside the 620px media query;\n // the width is consumed by .dq-balance at every viewport.\n useLayoutEffect(() => {\n const node = rootRef.current\n if (node === null) return\n const measure = (): void => {\n const frame = getShellFrame(node)\n const composer = getComposerElement(frame)\n const rect = composer?.getBoundingClientRect() ?? null\n const nextHeight = rect !== null && rect.height > 0 ? Math.round(rect.height) : null\n setComposerHeight(prev => (prev === nextHeight ? prev : nextHeight))\n // Width parity uses the composer's input card (the rounded panel with\n // the textarea), not the dock seat \u2014 the seat spans the whole pane, so\n // it can never narrow the dashboard column below 860px.\n const input = getComposerInputElement(frame)\n const inputRect = input?.getBoundingClientRect() ?? null\n const nextWidth = inputRect !== null && inputRect.width > 0 ? Math.round(inputRect.width) : null\n setComposerWidth(prev => (prev === nextWidth ? prev : nextWidth))\n }\n measure()\n const frame = getShellFrame(node)\n const composer = getComposerElement(frame)\n const input = getComposerInputElement(frame)\n let observer: ResizeObserver | null = null\n if (typeof ResizeObserver === 'function') {\n observer = new ResizeObserver(measure)\n if (composer !== null) observer.observe(composer)\n if (input !== null) observer.observe(input)\n if (frame !== null) observer.observe(frame)\n }\n window.addEventListener('resize', measure)\n return () => {\n observer?.disconnect()\n window.removeEventListener('resize', measure)\n }\n }, [])\n\n // All conversation view tabs (Chat / Trajectory / \u989D\u5EA6 \u2026) share one host\n // scrollport (`[data-conversation-scroll]`), and the host keeps scroll\n // memory only for Chat itself \u2014 switching to \u989D\u5EA6 from a scrolled-down Chat\n // otherwise opens the tab near the settings card instead of at the account\n // balance. Reset before paint on every mount; the view is remounted per tab\n // switch, and `sessionId` re-runs it if the host reuses the view for\n // another session.\n useLayoutEffect(() => {\n const node = rootRef.current\n if (node === null) return\n const scrollport = node.closest('[data-conversation-scroll]')\n if (scrollport instanceof HTMLElement) scrollport.scrollTop = 0\n }, [props.sessionId])\n\n // The full dashboard and the floating summary should never compete for the\n // same pixels. This does not touch the persisted widget preference: leaving\n // the tab restores it exactly as the user left it.\n useLayoutEffect(() => {\n quotaViewActiveStore.set(true)\n return () => quotaViewActiveStore.set(false)\n }, [])\n\n const errText = (err: unknown): string => (err as { message?: string } | null)?.message ?? String(err)\n\n /** Load both resources; each resolves to null on success or to its error\n * string on failure, so a partial failure can name the half that broke. */\n const load = async (force: boolean): Promise<void> => {\n const usageAtBefore = getCachedUsageAt()\n setError(null)\n setNotice(null)\n setRefreshing(true)\n setSyncState('syncing')\n const balanceTask = fetchBalance(force).then(res => {\n if (res.ok && res.data !== undefined) {\n setBalance(res.data)\n hadDataRef.current = true\n return null\n }\n return res.error ?? '\u4F59\u989D\u52A0\u8F7D\u5931\u8D25'\n }, err => errText(err)).then(result => {\n setLoadingBalance(false)\n return result\n })\n const usageTask = fetchUsage(force).then(res => {\n if (res.ok && res.data !== undefined) {\n setUsage(res.data)\n const updatedAt = getCachedUsageAt()\n setUsageUpdatedAt(updatedAt)\n setFreshnessNow(Date.now())\n setSyncState(updatedAt !== null && updatedAt !== usageAtBefore ? 'fresh' : 'cached')\n hadDataRef.current = true\n return null\n }\n return res.error ?? '\u7528\u91CF\u52A0\u8F7D\u5931\u8D25'\n }, err => errText(err)).then(result => {\n setLoadingUsage(false)\n return result\n })\n const [balanceError, usageError] = await Promise.all([balanceTask, usageTask])\n setRefreshing(false)\n if (usageError !== null) {\n const fallbackAt = getCachedUsageAt()\n setUsageUpdatedAt(fallbackAt)\n setFreshnessNow(Date.now())\n setSyncState(fallbackAt === null ? 'error' : 'fallback')\n }\n if (balanceError !== null && usageError !== null) {\n // Nothing came back at all: an error only when there is nothing to show.\n if (hadDataRef.current) setNotice({ kind: 'all', error: balanceError })\n else setError(balanceError)\n } else if (balanceError !== null) {\n setNotice({ kind: 'balance', error: balanceError })\n } else if (usageError !== null) {\n setNotice({ kind: 'usage', error: usageError })\n }\n // The current-session card rides along with an explicit force refresh so\n // the one refresh button covers all three data sources; the initial\n // mount-time fetch is handled separately below, keyed on the session id\n // itself, so it is not duplicated here (force=false would otherwise fire\n // twice on first render \u2014 once from this effect, once from that one).\n if (force && props.sessionId !== undefined && props.sessionId !== '') {\n void loadSessionUsage(props.sessionId, true)\n }\n }\n\n /** Stale-while-revalidate for the current-session card: on success, swap\n * in the new data; on failure, keep whatever is already on screen (a\n * refresh going bad should not blank out a card that was working). Guards\n * every state write against the session id having changed mid-flight, so\n * a slow request for a session the user has since navigated away from\n * cannot clobber the next one's numbers. */\n const loadSessionUsage = async (id: string, force: boolean): Promise<void> => {\n if (!force) setLoadingSessionUsage(true)\n try {\n const res = await fetchSessionUsage(id, force)\n if (sessionUsageIdRef.current !== id) return\n if (res.ok && res.data !== undefined) {\n setSessionUsage(res.data)\n setSessionUsageError(null)\n } else {\n setSessionUsageError(res.error ?? null)\n }\n } catch (err) {\n if (sessionUsageIdRef.current === id) setSessionUsageError(errText(err))\n } finally {\n if (sessionUsageIdRef.current === id) setLoadingSessionUsage(false)\n }\n }\n\n useEffect(() => {\n void load(false)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Mount-time (and session-switch-time) fetch for the current-session card.\n // Independent of the balance/usage effect above: this data is scoped to\n // whichever session the tab happens to be open inside, not to the account.\n useEffect(() => {\n const id = props.sessionId\n sessionUsageIdRef.current = id ?? null\n if (id === undefined || id === '') {\n setSessionUsage(null)\n setSessionUsageError(null)\n setLoadingSessionUsage(false)\n return\n }\n setSessionUsage(null)\n setSessionUsageError(null)\n setLoadingSessionUsage(true)\n void loadSessionUsage(id, false)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.sessionId])\n\n useEffect(() => {\n const timer = window.setInterval(() => setFreshnessNow(Date.now()), 30_000)\n return () => window.clearInterval(timer)\n }, [])\n\n const toggle = (): void => {\n const next = !widgetOn\n setWidgetOn(next)\n widgetVisibleStore.set(next)\n }\n\n const widgetTabs = viewTabs.filter(tab => tab.id !== 'balance')\n const toggleWidgetTab = (id: string): void => {\n const selected = widgetTabIds ?? widgetTabs.map(tab => tab.id)\n const next = selected.includes(id) ? selected.filter(tabId => tabId !== id) : [...selected, id]\n setWidgetTabIds(next)\n widgetTabIdsStore.set(next)\n }\n\n const changeLowBalance = (value: number): void => {\n setLowBalance(value)\n lowBalanceStore.set(value)\n }\n\n const changeMonthlyBudget = (value: number): void => {\n setMonthlyBudget(value)\n monthlyBudgetStore.set(value)\n }\n\n const changeWindowDays = (value: UsageWindowDays): void => {\n setWindowDays(value)\n usageWindowStore.set(value)\n }\n\n const changeChartMetric = (value: ChartMetric): void => {\n setChartMetric(value)\n chartMetricStore.set(value)\n }\n\n const primary = balance !== null && balance.balances.length > 0 ? balance.balances[0] : null\n\n // Runway: balance divided by the last 7 calendar days' average spend. Idle\n // days are included on purpose \u2014 that is the burn rate, not the busy-day rate.\n const recentDays = (usage?.daily ?? []).slice(-7)\n const avgDailyCost = recentDays.length > 0\n ? recentDays.reduce((sum, d) => sum + d.cost, 0) / recentDays.length\n : 0\n const balanceValue = primary !== null ? Number(primary.total) : Number.NaN\n // 0 disables the warning; an unreadable balance never triggers it.\n const balanceLow = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance\n const daysLeft = avgDailyCost > 0 && Number.isFinite(balanceValue) ? balanceValue / avgDailyCost : null\n const daysLeftText = daysLeft === null\n ? '\u2014'\n : daysLeft >= 365 ? t('balance.overYear') : t('balance.days', { count: daysLeft < 10 ? daysLeft.toFixed(1) : Math.round(daysLeft) })\n const runwayTitle = daysLeft === null\n ? t('balance.noRunway', { days: recentDays.length || 7 })\n : t('balance.runwayTitle', { days: recentDays.length, cost: fmt(avgDailyCost) })\n\n const activeWindow = usage?.windows.find(window => window.days === windowDays) ?? null\n const activeWindowName = windowName(windowDays, t)\n\n const dailyBars = (activeWindow?.daily ?? []).map(d => ({\n label: windowDays === 365 ? d.date.slice(5).replace('-', '/') : d.date.slice(8, 10),\n value: chartMetricValue(chartMetric, d),\n title: t('chart.tooltip', {\n head: d.date, tokens: fmtInt(d.total), cost: fmt(d.cost), calls: t('common.calls', { count: d.calls }),\n }),\n }))\n const hourlyBars = (activeWindow?.hourly ?? []).map(h => ({\n label: String(h.hour),\n value: chartMetricValue(chartMetric, h),\n title: t('chart.tooltip', {\n head: t('chart.hourPoint', { hour: h.hour }), tokens: fmtInt(h.total), cost: fmt(h.cost), calls: t('common.calls', { count: h.calls }),\n }),\n }))\n\n // One canonical model order \u2014 most expensive first \u2014 shared by the ranking\n // card, the picker and the chart, so a model keeps its colour everywhere\n // instead of being coloured by the order it happened to be ticked in.\n // Tallest bar currently on screen \u2014 the chart has no y axis, so this is the\n // only thing giving the bars a magnitude.\n const chartPeak = (barMode === 'daily' ? dailyBars : hourlyBars)\n .reduce((peak, bar) => (bar.value > peak ? bar.value : peak), 0)\n const chartPeakText = chartMetric === 'cost'\n ? `\u00A5${fmt(chartPeak)}`\n : chartMetric === 'calls'\n ? t('common.callsShort', { count: fmtInt(chartPeak) })\n : `${fmtCompact(chartPeak, locale)} tokens`\n\n const modelList = [...(activeWindow?.models ?? [])].sort((a, b) => b.cost - a.cost)\n const canonicalModels = [...(usage?.models ?? [])].sort((a, b) => b.cost - a.cost)\n const availableModelKeys = new Set(modelList.map(modelKeyOf))\n const effectiveSelection = selectedModels.filter(k => availableModelKeys.has(k))\n const colorOf = (key: string): string => {\n const index = canonicalModels.findIndex(m => modelKeyOf(m) === key)\n return MODEL_COLORS[(index < 0 ? 0 : index) % MODEL_COLORS.length]\n }\n const modelCostTotal = modelList.reduce((sum, m) => sum + m.cost, 0)\n\n // Per-model series for the grouped bar chart (only when models are selected).\n const grouped = effectiveSelection.map(key => {\n const m = modelList.find(x => modelKeyOf(x) === key)\n const name = m === undefined ? key : modelNameOf(m, t)\n const color = colorOf(key)\n if (barMode === 'daily') {\n return {\n key,\n name,\n color,\n bars: (m?.daily ?? []).map((p, i) => ({\n label: windowDays === 365\n ? ((activeWindow?.daily ?? [])[i]?.date.slice(5).replace('-', '/') ?? '')\n : ((activeWindow?.daily ?? [])[i]?.date.slice(8, 10) ?? ''),\n value: chartMetricValue(chartMetric, p),\n title: t('chart.tooltip', {\n head: `${name} \u00B7 ${(activeWindow?.daily ?? [])[i]?.date ?? ''}`,\n tokens: fmtInt(p.total), cost: fmt(p.cost), calls: t('common.calls', { count: p.calls }),\n }),\n })),\n }\n }\n return {\n key,\n name,\n color,\n bars: (m?.hourly ?? []).map((p, i) => ({\n label: String(i),\n value: chartMetricValue(chartMetric, p),\n title: t('chart.tooltip', {\n head: `${name} \u00B7 ${t('chart.hourPoint', { hour: i })}`,\n tokens: fmtInt(p.total), cost: fmt(p.cost), calls: t('common.calls', { count: p.calls }),\n }),\n })),\n }\n })\n const dailyLabelEvery = windowDays === 7 ? 1 : windowDays === 30 ? 5 : windowDays === 90 ? 15 : 30\n const dailyMinWidth = windowDays === 365 ? 1825 : undefined\n const noticeText = notice === null ? null : t(\n notice.kind === 'all'\n ? 'error.refreshCached'\n : notice.kind === 'balance' ? 'error.balanceRefreshCached' : 'error.usageRefreshCached',\n { error: localizeApiError(notice.error, t, 'error.query') },\n )\n const errorText = error === null ? null : localizeApiError(error, t, 'error.query')\n\n const composerSafeAreaStyle = {\n '--dq-composer-h': `${composerHeight ?? COMPOSER_FALLBACK_PX}px`,\n // Only set once measured; .dq-balance's max-width falls back to 860px\n // without it (pre-measurement render keeps the settled wide-viewport\n // column, then narrows in place \u2014 no flash of the old width).\n ...(composerWidth !== null ? { '--dq-composer-w': `${composerWidth}px` } : {}),\n } as CSSProperties\n\n return (\n <div className=\"dq-balance\" ref={rootRef} style={composerSafeAreaStyle}>\n <div className=\"dq-status-row\">\n <div className=\"dq-status-messages\">\n <span\n className={`dq-sync dq-sync--${syncState}`}\n role=\"status\"\n aria-live=\"polite\"\n title={usageUpdatedAt === null\n ? t('status.neverSynced')\n : t('status.usageTime', { time: new Date(usageUpdatedAt).toLocaleString(locale === 'en' ? 'en-US' : 'zh-CN', { timeZone: 'Asia/Shanghai' }) })}\n >\n <span className=\"dq-sync-dot\" aria-hidden=\"true\" />\n {syncStatusText(syncState, usageUpdatedAt, freshnessNow, t)}\n </span>\n {noticeText !== null && <span className=\"dq-warn\">{noticeText}</span>}\n {errorText !== null && <span className=\"dq-error\">{errorText}</span>}\n </div>\n <button\n type=\"button\"\n className=\"dq-refresh-btn\"\n title={t('status.forceRefresh')}\n disabled={refreshing}\n aria-busy={refreshing}\n onClick={() => { void load(true) }}\n >\n <span className={`dq-refresh-icon${refreshing ? ' dq-refresh-icon--spin' : ''}`}>\u21BB</span>\n {refreshing ? t('status.refreshing') : t('status.refresh')}\n </button>\n </div>\n\n {balanceLow && (\n <div className=\"dq-alert\" role=\"status\">\n <span className=\"dq-alert-icon\" aria-hidden=\"true\">!</span>\n <div>\n <strong>{t('alert.lowTitle', { balance: fmt(balanceValue), currency: primary?.currency, threshold: fmt(lowBalance) })}</strong>\n {daysLeft !== null && t('alert.runway', { days: daysLeftText })}{t('common.period')}\n <a className=\"dq-alert-link\" href=\"https://platform.deepseek.com/top_up\" target=\"_blank\" rel=\"noreferrer\">{t('alert.topUp')}</a>\n </div>\n </div>\n )}\n\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-title\">{t('balance.title')}</div>\n {primary !== null ? (\n <div className=\"dq-balance-grid\">\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('balance.remaining')}</div>\n <details\n className=\"dq-stat-value dq-remaining\"\n open={remainingHovering || remainingManuallyOpened}\n onPointerEnter={() => setRemainingHovering(true)}\n onPointerLeave={() => setRemainingHovering(false)}\n >\n <summary\n title={t('balance.breakdownTitle')}\n onClick={e => {\n // Own the toggle fully instead of the native default action: a controlled\n // <details> can be forced open by hover, and a plain native toggle here\n // would fight that (e.g. it flips to \"closed\" while the mouse is still\n // hovering). Preventing default and flipping our own \"manually opened\"\n // state keeps hover-preview and click/keyboard/tap-to-pin independent.\n e.preventDefault()\n setRemainingManuallyOpened(v => !v)\n }}\n >\n {fmt(primary.total)} {primary.currency}\n </summary>\n <div className=\"dq-remaining-breakdown\">\n <span>{t('balance.toppedUp', { amount: fmt(primary.toppedUp) })}</span>\n <span className=\"dq-remaining-granted\">{t('balance.granted', { amount: fmt(primary.granted) })}</span>\n </div>\n </details>\n </div>\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('balance.runway')}</div>\n <details\n className={`dq-stat-value dq-runway${daysLeft !== null && daysLeft < 3 ? ' dq-stat-value--bad' : ''}`}\n open={runwayHovering || runwayManuallyOpened}\n onPointerEnter={() => setRunwayHovering(true)}\n onPointerLeave={() => setRunwayHovering(false)}\n >\n <summary\n title={t('balance.runwayBreakdownTitle')}\n onClick={e => {\n // Own the toggle fully instead of the native default action: a controlled\n // <details> can be forced open by hover, and a plain native toggle here\n // would fight that (e.g. it flips to \"closed\" while the mouse is still\n // hovering). Preventing default and flipping our own \"manually opened\"\n // state keeps hover-preview and click/keyboard/tap-to-pin independent.\n e.preventDefault()\n setRunwayManuallyOpened(v => !v)\n }}\n >\n {daysLeftText}\n </summary>\n <div className=\"dq-runway-detail\">{runwayTitle}</div>\n </details>\n </div>\n <Stat label={t('common.status')}>\n <div className={`dq-stat-value${balance?.isAvailable === false ? ' dq-stat-value--bad' : ' dq-stat-value--ok'}`}>\n {balance?.isAvailable === false ? t('common.unavailable') : t('common.available')}\n </div>\n </Stat>\n </div>\n ) : loadingBalance ? (\n <BalanceSkeleton />\n ) : (\n <div className=\"dq-empty\">{t('balance.empty')}</div>\n )}\n </div>\n\n {props.sessionId !== undefined && props.sessionId !== '' && (\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-title\">{t('sessionUsage.title')}</div>\n {sessionUsage !== null ? (\n <SessionUsageCard data={sessionUsage} />\n ) : loadingSessionUsage ? (\n <SessionUsageSkeleton />\n ) : sessionUsageError !== null ? (\n <div className=\"dq-empty dq-empty--error\">{localizeApiError(sessionUsageError, t, 'error.query')}</div>\n ) : (\n <div className=\"dq-empty\">{t('sessionUsage.empty')}</div>\n )}\n </div>\n )}\n\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-title\">{t('overview.title')}</div>\n {usage !== null ? (\n <div className=\"dq-period-grid\">\n <Period label={t('overview.today')} period={usage.summary.today} previous={usage.summary.yesterday} compare={t('overview.vsYesterday')} />\n <Period label={t('overview.month')} period={usage.summary.month} previous={usage.summary.lastMonthToDate} compare={t('overview.vsLastMonth')} />\n <Period\n label={t('overview.total')}\n period={{ total: usage.totals.total, cost: usage.totals.cost, calls: usage.totals.calls }}\n />\n </div>\n ) : loadingUsage ? (\n\n <div className=\"dq-period-grid\">\n {[0, 1, 2].map(i => (\n <div key={i} className=\"dq-period\">\n <Skel w={40} h={11} /><Skel w={104} h={24} /><Skel w={132} h={11} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('overview.empty')}</div>\n )}\n {usage !== null && (\n <BudgetMeter\n spent={usage.summary.month.cost}\n budget={monthlyBudget}\n onConfigure={() => budgetInputRef.current?.focus()}\n />\n )}\n {usage !== null && <PricingNote pricing={usage.pricing} />}\n </div>\n\n {usage !== null && activeWindow !== null && activeWindow.vision !== undefined\n && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions) ? (\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('vision.title')}</div>\n <VisionCard\n vision={activeWindow.vision}\n daily={activeWindow.visionDaily}\n sessions={activeWindow.visionSessions}\n windowLabel={activeWindowName}\n inputTokens={activeWindow.totals.input}\n costTotal={activeWindow.totals.cost}\n />\n </div>\n ) : usage === null ? (\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('vision.title')}</div>\n {loadingUsage ? (\n <div className=\"dq-usage-totals\">\n {[64, 88, 72].map((w, i) => (\n <div key={i} className=\"dq-stat\"><Skel w={w} h={11} /><Skel w={72} h={20} /></div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('vision.empty', { window: activeWindowName })}</div>\n )}\n </div>\n ) : (\n // Host serving a pre-vision payload (e.g. not restarted yet): hide the\n // whole card instead of leaving a titled shell \u2014 it reappears as soon\n // as the host half serves the new shape.\n null\n )}\n\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-head\">\n <div className=\"dq-card-title\">{t('usage.trendTitle')}</div>\n {usage !== null && (\n <div className=\"dq-card-actions\">\n <UsageWindowPicker value={windowDays} onChange={changeWindowDays} />\n <ExportMenu usage={usage} />\n </div>\n )}\n </div>\n {usage !== null && activeWindow !== null ? (\n <>\n <div className=\"dq-usage-totals\">\n <Stat label={t('usage.windowInput', { window: activeWindowName })}><div className=\"dq-stat-value\">{fmtCompact(activeWindow.totals.input, locale)}</div></Stat>\n <Stat label={t('common.output')}><div className=\"dq-stat-value\">{fmtCompact(activeWindow.totals.output, locale)}</div></Stat>\n <Stat label={t('common.cacheHit')}><div className=\"dq-stat-value\">{fmtCompact(activeWindow.totals.cache, locale)}</div></Stat>\n <Stat label={t('usage.modelCalls')}><div className=\"dq-stat-value\">{fmtInt(activeWindow.totals.calls)}</div></Stat>\n </div>\n <div className=\"dq-chart-block-head\">\n <div className=\"dq-chart-title\">\n {barMode === 'daily'\n ? t('chart.dailyTitle', { window: activeWindowName, metric: chartMetricName(chartMetric, t) })\n : t('chart.hourlyTitle', { window: activeWindowName, metric: chartMetricName(chartMetric, t) })}\n {chartPeak > 0 && <span className=\"dq-chart-peak\">{t('chart.peak', { value: chartPeakText })}</span>}\n </div>\n <div className=\"dq-chart-controls\">\n <ModelPicker models={modelList} selected={effectiveSelection} colorOf={colorOf} onChange={setSelectedModels} />\n <ChartMetricPicker value={chartMetric} onChange={changeChartMetric} />\n <div className=\"dq-chart-switch\" role=\"tablist\" aria-label={t('chart.dimensionLabel')}>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={barMode === 'daily'}\n className={`dq-chart-switch-btn${barMode === 'daily' ? ' dq-chart-switch-btn--on' : ''}`}\n onClick={() => setBarMode('daily')}\n >\n {t('chart.daily')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={barMode === 'hourly'}\n className={`dq-chart-switch-btn${barMode === 'hourly' ? ' dq-chart-switch-btn--on' : ''}`}\n onClick={() => setBarMode('hourly')}\n >\n {t('chart.hourly')}\n </button>\n </div>\n </div>\n </div>\n {activeWindow.totals.calls === 0 ? (\n <div className=\"dq-empty\">{t('chart.noWindowData', { window: activeWindowName })}</div>\n ) : grouped.length > 0 ? (\n <>\n <GroupedBars\n series={grouped}\n height={barMode === 'daily' ? 120 : 100}\n labelEvery={barMode === 'daily' ? dailyLabelEvery : 3}\n minWidth={barMode === 'daily' ? dailyMinWidth : undefined}\n />\n <div className=\"dq-legend\">\n {grouped.map(s => (\n <span key={s.key} className=\"dq-legend-item\">\n <span className=\"dq-legend-swatch\" style={{ background: s.color }} />\n {s.name}\n </span>\n ))}\n </div>\n </>\n ) : barMode === 'daily' ? (\n <Bars data={dailyBars} height={120} labelEvery={dailyLabelEvery} minWidth={dailyMinWidth} />\n ) : (\n <Bars data={hourlyBars} height={100} labelEvery={3} />\n )}\n <CoverageDiagnostics coverage={usage.coverage} />\n </>\n ) : loadingUsage ? (\n <UsageSkeleton />\n ) : (\n <div className=\"dq-empty\">{t('chart.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('heatmap.title')}</div>\n {usage !== null ? (\n <>\n <div className=\"dq-chart-title\">{t('chart.heatmapTitle')}</div>\n <Heatmap data={usage.heatmap} />\n </>\n ) : loadingUsage ? (\n <HeatmapSkeleton />\n ) : (\n <div className=\"dq-empty\">{t('chart.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('peak.title')}</div>\n {usage !== null ? (\n <PeakCard split={usage.peakSplit} pricing={usage.pricing} />\n ) : loadingUsage ? (\n <>\n <div className=\"dq-peak-track\" />\n <div className=\"dq-peak-figures\"><Skel w={120} h={11} /><Skel w={96} h={20} /></div>\n </>\n ) : (\n <div className=\"dq-empty\">{t('peak.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('cache.title')}</div>\n {usage !== null ? (\n <CacheCard totals={usage.totals} />\n ) : loadingUsage ? (\n <div className=\"dq-cache-figures\">\n {[60, 96, 72].map((w, i) => (\n <div key={i} className=\"dq-stat\"><Skel w={w} h={11} /><Skel w={72} h={20} /></div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('cache.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('models.title', { window: activeWindowName })}</div>\n {activeWindow !== null ? (\n <ModelRanking models={modelList} totalCost={modelCostTotal} colorOf={colorOf} />\n ) : loadingUsage ? (\n <div className=\"dq-rank\">\n {[0, 1].map(i => (\n <div key={i} className=\"dq-rank-row\">\n <Skel w={148} h={13} />\n <div className=\"dq-rank-track\" />\n <Skel w={230} h={11} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('models.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('sessions.title', { window: activeWindowName })}</div>\n {activeWindow !== null ? (\n <SessionRanking sessions={activeWindow.sessions} count={activeWindow.sessionCount} totalCost={activeWindow.totals.cost} />\n ) : loadingUsage ? (\n <div className=\"dq-sessions\">\n {[0, 1, 2].map(i => (\n <div key={i} className=\"dq-session\">\n <Skel w={200} h={13} />\n <div className=\"dq-session-track\" />\n <Skel w={250} h={11} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('sessions.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('settings.title')}</div>\n <div className=\"dq-links-title\">{t('links.title')}</div>\n <div className=\"dq-links\">\n <Link href=\"https://platform.deepseek.com/usage\">{t('links.usage')}</Link>\n <Link href=\"https://platform.deepseek.com/api_keys\">{t('links.apiKey')}</Link>\n <Link href=\"https://status.deepseek.com\">{t('links.status')}</Link>\n </div>\n <label className=\"dq-toggle\">\n <input type=\"checkbox\" checked={widgetOn} onChange={toggle} />\n <span>{t('settings.widget')}</span>\n </label>\n <div className=\"dq-toggle-hint\">{t('settings.widgetHint')}</div>\n <div className=\"dq-setting dq-setting--tabs\">\n <div className=\"dq-setting-label\" id=\"dq-widget-tabs-label\">{t('settings.widgetTabs')}</div>\n <div className=\"dq-setting-control dq-widget-tabs\" role=\"group\" aria-labelledby=\"dq-widget-tabs-label\">\n <div className=\"dq-widget-tab-list\">\n {widgetTabs.map(tab => (\n <label className=\"dq-widget-tab\" key={tab.id}>\n <input\n type=\"checkbox\"\n checked={widgetTabIds === null || widgetTabIds.includes(tab.id)}\n onChange={() => toggleWidgetTab(tab.id)}\n />\n <span>{tab.label}</span>\n </label>\n ))}\n </div>\n <span className=\"dq-setting-hint\">\n {widgetTabs.length === 0 ? t('settings.widgetTabsEmpty') : t('settings.widgetTabsHint')}\n </span>\n </div>\n </div>\n <div className=\"dq-setting\">\n <label className=\"dq-setting-label\" htmlFor=\"dq-low-balance\">{t('settings.lowBalance')}</label>\n <div className=\"dq-setting-control\">\n <input\n id=\"dq-low-balance\"\n className=\"dq-number\"\n type=\"number\"\n min={0}\n step={1}\n value={lowBalance}\n onChange={e => changeLowBalance(Math.max(0, Number(e.target.value) || 0))}\n />\n <span className=\"dq-setting-hint\">\n {lowBalance > 0\n ? t('settings.lowBalanceOn', { amount: fmt(lowBalance), currency: primary?.currency ?? 'CNY' })\n : t('settings.lowBalanceOff')}\n </span>\n </div>\n </div>\n <div className=\"dq-setting\">\n <label className=\"dq-setting-label\" htmlFor=\"dq-monthly-budget\">{t('settings.monthlyBudget')}</label>\n <div className=\"dq-setting-control\">\n <input\n ref={budgetInputRef}\n id=\"dq-monthly-budget\"\n className=\"dq-number\"\n type=\"number\"\n min={0}\n step={10}\n value={monthlyBudget}\n aria-describedby=\"dq-monthly-budget-hint\"\n onChange={e => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))}\n />\n <span id=\"dq-monthly-budget-hint\" className=\"dq-setting-hint\">\n {monthlyBudget > 0\n ? t('settings.monthlyBudgetOn', { amount: fmt(monthlyBudget) })\n : t('settings.monthlyBudgetOff')}\n </span>\n </div>\n </div>\n </div>\n\n <p className=\"dq-footer\">{__PLUGIN_LABEL__}</p>\n </div>\n )\n}\n", "export type BudgetStatus = 'healthy' | 'risk' | 'over'\n\nexport interface BudgetSnapshot {\n spent: number\n budget: number\n ratio: number\n remaining: number\n forecast: number\n forecastOver: number\n status: BudgetStatus\n}\n\nconst BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000\n\n/**\n * Project the current month's cost to month end. DeepSeek's billing boundary\n * is Beijing time, so this stays deterministic even when the browser runs in\n * another timezone.\n */\nexport function budgetSnapshot(spentValue: number, budgetValue: number, nowMs = Date.now()): BudgetSnapshot {\n const spent = Number.isFinite(spentValue) ? Math.max(0, spentValue) : 0\n const budget = Number.isFinite(budgetValue) ? Math.max(0, budgetValue) : 0\n const beijingNow = new Date(nowMs + BEIJING_OFFSET_MS)\n const year = beijingNow.getUTCFullYear()\n const month = beijingNow.getUTCMonth()\n const start = Date.UTC(year, month, 1) - BEIJING_OFFSET_MS\n const end = Date.UTC(year, month + 1, 1) - BEIJING_OFFSET_MS\n const elapsedRatio = Math.min(1, Math.max(1 / (end - start), (nowMs - start) / (end - start)))\n const forecast = spent / elapsedRatio\n const ratio = budget > 0 ? spent / budget : 0\n const remaining = budget - spent\n const forecastOver = budget > 0 ? Math.max(0, forecast - budget) : 0\n const status: BudgetStatus = ratio >= 1\n ? 'over'\n : (ratio >= 0.8 || forecastOver > 0 ? 'risk' : 'healthy')\n\n return { spent, budget, ratio, remaining, forecast, forecastOver, status }\n}\n", "/** Dependency-free bar chart and heatmap primitives. */\nimport { useEffect, useRef, useState, type FocusEvent as ReactFocusEvent, type KeyboardEvent as ReactKeyboardEvent, type MutableRefObject, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react'\nimport { isTapGesture, lastPopulatedIndex, maxOrOne, nextChartFocus, nextPinnedIndex, stackedTotals, type ChartFocusKey } from './chart-focus.ts'\nimport { useI18n, type LocaleId } from './i18n.tsx'\nimport type { LocaleKey } from './locales.ts'\n\nexport interface BarDatum {\n label: string\n value: number\n /** Tooltip text, composed as `heading \u00B7 row \u00B7 row`; each ` \u00B7 ` segment is\n * rendered on its own line, the first as the heading. */\n title?: string\n}\n\ninterface TipState {\n x: number\n y: number\n lines: string[]\n above: boolean\n}\n\n/**\n * Cursor-following tooltip shared by every chart. The native `title` attribute\n * this replaces waited about a second, could not be styled to match the shell,\n * and never appeared on touch.\n *\n * Touch has no hover, so a tap pins the tooltip open instead of showing it only\n * while the finger is down (which produced the original one-flicker-then-gone\n * complaint). `pinned` and `hide()` share one `pinnedRef` so a `touchend`\n * followed synchronously by `pointerleave` \u2014 both native events fired in the\n * same tick, before React re-renders \u2014 never reads a stale \"nothing pinned\"\n * state and hides the tooltip it was just asked to pin.\n */\nfunction useChartTip(): {\n wrapRef: MutableRefObject<HTMLDivElement | null>\n tip: TipState | null\n pinned: number | null\n show: (e: ReactPointerEvent<HTMLElement>, text: string | undefined) => void\n showFocused: (e: ReactFocusEvent<HTMLElement>, text: string | undefined) => void\n hide: () => void\n onPointStart: (e: ReactPointerEvent<HTMLElement>) => void\n onPointEnd: (e: ReactPointerEvent<HTMLElement>, index: number, text: string | undefined) => void\n} {\n const wrapRef = useRef<HTMLDivElement | null>(null)\n const [tip, setTip] = useState<TipState | null>(null)\n const [pinned, setPinned] = useState<number | null>(null)\n const pinnedRef = useRef<number | null>(null)\n const touchStart = useRef<{ x: number; y: number; t: number } | null>(null)\n const setPin = (next: number | null): void => {\n pinnedRef.current = next\n setPinned(next)\n }\n const showAt = (target: HTMLElement, text: string | undefined, clientX?: number, clientY?: number): void => {\n const wrap = wrapRef.current\n if (text === undefined || text === '' || wrap === null) return\n const rect = wrap.getBoundingClientRect()\n const targetRect = target.getBoundingClientRect()\n // Keep the (centred) bubble inside the chart's own box.\n const x = Math.min(Math.max((clientX ?? targetRect.left + targetRect.width / 2) - rect.left, 70), Math.max(rect.width - 70, 70))\n const viewportY = clientY ?? targetRect.top\n const y = viewportY - rect.top\n setTip({ x, y, lines: text.split(' \u00B7 '), above: viewportY > window.innerHeight - 160 })\n }\n const show = (e: ReactPointerEvent<HTMLElement>, text: string | undefined): void => {\n // Touch has no hover; a bare `pointermove` while a finger drags across the\n // chart (e.g. scrolling the page) would otherwise flash tooltips at every\n // point it crosses. Touch only opens a tooltip via the deliberate tap\n // handled in `onPointEnd`.\n if (e.pointerType === 'touch') return\n showAt(e.currentTarget, text, e.clientX, e.clientY)\n }\n const showFocused = (e: ReactFocusEvent<HTMLElement>, text: string | undefined): void => {\n showAt(e.currentTarget, text)\n }\n const hide = (): void => {\n if (pinnedRef.current !== null) return\n setTip(null)\n }\n const onPointStart = (e: ReactPointerEvent<HTMLElement>): void => {\n if (e.pointerType === 'touch') touchStart.current = { x: e.clientX, y: e.clientY, t: Date.now() }\n }\n const onPointEnd = (e: ReactPointerEvent<HTMLElement>, index: number, text: string | undefined): void => {\n if (e.pointerType !== 'touch') return\n const start = touchStart.current\n touchStart.current = null\n if (start === null || !isTapGesture(e.clientX - start.x, e.clientY - start.y, Date.now() - start.t)) return\n const next = nextPinnedIndex(pinnedRef.current, index)\n setPin(next)\n if (next === null) setTip(null)\n else showAt(e.currentTarget, text, e.clientX, e.clientY)\n }\n useEffect(() => {\n if (pinned === null) return undefined\n const onDocPointerDown = (e: PointerEvent): void => {\n const wrap = wrapRef.current\n if (wrap !== null && e.target instanceof Node && wrap.contains(e.target)) return\n setPin(null)\n setTip(null)\n }\n document.addEventListener('pointerdown', onDocPointerDown)\n return () => document.removeEventListener('pointerdown', onDocPointerDown)\n }, [pinned])\n return { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd }\n}\n\nfunction useRovingChartPoints(count: number, initial: number, horizontalStep = 1): {\n active: number\n refs: MutableRefObject<Array<HTMLElement | null>>\n onKeyDown: (event: ReactKeyboardEvent<HTMLElement>, index: number) => void\n} {\n const refs = useRef<Array<HTMLElement | null>>([])\n const [active, setActive] = useState(initial)\n useEffect(() => setActive(Math.min(Math.max(0, initial), Math.max(0, count - 1))), [count, initial])\n const onKeyDown = (event: ReactKeyboardEvent<HTMLElement>, index: number): void => {\n if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) return\n event.preventDefault()\n const next = nextChartFocus(index, count, event.key as ChartFocusKey, horizontalStep)\n setActive(next)\n const target = refs.current[next]\n target?.focus({ preventScroll: true })\n target?.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }\n return { active, refs, onKeyDown }\n}\n\nfunction ChartTip(props: { tip: TipState | null }): ReactElement | null {\n const { tip } = props\n if (tip === null) return null\n const [head, ...rows] = tip.lines\n return (\n <div className={`dq-tip${tip.above ? ' dq-tip--above' : ''}`} style={{ left: `${tip.x}px`, top: `${tip.y}px` }} role=\"tooltip\">\n <div className=\"dq-tip-head\">{head}</div>\n {rows.map((row, i) => <div key={i} className=\"dq-tip-row\">{row}</div>)}\n </div>\n )\n}\n\n/** Color palette for per-model grouped bars, cycled by model order. */\nexport const MODEL_COLORS = ['#4176e6', '#2da44e', '#e16f24', '#8250df', '#bf8700', '#cf222e', '#1f883d', '#0969da']\n\nexport function Bars(props: { data: BarDatum[]; height?: number; labelEvery?: number; minWidth?: number }): ReactElement {\n const { data, height = 120, labelEvery = 1, minWidth } = props\n const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip()\n const scrollRef = useRef<HTMLDivElement | null>(null)\n const initialFocus = lastPopulatedIndex(data.map(datum => datum.value))\n const roving = useRovingChartPoints(data.length, initialFocus)\n useEffect(() => {\n const scroll = scrollRef.current\n if (scroll !== null && minWidth !== undefined) scroll.scrollLeft = scroll.scrollWidth\n }, [data.length, minWidth])\n let max = 0\n for (const datum of data) if (datum.value > max) max = datum.value\n const scale = max > 0 ? max : 1\n return (\n <div className=\"dq-chart-wrap\" ref={wrapRef} onPointerLeave={hide}>\n <div className=\"dq-bars-scroll\" ref={scrollRef}>\n <div className=\"dq-bars\" style={{ height: `${height + 18}px`, minWidth: minWidth === undefined ? undefined : `${minWidth}px` }}>\n {data.map((datum, i) => {\n const h = Math.max(1, Math.round((datum.value / scale) * height))\n const text = datum.title ?? `${datum.label}: ${datum.value.toLocaleString()}`\n return (\n <div\n key={i}\n className={`dq-bar-col${pinned === i ? ' dq-bar-col--pinned' : ''}`}\n role=\"img\"\n aria-label={text}\n aria-keyshortcuts=\"ArrowLeft ArrowRight ArrowUp ArrowDown Home End\"\n tabIndex={i === roving.active ? 0 : -1}\n ref={element => { roving.refs.current[i] = element }}\n onPointerMove={e => show(e, text)}\n onPointerDown={onPointStart}\n onPointerUp={e => onPointEnd(e, i, text)}\n onFocus={e => showFocused(e, text)}\n onBlur={hide}\n onKeyDown={e => roving.onKeyDown(e, i)}\n >\n <div className=\"dq-bar\" style={{ height: `${h}px` }} />\n <div className=\"dq-bar-label\" style={{ visibility: i % labelEvery === 0 ? 'visible' : 'hidden' }}>{datum.label}</div>\n </div>\n )\n })}\n </div>\n </div>\n <ChartTip tip={tip} />\n </div>\n )\n}\n\nexport interface HeatDatum {\n date: string\n total: number\n cost: number\n calls: number\n}\n\n/**\n * Multi-series bar chart: one bar per time slot, each series' value stacked\n * inside it (series[0] at the bottom, later series stacked above, same order\n * in every column). Segment height is scaled against the largest *per-slot\n * total* across all slots (not the largest single value) so the stacked\n * height reflects that slot's combined usage and columns stay comparable to\n * each other. Each segment stays its own focusable/hoverable/tappable point \u2014\n * `pointIndex = i * series.length + seriesIndex` is unchanged from the old\n * side-by-side layout, so `chart-focus.ts`'s roving tabindex (plain \u00B11 /\n * Home / End, no 2-D semantics) needs no changes: arrow keys still move one\n * point at a time, they just now walk bottom-to-top through one column's\n * models before advancing to the next column, instead of left-to-right\n * through side-by-side bars.\n */\nexport function GroupedBars(props: {\n series: Array<{ key: string; color: string; bars: BarDatum[] }>\n height?: number\n labelEvery?: number\n minWidth?: number\n}): ReactElement {\n const { series, height = 120, labelEvery = 1, minWidth } = props\n const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip()\n const scrollRef = useRef<HTMLDivElement | null>(null)\n const count = series.length > 0 ? series[0].bars.length : 0\n const pointValues = Array.from({ length: count }, (_, i) => series.map(s => s.bars[i]?.value ?? 0)).flat()\n const roving = useRovingChartPoints(pointValues.length, lastPopulatedIndex(pointValues))\n useEffect(() => {\n const scroll = scrollRef.current\n if (scroll !== null && minWidth !== undefined) scroll.scrollLeft = scroll.scrollWidth\n }, [count, minWidth])\n const scale = maxOrOne(stackedTotals(series, count))\n return (\n <div className=\"dq-chart-wrap\" ref={wrapRef} onPointerLeave={hide}>\n <div className=\"dq-bars-scroll\" ref={scrollRef}>\n <div className=\"dq-bars\" style={{ height: `${height + 18}px`, minWidth: minWidth === undefined ? undefined : `${minWidth}px` }}>\n {Array.from({ length: count }, (_, i) => (\n <div\n key={i}\n className=\"dq-bar-col\"\n >\n <div className=\"dq-bar-group\">\n {series.map((s, seriesIndex) => {\n const datum = s.bars[i]\n const h = Math.max(1, Math.round((datum.value / scale) * height))\n const text = datum.title ?? `${s.key}: ${datum.value.toLocaleString()}`\n const pointIndex = i * series.length + seriesIndex\n return (\n <div\n key={s.key}\n className={`dq-bar${pinned === pointIndex ? ' dq-bar--pinned' : ''}`}\n style={{ height: `${h}px`, background: s.color }}\n role=\"img\"\n aria-label={text}\n aria-keyshortcuts=\"ArrowLeft ArrowRight ArrowUp ArrowDown Home End\"\n tabIndex={pointIndex === roving.active ? 0 : -1}\n ref={element => { roving.refs.current[pointIndex] = element }}\n onPointerMove={e => show(e, text)}\n onPointerDown={onPointStart}\n onPointerUp={e => onPointEnd(e, pointIndex, text)}\n onFocus={e => showFocused(e, text)}\n onBlur={hide}\n onKeyDown={e => roving.onKeyDown(e, pointIndex)}\n />\n )\n })}\n </div>\n <div className=\"dq-bar-label\" style={{ visibility: i % labelEvery === 0 ? 'visible' : 'hidden' }}>\n {series[0].bars[i].label}\n </div>\n </div>\n ))}\n </div>\n </div>\n <ChartTip tip={tip} />\n </div>\n )\n}\n\nfunction heatColor(level: number): string {\n if (level <= 0) return 'rgba(120,130,150,0.12)'\n if (level === 1) return 'rgba(65,118,230,0.25)'\n if (level === 2) return 'rgba(65,118,230,0.5)'\n if (level === 3) return 'rgba(65,118,230,0.75)'\n return 'rgba(65,118,230,1)'\n}\n\n/**\n * Calendar heatmap: one flex column per week, cells filling the card's full\n * width (rather than a fixed pixel size hugging the left edge), month labels\n * below the grid and a \u5C11\u2192\u591A scale legend \u2014 modelled on Codex's activity\n * graph. No weekday row: at this cell count a day is identified by hovering\n * it, not by which row it sits in.\n */\nexport function Heatmap(props: { data: HeatDatum[] }): ReactElement {\n const { data } = props\n const { t, locale } = useI18n()\n const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip()\n const roving = useRovingChartPoints(data.length, lastPopulatedIndex(data.map(datum => datum.total)), 7)\n let max = 0\n for (const datum of data) if (datum.total > max) max = datum.total\n let firstDow = 0\n if (data.length > 0) {\n const dt = new Date(`${data[0].date}T00:00:00`)\n firstDow = Number.isNaN(dt.getTime()) ? 0 : dt.getDay()\n }\n const cells: Array<HeatDatum | null> = []\n for (let i = 0; i < firstDow; i++) cells.push(null)\n for (const datum of data) cells.push(datum)\n while (cells.length % 7 !== 0) cells.push(null)\n\n const weeks: Array<Array<HeatDatum | null>> = []\n for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7))\n\n // Label a column with its month when it is the first column of that month.\n let lastMonth = ''\n const monthLabels = weeks.map(week => {\n const first = week.find(cell => cell !== null)\n if (first === undefined || first === null) return ''\n const month = first.date.slice(0, 7)\n if (month === lastMonth) return ''\n lastMonth = month\n return t(`chart.month${Number(first.date.slice(5, 7))}` as LocaleKey)\n })\n\n const levelOf = (total: number): number => {\n if (total <= 0) return 0\n const r = max > 0 ? total / max : 0\n return r < 0.25 ? 1 : r < 0.5 ? 2 : r < 0.75 ? 3 : 4\n }\n\n return (\n <div className=\"dq-heat dq-chart-wrap\" ref={wrapRef} onPointerLeave={hide}>\n <div className=\"dq-heat-scroll\">\n <div className=\"dq-heatmap\">\n {weeks.map((week, w) => (\n <div key={w} className=\"dq-heat-week\">\n {week.map((datum, d) => {\n if (datum === null) return <div key={d} className=\"dq-heat-cell dq-heat-cell--pad\" />\n const text = t('chart.tooltip', {\n head: datum.date,\n tokens: datum.total.toLocaleString(locale === 'en' ? 'en-US' : 'zh-CN'),\n cost: datum.cost.toFixed(2),\n calls: t('common.calls', { count: datum.calls }),\n })\n const index = w * 7 + d - firstDow\n return (\n <div\n key={d}\n className={`dq-heat-cell${pinned === index ? ' dq-heat-cell--pinned' : ''}`}\n style={{ background: heatColor(levelOf(datum.total)) }}\n role=\"img\"\n aria-label={text}\n aria-keyshortcuts=\"ArrowLeft ArrowRight ArrowUp ArrowDown Home End\"\n tabIndex={index === roving.active ? 0 : -1}\n ref={element => { roving.refs.current[index] = element }}\n onPointerMove={e => show(e, text)}\n onPointerDown={onPointStart}\n onPointerUp={e => onPointEnd(e, index, text)}\n onFocus={e => showFocused(e, text)}\n onBlur={hide}\n onKeyDown={e => roving.onKeyDown(e, index)}\n />\n )\n })}\n </div>\n ))}\n </div>\n <div className=\"dq-heat-months\" aria-hidden=\"true\">\n {monthLabels.map((label, i) => <div key={i} className=\"dq-heat-month\">{label}</div>)}\n </div>\n </div>\n <ChartTip tip={tip} />\n <div className=\"dq-heat-scale\">\n <span>{t('chart.less')}</span>\n {[0, 1, 2, 3, 4].map(level => (\n <span key={level} className=\"dq-heat-key\" style={{ background: heatColor(level) }} />\n ))}\n <span>{t('chart.more')}</span>\n </div>\n </div>\n )\n}\n\nexport const fmt = (v: string | number): string => {\n const n = Number(v)\n if (!Number.isFinite(n)) return String(v)\n return n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })\n}\n\nexport const fmtInt = (v: number | undefined): string => Math.round(Number(v) || 0).toLocaleString()\n\nexport const fmtCompact = (v: number | undefined, locale: LocaleId = 'zh'): string => {\n const n = Number(v) || 0\n if (locale === 'en') {\n return new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(n)\n }\n if (n >= 100_000_000) return `${(n / 100_000_000).toFixed(1)}\u4EBF`\n if (n >= 10_000) return `${(n / 10_000).toFixed(1)}\u4E07`\n return Math.round(n).toLocaleString()\n}\n", "export type ChartFocusKey = 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown' | 'Home' | 'End'\n\n/** Resolve roving focus without adding every chart point to the page Tab order. */\nexport function nextChartFocus(\n current: number,\n count: number,\n key: ChartFocusKey,\n horizontalStep = 1,\n): number {\n if (count <= 0) return 0\n const index = Math.min(Math.max(0, current), count - 1)\n if (key === 'Home') return 0\n if (key === 'End') return count - 1\n const step = key === 'ArrowLeft' || key === 'ArrowRight' ? Math.max(1, horizontalStep) : 1\n const direction = key === 'ArrowLeft' || key === 'ArrowUp' ? -1 : 1\n return Math.min(Math.max(0, index + direction * step), count - 1)\n}\n\nexport function lastPopulatedIndex(values: number[]): number {\n for (let index = values.length - 1; index >= 0; index -= 1) {\n if (values[index] > 0) return index\n }\n return Math.max(0, values.length - 1)\n}\n\n/**\n * Touch tap toggles a chart point's tooltip pin: tapping the already-pinned\n * point releases it, tapping any other point (or none pinned yet) pins that\n * one instead. Shared by bar, grouped-bar and heatmap touch handling so pin\n * state never forks into a parallel machine per chart type.\n */\nexport function nextPinnedIndex(current: number | null, tapped: number): number | null {\n return current === tapped ? null : tapped\n}\n\n/**\n * Distinguishes a tap from a scroll/drag using the touch/pointer start-to-end\n * displacement and elapsed time. A page scroll that merely passes over a data\n * point moves well past `moveThreshold` before release; a long press held past\n * `timeThreshold` without lifting is not a quick tap either.\n */\nexport function isTapGesture(dx: number, dy: number, dt: number, moveThreshold = 10, timeThreshold = 500): boolean {\n return Math.hypot(dx, dy) <= moveThreshold && dt <= timeThreshold\n}\n\n/**\n * Per-slot totals across every series of a stacked bar chart: each column's\n * height must reflect the *summed* usage of every selected model at that\n * time slot, not any single model's value \u2014 otherwise a column with many\n * small models would look shorter than a column with one large one even\n * though it represents more total usage.\n */\nexport function stackedTotals(series: Array<{ bars: Array<{ value: number }> }>, count: number): number[] {\n return Array.from({ length: count }, (_, i) => series.reduce((sum, s) => sum + (s.bars[i]?.value ?? 0), 0))\n}\n\n/**\n * Highest value in the list, or 1 when every value is zero \u2014 the shared\n * fallback scale so an all-zero chart still divides safely instead of by 0.\n */\nexport function maxOrOne(values: number[]): number {\n let max = 0\n for (const v of values) if (v > max) max = v\n return max > 0 ? max : 1\n}\n", "import { createContext, useContext, type ReactElement, type ReactNode } from 'react'\nimport { zh, type LocaleKey } from './locales.ts'\n\nexport type LocaleId = 'zh' | 'en'\nexport type Translate = (key: LocaleKey, params?: Record<string, unknown>) => string\n\nexport const fallbackT: Translate = (key, params) => {\n const template = zh[key]\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n}\n\nconst TranslationContext = createContext<Translate>(fallbackT)\n\nexport function LocaleProvider(props: { t: Translate; children: ReactNode }): ReactElement {\n return <TranslationContext.Provider value={props.t}>{props.children}</TranslationContext.Provider>\n}\n\nexport function useI18n(): { t: Translate; locale: LocaleId } {\n const t = useContext(TranslationContext)\n return { t, locale: t('meta.locale') === 'en' ? 'en' : 'zh' }\n}\n\n/** Translate host error copy that predates stable machine-readable error codes. */\nexport function localizeApiError(message: string | undefined, t: Translate, fallback: LocaleKey): string {\n if (message === undefined || message === '') return t(fallback)\n if (message === '\u51ED\u8BC1\u670D\u52A1\u4E0D\u53EF\u7528') return t('error.credentialsUnavailable')\n if (message === '\u67E5\u8BE2\u5931\u8D25') return t('error.query')\n if (message === '\u4F59\u989D\u52A0\u8F7D\u5931\u8D25') return t('error.balanceLoad')\n if (message === '\u7528\u91CF\u52A0\u8F7D\u5931\u8D25') return t('error.usageLoad')\n if (message === '\u672A\u914D\u7F6E DEEPSEEK_API_KEY\uFF08\u53EF\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u4E2D\u586B\u5199\uFF09') return t('error.noApiKey')\n if (message === '\u89E3\u6790\u4F59\u989D\u54CD\u5E94\u5931\u8D25') return t('error.balanceParse')\n if (message === '\u4F1A\u8BDD\u6301\u4E45\u5316\u670D\u52A1\u4E0D\u53EF\u7528') return t('error.persistenceUnavailable')\n if (message === '\u7F3A\u5C11\u4F1A\u8BDD id') return t('error.missingSessionId')\n const mappings: Array<[RegExp, LocaleKey, string]> = [\n [/^\u8BFB\u53D6 API Key \u5931\u8D25\uFF1A(.*)$/, 'error.credentialRead', 'detail'],\n [/^\u8BF7\u6C42\u4F59\u989D\u63A5\u53E3\u5931\u8D25\uFF1A(.*)$/, 'error.balanceRequest', 'detail'],\n [/^\u4F59\u989D\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP (\\d+)\uFF09$/, 'error.balanceHttp', 'status'],\n [/^\u8BFB\u53D6\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF1A(.*)$/, 'error.sessionList', 'detail'],\n [/^\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A(.*)$/, 'error.sessionLogRead', 'detail'],\n ]\n for (const [pattern, key, field] of mappings) {\n const match = message.match(pattern)\n if (match !== null) return t(key, { [field]: match[1] })\n }\n return message\n}\n", "/** Locale namespace and complete bilingual copy for the dashboard surfaces. */\nexport const NS = 'dsh-usage-dashboard'\n\nexport const zh = {\n 'meta.locale': 'zh',\n 'nav.quota': '\u989D\u5EA6',\n 'widget.title': 'DeepSeek \u989D\u5EA6',\n 'common.unknownModel': '\u672A\u77E5\u6A21\u578B',\n 'common.calls': '{count} \u6B21\u8C03\u7528',\n 'common.callsShort': '{count} \u6B21',\n 'common.sessions': '{count} \u4E2A\u4F1A\u8BDD',\n 'common.records': '{count} \u6761',\n 'common.rows': '{count} \u884C',\n 'common.input': '\u8F93\u5165',\n 'common.output': '\u8F93\u51FA',\n 'common.cacheHit': '\u7F13\u5B58\u547D\u4E2D',\n 'common.status': '\u72B6\u6001',\n 'common.available': '\u53EF\u7528',\n 'common.unavailable': '\u4E0D\u53EF\u7528',\n 'common.justNow': '\u521A\u521A',\n 'common.minutesAgo': '{count} \u5206\u949F\u524D',\n 'common.hoursAgo': '{count} \u5C0F\u65F6\u524D',\n 'common.daysAgo': '{count} \u5929\u524D',\n 'common.listSeparator': '\u3001',\n 'common.period': '\u3002',\n 'delta.noBaselineTitle': '{label}\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u4ECE\u5BF9\u6BD4',\n 'delta.noBaseline': '\u65E0\u5BF9\u6BD4',\n 'delta.flat': '\u6301\u5E73',\n 'period.volume': '{tokens} tokens \u00B7 {calls}',\n 'budget.unset': '\u8FD8\u6CA1\u8BBE\u7F6E\u6708\u5EA6\u9884\u7B97\uFF0C\u65E0\u6CD5\u63D0\u524D\u5224\u65AD\u662F\u5426\u4F1A\u8D85\u652F\u3002',\n 'budget.configure': '\u8BBE\u7F6E\u9884\u7B97',\n 'budget.over': '\u5DF2\u8D85\u9884\u7B97 \u00A5{amount}',\n 'budget.forecastOver': '\u7167\u5F53\u524D\u901F\u5EA6\uFF0C\u9884\u8BA1\u8D85\u51FA \u00A5{amount}',\n 'budget.forecast': '\u7167\u5F53\u524D\u901F\u5EA6\uFF0C\u9884\u8BA1\u6708\u5E95 \u00A5{amount}',\n 'budget.title': '\u672C\u6708\u9884\u7B97',\n 'budget.progressLabel': '\u672C\u6708\u9884\u7B97\u4F7F\u7528\u8FDB\u5EA6',\n 'budget.used': '\u5DF2\u4F7F\u7528 {percent}%\uFF0C{forecast}',\n 'budget.remaining': '\u5269\u4F59 \u00A5{amount}',\n 'budget.exceeded': '\u8D85\u51FA \u00A5{amount}',\n 'pricing.title': '\u8BA1\u4EF7\u8BF4\u660E',\n 'pricing.currentPeak': '\u5F53\u524D \u9AD8\u5CF0\u65F6\u6BB5',\n 'pricing.currentOffPeak': '\u5F53\u524D \u95F2\u65F6',\n 'pricing.model': '\u6A21\u578B',\n 'pricing.period': '\u65F6\u6BB5',\n 'pricing.inputCacheHit': '\u8F93\u5165\u00B7\u7F13\u5B58\u547D\u4E2D',\n 'pricing.inputCacheMiss': '\u8F93\u5165\u00B7\u672A\u547D\u4E2D',\n 'pricing.output': '\u8F93\u51FA',\n 'pricing.peak': '\u9AD8\u5CF0',\n 'pricing.offPeak': '\u95F2\u65F6',\n 'pricing.unit': '\u5355\u4F4D\uFF1A{currency} / \u767E\u4E07 tokens\u3002',\n 'pricing.splitNote': '\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF08\u5468\u4E00\u81F3\u5468\u4E94\uFF09\uFF0C\u5176\u4F59\u4E3A\u95F2\u65F6\uFF08\u4EF7\u683C\u51CF\u534A\uFF09\uFF1B\u6BCF\u6761\u7528\u91CF\u6309\u53D1\u751F\u65F6\u523B\u751F\u6548\u7684\u5B98\u65B9\u4EF7\u683C\u4F30\u7B97\u3002',\n 'pricing.visionNote': '\u56FE\u7247\u6309\u5C3A\u5BF8\u6298\u7B97 token\uFF0C\u4E0E\u6587\u672C\u4E00\u5E76\u8BA1\u8D39\u3002',\n 'pricing.modelNote': 'deepseek-v4-flash\u3001deepseek-v4-flash-vision-exp \u662F\u65E7\u6A21\u578B\u540D\uFF0C\u73B0\u7531 deepseek-flash \u627F\u63A5\u5E76\u6309 Flash \u4EF7\u8BA1\u8D39\uFF1B2026-09-14 12:00 \u8D77 deepseek-v4-pro \u7684\u8BF7\u6C42\u4E5F\u4F1A\u8DEF\u7531\u5230 V4.1-Flash\uFF0C\u6309 Flash \u4EF7\u8BA1\u8D39\u3002',\n 'pricing.unknown': '\u672A\u77E5\u6A21\u578B\u6309 deepseek-v4-pro \u8BA1\u4EF7\u3002',\n 'coverage.noRange': '\u6682\u65E0\u6709\u6548\u7528\u91CF\u65F6\u95F4',\n 'coverage.title': '\u7EDF\u8BA1\u8303\u56F4',\n 'coverage.gaps': '\u5B58\u5728\u7F3A\u53E3',\n 'coverage.complete': '\u672C\u673A\u8BFB\u53D6\u5B8C\u6574',\n 'coverage.brief': '\u626B\u63CF {scanned} / {listed} \u4E2A\u4F1A\u8BDD \u00B7 {records} \u6761\u7528\u91CF\u8BB0\u5F55',\n 'coverage.scanned': '\u6210\u529F\u626B\u63CF',\n 'coverage.failed': '\u8BFB\u53D6\u5931\u8D25',\n 'coverage.skipped': '\u8DF3\u8FC7\u8BB0\u5F55',\n 'coverage.time': '\u8BB0\u5F55\u65F6\u95F4\uFF1A{range}',\n 'coverage.scope': '\u8FD9\u91CC\u53EA\u7EDF\u8BA1\u5F53\u524D\u8BBE\u5907\u4E0A\u7684 DSH \u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u4E0D\u5305\u542B\u5176\u4ED6\u8BBE\u5907\u3001DeepSeek \u5E73\u53F0\u76F4\u63A5\u8C03\u7528\u6216\u5DF2\u5220\u9664\u7684\u672C\u5730\u65E5\u5FD7\u3002',\n 'coverage.warning': '\u672C\u6B21\u56DE\u653E\u6709\u65E5\u5FD7\u65E0\u6CD5\u8BFB\u53D6\u6216\u7528\u91CF\u8BB0\u5F55\u683C\u5F0F\u5F02\u5E38\uFF0C\u9875\u9762\u6C47\u603B\u53EF\u80FD\u4F4E\u4E8E\u5B9E\u9645\u6D88\u8017\uFF1B\u53EF\u5237\u65B0\u91CD\u8BD5\uFF0C\u5E76\u4EE5 DeepSeek \u5B98\u65B9\u5E73\u53F0\u8D26\u5355\u4E3A\u51C6\u3002',\n 'export.title': '\u5BFC\u51FA\u5168\u90E8\u672C\u673A DSH \u4F1A\u8BDD\u65E5\u5FD7\u6570\u636E\uFF08\u4E0D\u53D7\u7EDF\u8BA1\u5468\u671F\u5F71\u54CD\uFF09',\n 'export.done': '\u5DF2\u5BFC\u51FA',\n 'export.action': '\u5BFC\u51FA',\n 'export.menuLabel': '\u9009\u62E9\u5BFC\u51FA\u683C\u5F0F',\n 'export.dailyCsv': '\u9010\u5929 CSV',\n 'export.modelCsv': '\u9010\u6A21\u578B CSV',\n 'export.fullJson': '\u5B8C\u6574 JSON',\n 'export.jsonHint': '\u542B\u56FE\u8868\u4E0E\u6392\u884C',\n 'cache.empty': '\u8FD8\u6CA1\u6709 prompt token \u53EF\u7EDF\u8BA1\u7F13\u5B58\u547D\u4E2D\u3002',\n 'cache.rate': '\u7F13\u5B58\u547D\u4E2D\u7387',\n 'cache.saved': '\u5DF2\u8282\u7701\u8D39\u7528\uFF08\u4F30\u7B97\uFF09',\n 'cache.allMiss': '\u82E5\u5168\u90E8\u672A\u547D\u4E2D',\n 'cache.aria': '\u7F13\u5B58\u547D\u4E2D {hit}%\uFF0C\u672A\u547D\u4E2D {miss}%',\n 'cache.hitLegend': '\u7F13\u5B58\u547D\u4E2D {tokens}',\n 'cache.missLegend': '\u672A\u547D\u4E2D {tokens}\uFF08\u6309\u672A\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF09',\n 'cache.lowHint': '\u547D\u4E2D\u7387\u504F\u4F4E\uFF1A\u9891\u7E41\u6539\u52A8 system prompt / \u5DE5\u5177\u5B9A\u4E49\u4F1A\u8BA9\u524D\u7F00\u7F13\u5B58\u5931\u6548\uFF0C\u628A\u7A33\u5B9A\u5185\u5BB9\u653E\u5728\u5BF9\u8BDD\u6700\u524D\u9762\u80FD\u63D0\u9AD8\u547D\u4E2D\u7387\u3002',\n 'cache.goodHint': '\u524D\u7F00\u7F13\u5B58\u628A\u91CD\u590D\u7684 prompt \u524D\u7F00\u6309\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF0C\u662F\u8FD9\u4EFD\u8D26\u5355\u4E0A\u6700\u5927\u7684\u7701\u94B1\u6760\u6746\u3002',\n 'peak.empty': '\u8FD8\u6CA1\u6709\u53EF\u6309\u65F6\u6BB5\u5F52\u7C7B\u7684\u7528\u91CF\u3002',\n 'peak.aria': '\u9AD8\u5CF0\u65F6\u6BB5 {peak}%\uFF0C\u95F2\u65F6 {offPeak}%',\n 'peak.peakLegend': '\u9AD8\u5CF0 {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.offPeakLegend': '\u95F2\u65F6 {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.shift': '\u82E5\u9AD8\u5CF0\u7528\u91CF\u90FD\u632A\u5230\u95F2\u65F6',\n 'peak.saving': '\u53EF\u7701 \u00A5{amount}',\n 'peak.schedule': '\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF08\u5468\u4E00\u81F3\u5468\u4E94\uFF09\uFF0C\u95F2\u65F6\u4EF7\u683C\u51CF\u534A\u3002',\n 'peak.activeHint': '\u628A\u6279\u91CF\u3001\u53EF\u5EF6\u540E\u7684\u4EFB\u52A1\u653E\u5230\u95F2\u65F6\u8DD1\uFF0C\u540C\u6837\u7684 token \u53EA\u8981\u4E00\u534A\u7684\u94B1\u3002',\n 'vision.empty': '{window}\u6CA1\u6709\u56FE\u7247\u8F93\u5165\u3002',\n 'vision.title': '\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF',\n 'vision.images': '\u56FE\u7247\u5F20\u6570',\n 'vision.tokens': '\u4F30\u7B97\u56FE\u7247 tokens',\n 'vision.cost': '\u4F30\u7B97\u8D39\u7528',\n 'vision.tokensShare': '\u5360\u8F93\u5165 {percent}%',\n 'vision.costShare': '\u5360\u8D39\u7528 {percent}%',\n 'vision.note': '\u56FE\u7247\u6309\u5B98\u65B9\u6362\u7B97\u89C4\u5219\u4F30\u7B97\uFF1A\u5148\u6309\u6BD4\u4F8B\u7F29\u653E\uFF08\u4E0A\u9650 ~800\u00D7800\u3001\u4E0B\u9650 384\u00D7384\uFF09\uFF0Ctoken \u4E0E\u7F29\u653E\u540E\u9762\u79EF\u6210\u6B63\u6BD4\u3001\u6BCF\u5F20\u4E0A\u9650 384\uFF1B\u56FE\u7247\u5F52\u5C5E\u5230\u968F\u540E\u4E00\u6B21\u6A21\u578B\u8C03\u7528\uFF0C\u6309\u8BE5\u8C03\u7528\u7684\u65F6\u6BB5\u4E0E\u6A21\u578B\u5355\u4EF7\u4F30\u7B97\uFF0C\u5B9E\u9645\u4EE5\u63A5\u53E3\u8FD4\u56DE\u7684\u7528\u91CF\u4E3A\u51C6\u3002',\n 'vision.sessionsTitle': '\u56FE\u7247\u6700\u591A\u7684\u4F1A\u8BDD',\n 'vision.sessionRow': '{images} \u5F20 \u00B7 \u00A5{cost}',\n 'vision.dailyTitle': '{window} \u00B7 \u6BCF\u5929\u56FE\u7247\u6570',\n 'vision.barAria': '{date} \u00B7 {images} \u5F20\u56FE\u7247 \u00B7 \u4F30\u7B97 {tokens} tokens',\n 'vision.barTitle': '{date} \u00B7 {images} \u5F20\u56FE\u7247 \u00B7 \u4F30\u7B97 {tokens} tokens \u00B7 \u00A5{cost}',\n 'sessions.empty': '\u8FD8\u6CA1\u6709\u4EA7\u751F\u8D39\u7528\u7684\u4F1A\u8BDD\u3002',\n 'sessions.detail': '{tokens} tokens \u00B7 {calls} \u00B7 {ago}',\n 'sessions.share': '\u5360 {percent}%',\n 'sessions.more': '\u5171 {count} \u4E2A\u4F1A\u8BDD\u6709\u7528\u91CF\uFF0C\u4E0A\u9762\u662F\u6700\u8D35\u7684 {shown} \u4E2A\u3002',\n 'sessions.fallbackTitle': '\u4F1A\u8BDD {id}',\n 'models.empty': '\u8FD8\u6CA1\u6709\u53EF\u5F52\u7C7B\u7684\u6A21\u578B\u7528\u91CF\u3002',\n 'models.shareAria': '{name} \u5360\u603B\u8D39\u7528 {percent}%',\n 'models.detail': '{tokens} tokens \u00B7 {calls} \u00B7 \u8F93\u5165 {input} / \u8F93\u51FA {output} / \u7F13\u5B58 {cache}',\n 'models.all': '\u5168\u90E8\u6A21\u578B',\n 'models.selected': '\u6A21\u578B \u00D7{count}',\n 'models.filterTitle': '\u7B5B\u9009\u56FE\u8868\u4E2D\u663E\u793A\u7684\u6A21\u578B',\n 'models.filterLabel': '\u7B5B\u9009\u56FE\u8868\u6A21\u578B',\n 'window.year': '\u8FD1 1 \u5E74',\n 'window.days': '\u8FD1 {days} \u5929',\n 'window.groupLabel': '\u7EDF\u8BA1\u5468\u671F',\n 'window.viewTitle': '\u67E5\u770B{window}\u7684\u7528\u91CF\u4E0E\u6392\u884C',\n 'window.yearButton': '1 \u5E74',\n 'window.daysButton': '{days} \u5929',\n 'metric.tokens': 'tokens',\n 'metric.usage': '\u7528\u91CF',\n 'metric.cost': '\u8D39\u7528',\n 'metric.calls': '\u8C03\u7528',\n 'metric.groupLabel': '\u56FE\u8868\u6307\u6807',\n 'metric.compareTitle': '\u6309{metric}\u6BD4\u8F83',\n 'chart.dailyTitle': '{window} \u00B7 \u9010\u5929{metric}',\n 'chart.hourlyTitle': '{window} \u00B7 {metric}\u6309\u5C0F\u65F6\u5206\u5E03\uFF080\u201323 \u70B9\uFF09',\n 'chart.peak': '\u5CF0\u503C {value}',\n 'chart.dimensionLabel': '\u5207\u6362\u56FE\u8868\u7EF4\u5EA6',\n 'chart.daily': '\u9010\u5929',\n 'chart.hourly': '\u9010\u5C0F\u65F6',\n 'chart.noWindowData': '{window}\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\uFF0C\u6362\u4E2A\u66F4\u957F\u5468\u671F\u770B\u770B\u3002',\n 'chart.heatmapTitle': '\u8FD1\u4E00\u5E74 \u00B7 \u6BCF\u65E5\u7528\u91CF\u70ED\u529B\u56FE',\n 'chart.empty': '\u8FD8\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002\u5728 DSH \u91CC\u8DD1\u4E00\u8F6E\u5BF9\u8BDD\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u9010\u5929 / \u9010\u5C0F\u65F6\u7EDF\u8BA1\u4E0E\u8D39\u7528\u4F30\u7B97\u3002',\n 'chart.hourPoint': '{hour} \u70B9',\n 'chart.tooltip': '{head} \u00B7 {tokens} tokens \u00B7 \u00A5{cost} \u00B7 {calls}',\n 'chart.less': '\u5C11',\n 'chart.more': '\u591A',\n 'chart.month1': '1\u6708',\n 'chart.month2': '2\u6708',\n 'chart.month3': '3\u6708',\n 'chart.month4': '4\u6708',\n 'chart.month5': '5\u6708',\n 'chart.month6': '6\u6708',\n 'chart.month7': '7\u6708',\n 'chart.month8': '8\u6708',\n 'chart.month9': '9\u6708',\n 'chart.month10': '10\u6708',\n 'chart.month11': '11\u6708',\n 'chart.month12': '12\u6708',\n 'number.tenThousand': '{value}\u4E07',\n 'number.hundredMillion': '{value}\u4EBF',\n 'fresh.none': '\u5C1A\u65E0\u6210\u529F\u8BB0\u5F55',\n 'fresh.justNow': '\u521A\u521A\u66F4\u65B0',\n 'fresh.minutes': '\u66F4\u65B0\u4E8E {count} \u5206\u949F\u524D',\n 'fresh.hours': '\u66F4\u65B0\u4E8E {count} \u5C0F\u65F6\u524D',\n 'fresh.days': '\u66F4\u65B0\u4E8E {count} \u5929\u524D',\n 'sync.first': '\u9996\u6B21\u540C\u6B65\u4E2D',\n 'sync.syncing': '\u540C\u6B65\u4E2D \u00B7 \u5F53\u524D\u6570\u636E{age}',\n 'sync.fresh': '\u5DF2\u540C\u6B65 \u00B7 {age}',\n 'sync.cached': '\u7F13\u5B58\u6570\u636E \u00B7 {age}',\n 'sync.fallback': '\u7F13\u5B58\u56DE\u9000 \u00B7 {age}',\n 'sync.error': '\u540C\u6B65\u5931\u8D25 \u00B7 \u6682\u65E0\u53EF\u663E\u793A\u7684\u7528\u91CF',\n 'status.neverSynced': '\u8FD8\u6CA1\u6709\u6210\u529F\u540C\u6B65\u8FC7\u7528\u91CF\u6570\u636E',\n 'status.usageTime': '\u7528\u91CF\u6570\u636E\u65F6\u95F4\uFF1A{time}',\n 'status.forceRefresh': '\u5F3A\u5236\u5237\u65B0\uFF08\u7ED5\u8FC7\u7F13\u5B58\uFF09',\n 'status.refreshing': '\u5237\u65B0\u4E2D',\n 'status.refresh': '\u5237\u65B0',\n 'error.balanceLoad': '\u4F59\u989D\u52A0\u8F7D\u5931\u8D25',\n 'error.usageLoad': '\u7528\u91CF\u52A0\u8F7D\u5931\u8D25',\n 'error.refreshCached': '\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C55\u793A\u7F13\u5B58\u6570\u636E\uFF08{error}\uFF09',\n 'error.balanceRefreshCached': '\u4F59\u989D\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C55\u793A\u7F13\u5B58\u4F59\u989D\uFF08{error}\uFF09',\n 'error.usageRefreshCached': '\u7528\u91CF\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C55\u793A\u7F13\u5B58\u7528\u91CF\uFF08{error}\uFF09',\n 'error.query': '\u67E5\u8BE2\u5931\u8D25',\n 'error.credentialsUnavailable': '\u51ED\u8BC1\u670D\u52A1\u4E0D\u53EF\u7528',\n 'error.credentialRead': '\u8BFB\u53D6 API Key \u5931\u8D25\uFF1A{detail}',\n 'error.noApiKey': '\u672A\u914D\u7F6E DEEPSEEK_API_KEY\uFF08\u53EF\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u4E2D\u586B\u5199\uFF09',\n 'error.balanceRequest': '\u8BF7\u6C42\u4F59\u989D\u63A5\u53E3\u5931\u8D25\uFF1A{detail}',\n 'error.balanceHttp': '\u4F59\u989D\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP {status}\uFF09',\n 'error.balanceParse': '\u89E3\u6790\u4F59\u989D\u54CD\u5E94\u5931\u8D25',\n 'error.persistenceUnavailable': '\u4F1A\u8BDD\u6301\u4E45\u5316\u670D\u52A1\u4E0D\u53EF\u7528',\n 'error.sessionList': '\u8BFB\u53D6\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF1A{detail}',\n 'error.sessionLogRead': '\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A{detail}',\n 'error.missingSessionId': '\u7F3A\u5C11\u4F1A\u8BDD id',\n 'alert.lowTitle': '\u4F59\u989D {balance} {currency} \u5DF2\u4F4E\u4E8E\u9884\u8B66\u7EBF {threshold}',\n 'alert.runway': '\uFF0C\u6309\u8FD1\u671F\u7528\u91CF\u4F30\u8BA1\u8FD8\u80FD\u6491 {days}',\n 'alert.topUp': '\u53BB\u5145\u503C',\n 'balance.title': '\u8D26\u6237\u4F59\u989D',\n 'balance.remaining': '\u5269\u4F59\u4F59\u989D',\n 'balance.breakdownTitle': '\u67E5\u770B\u5145\u503C\u4E0E\u8D60\u9001\u989D\u5EA6\u660E\u7EC6',\n 'balance.toppedUp': '\u5145\u503C\u989D\u5EA6 {amount}',\n 'balance.granted': '\u8D60\u9001\u989D\u5EA6 {amount}',\n 'balance.runway': '\u9884\u8BA1\u53EF\u7528',\n 'balance.runwayBreakdownTitle': '\u67E5\u770B\u9884\u8BA1\u53EF\u7528\u5929\u6570\u8BF4\u660E',\n 'balance.days': '{count} \u5929',\n 'balance.overYear': '> 365 \u5929',\n 'balance.noRunway': '\u8FD1 {days} \u5929\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u6CD5\u4F30\u7B97',\n 'balance.runwayTitle': '\u6309\u8FD1 {days} \u5929\u65E5\u5747 \u00A5{cost} \u4F30\u7B97\uFF08\u542B\u65E0\u7528\u91CF\u7684\u65E5\u5B50\uFF1B\u4ECA\u5929\u5C1A\u672A\u8FC7\u5B8C\uFF09',\n 'balance.empty': '\u8BFB\u4E0D\u5230\u4F59\u989D\u3002\u8BF7\u786E\u8BA4\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u91CC\u5DF2\u586B DEEPSEEK_API_KEY\uFF0C\u7136\u540E\u70B9\u53F3\u4E0A\u89D2\u5237\u65B0\u3002',\n 'sessionUsage.title': '\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\uFF08\u8D39\u7528\u4E3A\u4F30\u7B97\uFF09',\n 'sessionUsage.cost': '\u672C\u4F1A\u8BDD\u8D39\u7528',\n 'sessionUsage.tokens': '\u672C\u4F1A\u8BDD tokens',\n 'sessionUsage.calls': '\u672C\u4F1A\u8BDD\u8C03\u7528',\n 'sessionUsage.range': '\u6D3B\u8DC3\u65F6\u95F4 {range}',\n 'sessionUsage.empty': '\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u4EA7\u751F\u7528\u91CF\u3002\u53D1\u4E00\u6761\u6D88\u606F\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u8FD9\u4E2A\u4F1A\u8BDD\u4E13\u5C5E\u7684\u8D39\u7528\u4E0E\u6A21\u578B\u660E\u7EC6\u3002',\n 'overview.title': '\u6D88\u8017\u6982\u89C8\uFF08\u8D39\u7528\u4E3A\u4F30\u7B97\uFF09',\n 'overview.today': '\u4ECA\u65E5',\n 'overview.month': '\u672C\u6708',\n 'overview.total': '\u7D2F\u8BA1',\n 'overview.vsYesterday': '\u8F83\u6628\u65E5',\n 'overview.vsLastMonth': '\u8F83\u4E0A\u6708\u540C\u671F',\n 'overview.empty': '\u8FD8\u6CA1\u6709\u53EF\u7EDF\u8BA1\u7684\u6D88\u8017\u3002',\n 'usage.trendTitle': '\u7528\u91CF\u8D8B\u52BF\uFF08tokens\uFF09',\n 'usage.windowInput': '{window}\u8F93\u5165',\n 'usage.modelCalls': '\u6A21\u578B\u8C03\u7528',\n 'heatmap.title': '\u7528\u91CF\u70ED\u529B\u56FE',\n 'peak.title': '\u9AD8\u5CF0 / \u95F2\u65F6\u5206\u5E03',\n 'cache.title': '\u7F13\u5B58\u547D\u4E2D\u4E0E\u8282\u7701',\n 'models.title': '\u6A21\u578B\u6210\u672C\u6392\u884C \u00B7 {window}',\n 'sessions.title': '\u4F1A\u8BDD\u6210\u672C\u6392\u884C \u00B7 {window}',\n 'links.title': '\u5FEB\u6377\u5165\u53E3',\n 'links.usage': '\u67E5\u770B\u989D\u5EA6 / \u7528\u91CF',\n 'links.apiKey': '\u751F\u6210 API Key',\n 'links.status': '\u670D\u52A1\u72B6\u6001',\n 'settings.title': '\u8BBE\u7F6E',\n 'settings.widget': '\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3',\n 'settings.widgetHint': '\u989D\u5EA6\u9875\u4F1A\u81EA\u52A8\u9690\u85CF\u6D6E\u7A97\uFF1B\u5176\u4ED6\u9875\u9762\u662F\u5426\u5C55\u793A\u7531\u4E0B\u65B9\u9009\u62E9\u51B3\u5B9A\u3002',\n 'settings.widgetTabs': '\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762',\n 'settings.widgetTabsHint': '\u9875\u9762\u5217\u8868\u6765\u81EA DSH \u7684\u5B9E\u65F6 Tab \u6CE8\u518C\u8868\uFF1B\u5176\u4ED6\u63D2\u4EF6\u65B0\u589E\u7684 Tab \u4E5F\u4F1A\u81EA\u52A8\u51FA\u73B0\u5728\u8FD9\u91CC\u3002',\n 'settings.widgetTabsEmpty': '\u5F53\u524D\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u4F1A\u8BDD\u9875\u9762\u3002',\n 'settings.lowBalance': '\u4F59\u989D\u9884\u8B66\u7EBF',\n 'settings.lowBalanceOn': '\u4F59\u989D\u4F4E\u4E8E {amount} {currency} \u65F6\uFF0C\u8FD9\u91CC\u548C\u60AC\u6D6E\u7A97\u90FD\u4F1A\u8F6C\u4E3A\u8B66\u793A\u8272\u3002\u586B 0 \u5173\u95ED\u3002',\n 'settings.lowBalanceOff': '\u5DF2\u5173\u95ED\u4F59\u989D\u9884\u8B66\u3002\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u6570\u5F00\u542F\u3002',\n 'settings.monthlyBudget': '\u6708\u5EA6\u9884\u7B97',\n 'settings.monthlyBudgetOn': '\u6309\u5317\u4EAC\u65F6\u95F4\u81EA\u7136\u6708\u8DDF\u8E2A {amount} CNY \u9884\u7B97\uFF0C\u5E76\u5728\u6D88\u8017\u6982\u89C8\u9884\u6D4B\u6708\u5E95\u82B1\u8D39\u3002\u586B 0 \u5173\u95ED\u3002',\n 'settings.monthlyBudgetOff': '\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u91D1\u989D\uFF0C\u6D88\u8017\u6982\u89C8\u4F1A\u663E\u793A\u8FDB\u5EA6\u4E0E\u6708\u5E95\u9884\u6D4B\u3002',\n 'widget.updatedNow': '\u521A\u521A\u66F4\u65B0',\n 'widget.stale': '{minutes} \u5206\u949F\u524D\u7684\u6570\u636E\uFF0C\u70B9 \u21BB \u66F4\u65B0',\n 'widget.loading': '\u67E5\u8BE2\u4E2D\u2026',\n 'widget.lowTitle': '\u4F59\u989D\u4F4E\u4E8E\u9884\u8B66\u7EBF {amount}',\n 'widget.todayCostTitle': '\u4ECA\u65E5\u6D88\u8017\uFF08\u4F30\u7B97\uFF09\u00B7 {age}',\n 'widget.todayCost': '\u4ECA\u65E5\u6D88\u8017',\n 'widget.sessionCostTitle': '\u5F53\u524D\u4F1A\u8BDD\u7D2F\u8BA1\u6D88\u8017\uFF08\u4F30\u7B97\uFF09',\n 'widget.sessionCost': '\u5F53\u524D\u4F1A\u8BDD',\n 'widget.noBalance': '\u6682\u65E0\u4F59\u989D\u6570\u636E',\n 'widget.dragCard': '\u62D6\u52A8\u5230\u56DB\u4E2A\u89D2\u843D',\n 'widget.dragGrip': '\u62D6\u52A8\u4EE5\u79FB\u52A8',\n 'widget.refresh': '\u5237\u65B0\u4F59\u989D\u4E0E\u4ECA\u65E5\u7528\u91CF',\n 'widget.expand': '\u5C55\u5F00',\n 'widget.collapse': '\u6536\u8D77',\n 'widget.expandAria': '\u5C55\u5F00\u989D\u5EA6\u7A97\u53E3',\n 'widget.collapseAria': '\u6536\u8D77\u989D\u5EA6\u7A97\u53E3',\n 'messageCost.label': '\u672C\u8F6E\u8D39\u7528',\n 'messageCost.tooltip': '\u7531 dsh-usage-dashboard \u63D0\u4F9B \u00B7 \u672C\u8F6E\u5BF9\u8BDD\u8D39\u7528\u4E3A\u4F30\u7B97\u503C',\n 'messageCost.aria': '{amount}\uFF0C\u672C\u8F6E\u5BF9\u8BDD\u4F30\u7B97\u8D39\u7528\uFF0C\u7531 dsh-usage-dashboard \u63D0\u4F9B',\n 'boundary.title': '\u989D\u5EA6\u9762\u677F\u51FA\u9519\u4E86',\n 'boundary.body': '\u6E32\u67D3\u65F6\u629B\u4E86\u5F02\u5E38\uFF0C\u901A\u5E38\u662F\u6D4F\u89C8\u5668\u91CC\u5B58\u7740\u65E7\u7248\u672C\u7684\u7F13\u5B58\u6570\u636E\u3002\u6E05\u6389\u672C\u63D2\u4EF6\u7684\u672C\u5730\u6570\u636E\u518D\u91CD\u65B0\u52A0\u8F7D\u5C31\u80FD\u6062\u590D\u3002',\n 'boundary.reset': '\u6E05\u9664\u672C\u5730\u6570\u636E\u5E76\u91CD\u65B0\u52A0\u8F7D',\n} as const\n\nexport type LocaleKey = keyof typeof zh\n\nexport const en: Record<LocaleKey, string> = {\n 'meta.locale': 'en',\n 'nav.quota': 'Usage',\n 'widget.title': 'DeepSeek Usage',\n 'common.unknownModel': 'Unknown model',\n 'common.calls': '{count} calls',\n 'common.callsShort': '{count} calls',\n 'common.sessions': '{count} sessions',\n 'common.records': '{count} records',\n 'common.rows': '{count} rows',\n 'common.input': 'Input',\n 'common.output': 'Output',\n 'common.cacheHit': 'Cache hit',\n 'common.status': 'Status',\n 'common.available': 'Available',\n 'common.unavailable': 'Unavailable',\n 'common.justNow': 'Just now',\n 'common.minutesAgo': '{count}m ago',\n 'common.hoursAgo': '{count}h ago',\n 'common.daysAgo': '{count}d ago',\n 'common.listSeparator': ', ',\n 'common.period': '.',\n 'delta.noBaselineTitle': 'No usage in {label}, so there is no baseline',\n 'delta.noBaseline': 'No baseline',\n 'delta.flat': 'No change',\n 'period.volume': '{tokens} tokens \u00B7 {calls}',\n 'budget.unset': 'Set a monthly budget to see whether current spending is on track.',\n 'budget.configure': 'Set budget',\n 'budget.over': 'Over budget by \u00A5{amount}',\n 'budget.forecastOver': 'At this pace, \u00A5{amount} over by month-end',\n 'budget.forecast': 'At this pace, \u00A5{amount} by month-end',\n 'budget.title': 'Monthly budget',\n 'budget.progressLabel': 'Monthly budget usage',\n 'budget.used': '{percent}% used. {forecast}',\n 'budget.remaining': '\u00A5{amount} remaining',\n 'budget.exceeded': '\u00A5{amount} over',\n 'pricing.title': 'Pricing details',\n 'pricing.currentPeak': 'Peak now',\n 'pricing.currentOffPeak': 'Off-peak now',\n 'pricing.model': 'Model',\n 'pricing.period': 'Period',\n 'pricing.inputCacheHit': 'Input \u00B7 cache hit',\n 'pricing.inputCacheMiss': 'Input \u00B7 cache miss',\n 'pricing.output': 'Output',\n 'pricing.peak': 'Peak',\n 'pricing.offPeak': 'Off-peak',\n 'pricing.unit': 'Unit: {currency} / million tokens. ',\n 'pricing.splitNote': 'Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. Each record is estimated at the official rates in force when it was written. ',\n 'pricing.visionNote': 'Images are converted to tokens by size and charged together with text. ',\n 'pricing.modelNote': 'deepseek-v4-flash and deepseek-v4-flash-vision-exp are legacy names, now served by deepseek-flash at Flash rates; from 2026-09-14 12:00 deepseek-v4-pro requests are routed to V4.1-Flash and billed at Flash rates. ',\n 'pricing.unknown': 'Unknown models use deepseek-v4-pro rates.',\n 'coverage.noRange': 'No valid usage timestamps',\n 'coverage.title': 'Coverage',\n 'coverage.gaps': 'Gaps found',\n 'coverage.complete': 'Local logs complete',\n 'coverage.brief': 'Scanned {scanned} / {listed} sessions \u00B7 {records} usage records',\n 'coverage.scanned': 'Scanned',\n 'coverage.failed': 'Read failures',\n 'coverage.skipped': 'Skipped records',\n 'coverage.time': 'Record range: {range}',\n 'coverage.scope': 'Only DSH session logs on this device are included. Other devices, direct DeepSeek Platform calls, and deleted local logs are excluded.',\n 'coverage.warning': 'Some logs could not be read or contained invalid usage records, so totals may be lower than actual spend. Refresh to retry and use the DeepSeek Platform bill as the source of truth.',\n 'export.title': 'Export all local DSH session-log data (independent of the selected range)',\n 'export.done': 'Exported',\n 'export.action': 'Export',\n 'export.menuLabel': 'Choose export format',\n 'export.dailyCsv': 'Daily CSV',\n 'export.modelCsv': 'Model CSV',\n 'export.fullJson': 'Full JSON',\n 'export.jsonHint': 'Charts and rankings included',\n 'cache.empty': 'No prompt tokens are available for cache analysis yet.',\n 'cache.rate': 'Cache hit rate',\n 'cache.saved': 'Estimated savings',\n 'cache.allMiss': 'Cost with no hits',\n 'cache.aria': '{hit}% cache hits, {miss}% misses',\n 'cache.hitLegend': 'Cache hits {tokens}',\n 'cache.missLegend': 'Misses {tokens} (charged at miss rate)',\n 'cache.lowHint': 'Low hit rate: frequent changes to the system prompt or tool definitions invalidate prefix caching. Put stable content first to improve reuse.',\n 'cache.goodHint': 'Prefix caching charges repeated prompt prefixes at the hit rate, making it the largest saving lever on this bill.',\n 'peak.empty': 'No usage can be classified by pricing period yet.',\n 'peak.aria': '{peak}% peak, {offPeak}% off-peak',\n 'peak.peakLegend': 'Peak {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.offPeakLegend': 'Off-peak {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.shift': 'If all peak usage moved off-peak',\n 'peak.saving': 'Save \u00A5{amount}',\n 'peak.schedule': 'Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. ',\n 'peak.activeHint': 'Run batch or delay-tolerant work off-peak to pay half as much for the same tokens.',\n 'vision.empty': 'No image input in {window}.',\n 'vision.title': 'Image / multimodal usage',\n 'vision.images': 'Images',\n 'vision.tokens': 'Estimated image tokens',\n 'vision.cost': 'Estimated cost',\n 'vision.tokensShare': '{percent}% of input',\n 'vision.costShare': '{percent}% of cost',\n 'vision.note': 'Images are estimated with the official rule: resized keeping aspect ratio (cap ~800\u00D7800, floor 384\u00D7384), tokens proportional to the resized area with a 384-token cap per image. Each image is attributed to the next model call and priced at that call\\'s period and model rate; the provider usage numbers remain the source of truth.',\n 'vision.sessionsTitle': 'Sessions with the most images',\n 'vision.sessionRow': '{images} images \u00B7 \u00A5{cost}',\n 'vision.dailyTitle': '{window} \u00B7 images per day',\n 'vision.barAria': '{date} \u00B7 {images} images \u00B7 est. {tokens} tokens',\n 'vision.barTitle': '{date} \u00B7 {images} images \u00B7 est. {tokens} tokens \u00B7 \u00A5{cost}',\n 'sessions.empty': 'No sessions have incurred cost yet.',\n 'sessions.detail': '{tokens} tokens \u00B7 {calls} \u00B7 {ago}',\n 'sessions.share': '{percent}% of total',\n 'sessions.more': '{count} sessions have usage; the {shown} most expensive are shown above.',\n 'sessions.fallbackTitle': 'Session {id}',\n 'models.empty': 'No model usage can be classified yet.',\n 'models.shareAria': '{name} accounts for {percent}% of total cost',\n 'models.detail': '{tokens} tokens \u00B7 {calls} \u00B7 input {input} / output {output} / cache {cache}',\n 'models.all': 'All models',\n 'models.selected': '{count} models',\n 'models.filterTitle': 'Filter models shown in the chart',\n 'models.filterLabel': 'Filter chart models',\n 'window.year': 'Last year',\n 'window.days': 'Last {days} days',\n 'window.groupLabel': 'Date range',\n 'window.viewTitle': 'View usage and rankings for {window}',\n 'window.yearButton': '1 year',\n 'window.daysButton': '{days} days',\n 'metric.tokens': 'tokens',\n 'metric.usage': 'usage',\n 'metric.cost': 'cost',\n 'metric.calls': 'calls',\n 'metric.groupLabel': 'Chart metric',\n 'metric.compareTitle': 'Compare by {metric}',\n 'chart.dailyTitle': '{window} \u00B7 daily {metric}',\n 'chart.hourlyTitle': '{window} \u00B7 {metric} by hour (00\u201323)',\n 'chart.peak': 'Peak {value}',\n 'chart.dimensionLabel': 'Chart interval',\n 'chart.daily': 'Daily',\n 'chart.hourly': 'Hourly',\n 'chart.noWindowData': 'No usage in {window}. Try a longer range.',\n 'chart.heatmapTitle': 'Last year \u00B7 daily usage heatmap',\n 'chart.empty': 'No usage yet. Run a conversation in DSH to see daily and hourly totals with cost estimates.',\n 'chart.hourPoint': '{hour}:00',\n 'chart.tooltip': '{head} \u00B7 {tokens} tokens \u00B7 \u00A5{cost} \u00B7 {calls}',\n 'chart.less': 'Less',\n 'chart.more': 'More',\n 'chart.month1': 'Jan',\n 'chart.month2': 'Feb',\n 'chart.month3': 'Mar',\n 'chart.month4': 'Apr',\n 'chart.month5': 'May',\n 'chart.month6': 'Jun',\n 'chart.month7': 'Jul',\n 'chart.month8': 'Aug',\n 'chart.month9': 'Sep',\n 'chart.month10': 'Oct',\n 'chart.month11': 'Nov',\n 'chart.month12': 'Dec',\n 'number.tenThousand': '{value}0K',\n 'number.hundredMillion': '{value}00M',\n 'fresh.none': 'No successful sync yet',\n 'fresh.justNow': 'Updated just now',\n 'fresh.minutes': 'Updated {count}m ago',\n 'fresh.hours': 'Updated {count}h ago',\n 'fresh.days': 'Updated {count}d ago',\n 'sync.first': 'Syncing for the first time',\n 'sync.syncing': 'Syncing \u00B7 current data {age}',\n 'sync.fresh': 'Synced \u00B7 {age}',\n 'sync.cached': 'Cached data \u00B7 {age}',\n 'sync.fallback': 'Using cache \u00B7 {age}',\n 'sync.error': 'Sync failed \u00B7 no usage to display',\n 'status.neverSynced': 'Usage has not synced successfully yet',\n 'status.usageTime': 'Usage data timestamp: {time}',\n 'status.forceRefresh': 'Force refresh (bypass cache)',\n 'status.refreshing': 'Refreshing',\n 'status.refresh': 'Refresh',\n 'error.balanceLoad': 'Balance failed to load',\n 'error.usageLoad': 'Usage failed to load',\n 'error.refreshCached': 'Refresh failed; showing cached data ({error})',\n 'error.balanceRefreshCached': 'Balance refresh failed; showing cached balance ({error})',\n 'error.usageRefreshCached': 'Usage refresh failed; showing cached usage ({error})',\n 'error.query': 'Request failed',\n 'error.credentialsUnavailable': 'Credential service is unavailable',\n 'error.credentialRead': 'Could not read the API key: {detail}',\n 'error.noApiKey': 'DEEPSEEK_API_KEY is not configured (add it under Settings \u2192 Models)',\n 'error.balanceRequest': 'Balance request failed: {detail}',\n 'error.balanceHttp': 'Balance endpoint returned HTTP {status}',\n 'error.balanceParse': 'Could not parse the balance response',\n 'error.persistenceUnavailable': 'Session persistence is unavailable',\n 'error.sessionList': 'Could not read the session list: {detail}',\n 'error.sessionLogRead': 'Could not read the session log: {detail}',\n 'error.missingSessionId': 'Missing session id',\n 'alert.lowTitle': 'Balance {balance} {currency} is below the {threshold} warning threshold',\n 'alert.runway': '; about {days} left at recent usage',\n 'alert.topUp': 'Top up',\n 'balance.title': 'Account balance',\n 'balance.remaining': 'Remaining balance',\n 'balance.breakdownTitle': 'View topped-up and granted balance details',\n 'balance.toppedUp': 'Topped up {amount}',\n 'balance.granted': 'Granted {amount}',\n 'balance.runway': 'Estimated runway',\n 'balance.runwayBreakdownTitle': 'View estimated runway details',\n 'balance.days': '{count} days',\n 'balance.overYear': '> 365 days',\n 'balance.noRunway': 'No usage in the last {days} days, so runway cannot be estimated',\n 'balance.runwayTitle': 'Based on \u00A5{cost} average daily spend over the last {days} days (including idle days; today is incomplete)',\n 'balance.empty': 'Balance is unavailable. Add DEEPSEEK_API_KEY under Settings \u2192 Models, then refresh.',\n 'sessionUsage.title': 'Current session spend (estimated)',\n 'sessionUsage.cost': 'This session',\n 'sessionUsage.tokens': 'Session tokens',\n 'sessionUsage.calls': 'Session calls',\n 'sessionUsage.range': 'Active {range}',\n 'sessionUsage.empty': 'No usage in this session yet. Send a message and this session\u2019s own cost and model breakdown will show up here.',\n 'overview.title': 'Spend overview (estimated)',\n 'overview.today': 'Today',\n 'overview.month': 'This month',\n 'overview.total': 'All time',\n 'overview.vsYesterday': 'vs yesterday',\n 'overview.vsLastMonth': 'vs same period last month',\n 'overview.empty': 'No spend to summarize yet.',\n 'usage.trendTitle': 'Usage trend (tokens)',\n 'usage.windowInput': '{window} input',\n 'usage.modelCalls': 'Model calls',\n 'heatmap.title': 'Usage heatmap',\n 'peak.title': 'Peak / off-peak distribution',\n 'cache.title': 'Cache hits and savings',\n 'models.title': 'Model cost ranking \u00B7 {window}',\n 'sessions.title': 'Session cost ranking \u00B7 {window}',\n 'links.title': 'Quick Links',\n 'links.usage': 'View balance / usage',\n 'links.apiKey': 'Create API key',\n 'links.status': 'Service status',\n 'settings.title': 'Settings',\n 'settings.widget': 'Show the floating balance widget on other views',\n 'settings.widgetHint': 'The widget always hides on Usage; choose its other views below.',\n 'settings.widgetTabs': 'Show widget on',\n 'settings.widgetTabsHint': 'This live list comes from DSH\u2019s tab registry, including tabs added by other plugins.',\n 'settings.widgetTabsEmpty': 'There are no configurable conversation views right now.',\n 'settings.lowBalance': 'Balance warning',\n 'settings.lowBalanceOn': 'This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.',\n 'settings.lowBalanceOff': 'Balance warnings are off. Enter an amount above 0 to enable them.',\n 'settings.monthlyBudget': 'Monthly budget',\n 'settings.monthlyBudgetOn': 'Track a {amount} CNY budget by Beijing calendar month and forecast month-end spend. Enter 0 to disable.',\n 'settings.monthlyBudgetOff': 'Enter an amount above 0 to show progress and a month-end forecast.',\n 'widget.updatedNow': 'Updated just now',\n 'widget.stale': 'Data from {minutes}m ago; press \u21BB to update',\n 'widget.loading': 'Loading\u2026',\n 'widget.lowTitle': 'Balance below warning threshold {amount}',\n 'widget.todayCostTitle': 'Today\u2019s spend (estimated) \u00B7 {age}',\n 'widget.todayCost': 'Today\u2019s spend',\n 'widget.sessionCostTitle': 'Estimated spend for the current session',\n 'widget.sessionCost': 'Current session',\n 'widget.noBalance': 'No balance data',\n 'widget.dragCard': 'Drag to any corner',\n 'widget.dragGrip': 'Drag to move',\n 'widget.refresh': 'Refresh balance and today\u2019s usage',\n 'widget.expand': 'Expand',\n 'widget.collapse': 'Collapse',\n 'widget.expandAria': 'Expand balance widget',\n 'widget.collapseAria': 'Collapse balance widget',\n 'messageCost.label': 'Turn cost',\n 'messageCost.tooltip': 'Provided by dsh-usage-dashboard \u00B7 estimated cost for this turn',\n 'messageCost.aria': '{amount}, estimated cost for this turn, provided by dsh-usage-dashboard',\n 'boundary.title': 'The usage dashboard crashed',\n 'boundary.body': 'Rendering failed, usually because this browser still has cached data from an older version. Clear this plugin\u2019s local data and reload to recover.',\n 'boundary.reset': 'Clear local data and reload',\n}\n", "/** Shared lookups into the DSH host shell DOM. Both the floating widget\n * (keeping its drag bounds clear of the header/composer) and the dashboard\n * (keeping its scroll content clear of the fixed composer on narrow\n * viewports) need to find the same host elements \u2014 sharing the lookup here\n * keeps the two readings of \"where is the composer\" from drifting apart.\n */\n\n/** The shell's main column: the region between the sidebar and the details\n * panel. Found through the stable `conversation` slot anchor rather than the\n * hashed layout class names. */\nexport function getMainColumn(frame: HTMLElement): HTMLElement | null {\n const anchor = frame.querySelector('[data-slot=\"conversation\"]')\n const column = anchor?.parentElement ?? null\n return column === frame ? null : column\n}\n\n/**\n * The laid-out element behind a slot. Slot wrappers are 0\u00D70, and which\n * neighbour carries the real box differs per slot: the session header\n * renders inside its wrapper (`'child'`), while the composer's wrapper sits\n * inside the seat that reserves the space (`'ancestor'`). Picking the wrong\n * side silently yields a box in the wrong place, so the caller says which one\n * it means.\n */\nfunction slotElement(frame: HTMLElement, slot: string, side: 'child' | 'ancestor'): HTMLElement | null {\n const anchor = frame.querySelector(`[data-slot=\"${slot}\"]`)\n if (anchor === null) return null\n if (side === 'child') {\n const el = anchor.firstElementChild as HTMLElement | null\n return el !== null && el.getBoundingClientRect().height > 0 ? el : null\n }\n let el: HTMLElement | null = anchor.parentElement\n for (let depth = 0; depth < 4 && el !== null && el !== frame; depth++) {\n if (el.getBoundingClientRect().height > 0) return el\n el = el.parentElement\n }\n return null\n}\n\n/** The laid-out box behind a slot (see `slotElement`), or null if the slot is\n * not currently present or not yet laid out. */\nexport function slotBox(frame: HTMLElement, slot: string, side: 'child' | 'ancestor'): DOMRect | null {\n return slotElement(frame, slot, side)?.getBoundingClientRect() ?? null\n}\n\n/** The DSH shell frame \u2014 the element that hosts the sidebar, conversation\n * column and fixed composer \u2014 found by walking up from any node inside our\n * own plugin root via the stable `[data-shell-overlay]` anchor. */\nexport function getShellFrame(node: HTMLElement | null): HTMLElement | null {\n const overlay = node !== null && typeof node.closest === 'function' ? node.closest('[data-shell-overlay]') : null\n return (overlay?.parentElement as HTMLElement | null) ?? null\n}\n\n/** The composer's laid-out element (the seat that reserves its space, not the\n * 0\u00D70 slot wrapper), or null if the slot is not present in the current DOM\n * (host markup changed, or mid-navigation). Callers that only need the box\n * can call `.getBoundingClientRect()`; callers that want to observe size\n * changes (e.g. `ResizeObserver`) need the element itself.\n *\n * `frame` may be null: the floating widget lives inside the shell overlay\n * subtree, but the dashboard's plugin root is mounted in the conversation\n * pane and cannot reach the shell frame that way. A frame-less call falls\n * back to a document-wide lookup \u2014 the composer is unique in the active\n * shell, so this cannot hit an ambiguous element. */\nexport function getComposerElement(frame: HTMLElement | null): HTMLElement | null {\n if (frame === null) {\n return slotElement(document.documentElement, 'conversation.composer.dock', 'ancestor')\n }\n return slotElement(frame, 'conversation.composer.dock', 'ancestor')\n}\n\n/**\n * The composer's *input card* \u2014 the rounded panel that visually IS the input\n * box (textarea + send row, ~780px max in DSH). The `.dock` seat found by\n * {@link getComposerElement} spans the whole pane, so its width is the wrong\n * answer for any width-parity goal: the visible input card is narrower, and\n * it is the column the dashboard cards must align with. The card is located\n * from the textarea's ancestor chain \u2014 the first ancestor with a visible\n * background and a non-zero border radius, the shape DSH gives the input\n * panel. Returns null when the host markup stops matching; callers then fall\n * back to {@link getComposerElement}. */\nexport function getComposerInputElement(frame: HTMLElement | null): HTMLElement | null {\n const root = frame ?? document.documentElement\n const textarea = root.querySelector(\n '[data-slot=\"conversation.composer\"] textarea, [data-slot=\"conversation.composer.bar\"] textarea, textarea',\n )\n if (textarea === null) return null\n let el = textarea.parentElement\n for (let depth = 0; depth < 6 && el !== null; depth++) {\n const cs = getComputedStyle(el)\n if (cs.borderRadius !== '0px' && cs.backgroundColor !== 'rgba(0, 0, 0, 0)') return el\n el = el.parentElement\n }\n return null\n}\n\n/** Resolve the active `conversation.view` id from the host's semantic tablist.\n * The DOM intentionally carries no view id, but it renders buttons in the\n * exact order of the slot ledger. Indexing into that same live ledger avoids\n * translated-label matching and remains compatible with third-party tabs. */\nexport function getActiveConversationViewId(\n frame: HTMLElement | null,\n viewIds: string[],\n): string | null {\n if (viewIds.length === 0) return null\n const header = frame === null ? null : slotElement(frame, 'conversation.session.header', 'child')\n const tablist = header?.querySelector('[role=\"tablist\"]') ?? null\n if (tablist === null) return viewIds[0] ?? null\n const tabs = [...tablist.querySelectorAll('[role=\"tab\"]')]\n const activeIndex = tabs.findIndex(tab => tab.getAttribute('aria-selected') === 'true')\n return activeIndex < 0 ? viewIds[0] ?? null : viewIds[activeIndex] ?? null\n}\n", "import type { UsageData } from '../contract.ts'\n\nconst BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000\n\n/** YYYYMMDD in DeepSeek's billing timezone. */\nexport function exportDateStamp(nowMs = Date.now()): string {\n const date = new Date(nowMs + BEIJING_OFFSET_MS)\n return [\n date.getUTCFullYear(),\n String(date.getUTCMonth() + 1).padStart(2, '0'),\n String(date.getUTCDate()).padStart(2, '0'),\n ].join('')\n}\n\n/** RFC 4180 cell escaping plus spreadsheet-formula neutralisation for labels. */\nexport function csvCell(value: string | number): string {\n let text = String(value)\n if (typeof value === 'string' && /^[=+\\-@]/.test(text)) text = `'${text}`\n return /[\",\\r\\n]/.test(text) ? `\"${text.replaceAll('\"', '\"\"')}\"` : text\n}\n\nconst csv = (rows: Array<Array<string | number>>): string =>\n `\\uFEFF${rows.map(row => row.map(csvCell).join(',')).join('\\r\\n')}\\r\\n`\n\nexport function dailyUsageCsv(usage: UsageData): string {\n return csv([\n ['date', 'input_tokens', 'output_tokens', 'cache_hit_tokens', 'total_tokens', 'calls', 'estimated_cost_cny'],\n ...usage.daily.map(day => [day.date, day.input, day.output, day.cache, day.total, day.calls, day.cost]),\n ])\n}\n\nexport function modelUsageCsv(usage: UsageData): string {\n return csv([\n ['provider', 'model', 'input_tokens', 'output_tokens', 'cache_hit_tokens', 'total_tokens', 'calls', 'estimated_cost_cny'],\n ...usage.models.map(model => [\n model.provider, model.model, model.input, model.output, model.cache, model.total, model.calls, model.cost,\n ]),\n ])\n}\n\nexport function fullUsageJson(usage: UsageData, nowMs = Date.now()): string {\n return `${JSON.stringify({\n schemaVersion: 1,\n exportedAt: new Date(nowMs).toISOString(),\n scope: 'local-dsh-session-logs',\n data: usage,\n }, null, 2)}\\n`\n}\n\nexport function downloadText(filename: string, text: string, mediaType: string): void {\n const url = URL.createObjectURL(new Blob([text], { type: mediaType }))\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = filename\n anchor.hidden = true\n document.body.appendChild(anchor)\n anchor.click()\n anchor.remove()\n window.setTimeout(() => URL.revokeObjectURL(url), 0)\n}\n", "import { fallbackT, type Translate } from './i18n.tsx'\n\nexport type SyncState = 'syncing' | 'fresh' | 'cached' | 'fallback' | 'error'\n\n/** Short, stable freshness copy for the dashboard status row. */\nexport function updatedText(updatedAt: number | null, nowMs = Date.now(), t: Translate = fallbackT): string {\n if (updatedAt === null || !Number.isFinite(updatedAt)) return t('fresh.none')\n const elapsed = Math.max(0, nowMs - updatedAt)\n if (elapsed < 60_000) return t('fresh.justNow')\n const minutes = Math.floor(elapsed / 60_000)\n if (minutes < 60) return t('fresh.minutes', { count: minutes })\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return t('fresh.hours', { count: hours })\n return t('fresh.days', { count: Math.floor(hours / 24) })\n}\n\nexport function syncStatusText(state: SyncState, updatedAt: number | null, nowMs = Date.now(), t: Translate = fallbackT): string {\n const age = updatedText(updatedAt, nowMs, t)\n if (state === 'syncing') return updatedAt === null ? t('sync.first') : t('sync.syncing', { age })\n if (state === 'fresh') return t('sync.fresh', { age })\n if (state === 'cached') return t('sync.cached', { age })\n if (state === 'fallback') return t('sync.fallback', { age })\n return t('sync.error')\n}\n", "import { fallbackT, type Translate } from './i18n.tsx'\n\n/** Value axis shared by daily, hourly, and per-model usage charts. */\nexport const CHART_METRICS = ['tokens', 'cost', 'calls'] as const\nexport type ChartMetric = (typeof CHART_METRICS)[number]\n\nexport interface ChartMetricPoint {\n total: number\n cost: number\n calls: number\n}\n\nexport const isChartMetric = (value: unknown): value is ChartMetric =>\n typeof value === 'string' && CHART_METRICS.some(metric => metric === value)\n\nexport function chartMetricValue(metric: ChartMetric, point: ChartMetricPoint): number {\n if (metric === 'cost') return point.cost\n if (metric === 'calls') return point.calls\n return point.total\n}\n\nexport function chartMetricName(metric: ChartMetric, t: Translate = fallbackT): string {\n if (metric === 'cost') return t('metric.cost')\n if (metric === 'calls') return t('metric.calls')\n return t('metric.usage')\n}\n", "/**\n * localStorage-backed user preferences for the plugin.\n *\n * Everything the user deliberately sets (widget visibility, the corner it was\n * dragged to, whether it is collapsed) belongs here so a page refresh does not\n * throw the choice away. Keys share one prefix and carry a version suffix so\n * the shape can evolve without reading back stale values.\n *\n * Every read is validated by the caller's guard: a corrupted or hand-edited\n * entry falls back instead of poisoning the UI.\n */\n\nconst PREFIX = 'dsh-usage-dashboard:pref:'\nconst VERSION = 1\n\nconst keyOf = (key: string): string => `${PREFIX}${key}:v${VERSION}`\n\nexport function loadPref<T>(key: string, isValid: (value: unknown) => boolean, fallback: T): T {\n if (typeof localStorage === 'undefined') return fallback\n try {\n const raw = localStorage.getItem(keyOf(key))\n if (raw === null) return fallback\n const parsed = JSON.parse(raw) as unknown\n return isValid(parsed) ? (parsed as T) : fallback\n } catch {\n return fallback\n }\n}\n\nexport function savePref(key: string, value: unknown): void {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(keyOf(key), JSON.stringify(value))\n } catch {\n // Quota / private-mode failures must never break the UI.\n }\n}\n\nexport const isBoolean = (value: unknown): boolean => typeof value === 'boolean'\n", "/**\n * Package-local stores for settings both surfaces read: the dashboard writes\n * them, the floating widget reacts. Every value is persisted (see ./prefs.ts),\n * so a choice survives a page refresh.\n */\nimport { USAGE_WINDOW_DAYS, type UsageWindowDays } from '../contract.ts'\nimport { isChartMetric, type ChartMetric } from './metric.ts'\nimport { isBoolean, loadPref, savePref } from './prefs.ts'\n\nexport interface Store<T> {\n get(): T\n set(value: T): void\n subscribe(fn: () => void): () => void\n}\n\nfunction createStore<T>(key: string, isValid: (value: unknown) => boolean, fallback: T): Store<T> {\n let current = loadPref<T>(key, isValid, fallback)\n const listeners = new Set<() => void>()\n return {\n get: () => current,\n set: (value: T) => {\n current = value\n savePref(key, value)\n for (const listener of [...listeners]) listener()\n },\n subscribe: (fn: () => void) => {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n }\n}\n\nconst isThreshold = (value: unknown): boolean =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0\n\n/** Whether the floating balance widget is shown. */\nexport const widgetVisibleStore = createStore<boolean>('widget.visible', isBoolean, true)\n\nconst isWidgetTabIds = (value: unknown): boolean => value === null\n || (Array.isArray(value) && value.every(id => typeof id === 'string' && id !== '')\n && new Set(value).size === value.length)\n\n/** Conversation view ids on which the widget is allowed to appear. `null`\n * means \"all available views\" and preserves the pre-setting default; once a\n * user customizes the list, newly installed plugin tabs arrive unchecked. */\nexport const widgetTabIdsStore = createStore<string[] | null>('widget.tabIds', isWidgetTabIds, null)\n\n/** Balance (in the account's own currency) below which both surfaces warn.\n * 0 turns the warning off. */\nexport const lowBalanceStore = createStore<number>('alert.lowBalance', isThreshold, 10)\n\n/** Monthly estimated-cost budget in CNY. 0 leaves budget tracking disabled. */\nexport const monthlyBudgetStore = createStore<number>('budget.monthly', isThreshold, 0)\n\nconst isUsageWindowDays = (value: unknown): value is UsageWindowDays =>\n typeof value === 'number' && USAGE_WINDOW_DAYS.some(days => days === value)\n\n/** Shared time range for the usage chart and both cost rankings. */\nexport const usageWindowStore = createStore<UsageWindowDays>('usage.windowDays', isUsageWindowDays, 30)\n\n/** Whether charts compare token volume, estimated CNY cost, or call count. */\nexport const chartMetricStore = createStore<ChartMetric>('usage.chartMetric', isChartMetric, 'tokens')\n\n/** Ephemeral presence of the full dashboard. The widget uses it to step out of\n * the way without changing the user's persisted visibility preference. */\nlet quotaViewActive = false\nconst quotaViewListeners = new Set<() => void>()\nexport const quotaViewActiveStore: Store<boolean> = {\n get: () => quotaViewActive,\n set: (value) => {\n if (value === quotaViewActive) return\n quotaViewActive = value\n for (const listener of [...quotaViewListeners]) listener()\n },\n subscribe: (fn) => {\n quotaViewListeners.add(fn)\n return () => {\n quotaViewListeners.delete(fn)\n }\n },\n}\n", "/** Estimated spend contributed beside each completed chat turn. */\nimport { useEffect, useState, type ReactElement } from 'react'\nimport type { SessionUsageData, TurnUsage } from '../contract.ts'\nimport { fetchSessionUsage, getCachedSessionUsage, subscribeSessionUsage } from './api.ts'\nimport { useI18n } from './i18n.tsx'\n\nexport interface MessageCostProps {\n sessionId: string\n messageId: string\n}\n\n/** Small costs need more than the dashboard's two-decimal money formatter or\n * almost every individual turn would misleadingly read as \u00A50.00. */\nexport const fmtTurnCost = (cost: number): string => cost.toLocaleString(undefined, {\n minimumFractionDigits: cost > 0 && cost < 0.01 ? 4 : 2,\n maximumFractionDigits: cost > 0 && cost < 0.01 ? 6 : 4,\n})\n\nconst cachedData = (sessionId: string): SessionUsageData | null => {\n const response = getCachedSessionUsage(sessionId)\n return response?.ok === true ? response.data ?? null : null\n}\n\nexport function MessageCost({ sessionId, messageId }: MessageCostProps): ReactElement | null {\n const { t } = useI18n()\n const [data, setData] = useState<SessionUsageData | null>(() => cachedData(sessionId))\n\n useEffect(() => {\n setData(cachedData(sessionId))\n const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData(sessionId)))\n const hit = cachedData(sessionId)?.turns.some(turn => turn.messageId === messageId) ?? false\n if (!hit) void fetchSessionUsage(sessionId)\n return unsubscribe\n }, [messageId, sessionId])\n\n const turn: TurnUsage | undefined = data?.turns.find(candidate => candidate.messageId === messageId)\n if (turn === undefined) return null\n const amount = `\u00A5 ${fmtTurnCost(turn.cost)}`\n const tooltip = t('messageCost.tooltip')\n return (\n <span\n className=\"dq-message-cost\"\n tabIndex={0}\n data-dq-tooltip={tooltip}\n aria-label={t('messageCost.aria', { amount })}\n >\n <span className=\"dq-message-cost-separator\" aria-hidden>\u00B7</span>\n <span className=\"dq-message-cost-amount\">{amount}</span>\n </span>\n )\n}\n", "/** All plugin styles in one sheet, injected once at client load. */\n\nexport const css = `\n.dsh-quota-root{position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.45;color:var(--dsw-alias-label-primary,#1f2328);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}\n.dsh-quota-root.dsh-quota-root--hidden{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .14s ease-out,visibility 0s linear .14s}\n.dsh-quota-root.dsh-quota-dragging{transition:none}\n.dsh-quota-card,.dsh-quota-card *{box-sizing:border-box}\n.dsh-quota-card{background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);padding:12px 14px;min-width:208px;max-width:270px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}\n.dsh-quota-root.dsh-quota-dragging .dsh-quota-card{cursor:grabbing;box-shadow:0 12px 32px rgba(0,0,0,.28)}\n.dsh-quota-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;min-width:0}\n.dsh-quota-title{font-weight:600;font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden}\n.dsh-quota-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-quota-grip{color:var(--dsw-alias-label-secondary,#59636e);opacity:.7;font-size:11px;letter-spacing:0;cursor:grab;flex:none}\n.dsh-quota-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#2da44e);flex:none}\n.dsh-quota-dot--error{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dsh-quota-dot--warn{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dsh-quota-total--low{color:var(--dsw-alias-state-warning-primary,#9a6700)!important}\n.dsh-quota-dot--idle{background:var(--dsw-alias-label-secondary,#59636e);opacity:.6}\n.dsh-quota-collapsed-total-row{margin-top:6px}\n.dsh-quota-collapsed-total{font-weight:700;font-size:15px;color:var(--dsw-alias-state-business-primary,#0969da);font-variant-numeric:tabular-nums}\n.dsh-quota-actions{display:flex;gap:2px;flex:none}\n.dsh-quota-btn{border:none;background:transparent;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);padding:3px 7px;border-radius:6px;font-size:13px;line-height:1}\n.dsh-quota-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#1f2328)}\n.dsh-quota-btn:active:not(:disabled){transform:scale(.92)}\n.dsh-quota-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dsh-quota-btn:disabled{opacity:.5;cursor:default}\n.dsh-quota-body{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-remaining-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dsh-quota-total{font-size:24px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328);letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n.dsh-quota-currency{font-size:14px;font-weight:600;margin-left:3px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-row{display:flex;justify-content:space-between;gap:12px;margin-top:6px;font-size:12px}\n.dsh-quota-label{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-value{color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dsh-quota-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-message-cost{position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}\n.dq-message-cost-separator{cursor:default}\n.dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}\n.dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}\n.dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}\n.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.45;text-align:left;white-space:normal;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(-3px);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear .12s}\n.dq-message-cost:hover::after,.dq-message-cost:focus-visible::after{opacity:1;visibility:visible;transform:translateY(0);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear}\n@media (hover:hover){\n [data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}\n [data-time-hover-root]:hover .dq-message-cost,[data-time-hover-root]:focus-within .dq-message-cost{opacity:1}\n}\n/* \u5361\u7247\u95F4\u8DDD\uFF08\u6B64\u5904 gap\uFF09\u523B\u610F\u5927\u4E8E\u5361\u5185\u5C0F\u8282\u95F4\u8DDD\uFF08margin-top:12/14/16px \u90A3\u4E00\u6863\uFF09\uFF0C\u5236\u9020\"\u5361\u7247\u5916\u758F\u3001\u5361\u7247\u5185\u5BC6\"\u7684\n \u758F\u5BC6\u5BF9\u6BD4\uFF1A24px \u76F8\u5BF9\u5361\u5185\u6700\u5927\u6863 16px \u4ECD\u6709 1.5 \u500D\u7EA7\u5DEE\uFF0C\u76F8\u5BF9\u5E38\u89C1\u6863 12px \u6709 2 \u500D\u7EA7\u5DEE\uFF0C\u4E0D\u4E0E .dq-card \u7684\n padding\uFF0816px/20px\uFF0C\u5361\u7247\u5185\u90E8\u547C\u5438\u611F\uFF0C\u8F6E\u6B21 36 \u5DF2\u5B9A\uFF09\u6DF7\u540C\u3002 */\n/* Width parity with the host composer: the dashboard column reserves the\n same max-width as the conversation reading column (860px, \u8F6E\u6B21 42 kept it\n deliberately), but on narrower viewports the fixed 860px would overhang\n the input box \u2014 measured in dashboard.tsx as --dq-composer-w, capped at\n the reading-column width so wide viewports are unchanged. */\n.dq-balance{display:flex;flex-direction:column;gap:24px;padding:20px 24px 48px;max-width:min(860px,var(--dq-composer-w,860px));margin:0 auto;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n@media (max-width:620px){.dq-balance{box-sizing:border-box;max-width:calc(100vw - 56px);padding:16px 16px calc(var(--dq-composer-h,126px) + 16px)}}\n.dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px;min-width:0;max-width:100%}\n.dq-card-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 12px;letter-spacing:.02em}\n.dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}\n.dq-card-head .dq-card-title{margin:0}\n.dq-card-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0}\n/* \u4E3B\u5361\u7247\uFF08\u4F59\u989D/\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017/\u6D88\u8017\u6982\u89C8/\u7528\u91CF\u8D8B\u52BF\uFF0C\u8F6E\u6B21 45 \u65B0\u589E\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\uFF09\u89C6\u89C9\u6743\u91CD\u9AD8\u4E8E\u6B21\u7EA7\u5206\u6790\n \u5361\u7247\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF1A\u53EA\u8C03\u6392\u7248\u5C5E\u6027\u2014\u2014\u5185\u8FB9\u8DDD\u3001\u6807\u9898\u5B57\u53F7\u5B57\u91CD\u4E0E\u53D6\u8272\u3001\u8FB9\u6846\u6DF1\u6D45\u2014\u2014\u4E0D\u5F15\u5165\u65B0\u914D\u8272\u4F53\u7CFB\u3002\u4E24\u6761\u89C4\u5219\n \u4E0E\u4E0A\u9762\u7684\u57FA\u7840\u89C4\u5219\u540C\u4E3A\u5355\u7C7B\u9009\u62E9\u5668\u4E14\u6E90\u7801\u987A\u5E8F\u66F4\u9760\u540E\uFF0C\u5929\u7136\u8986\u76D6\uFF0C\u4E0D\u9700\u8981 !important\u3002\u6B21\u7EA7\u5206\u6790\u5361\u7247\uFF08\u7528\u91CF\n \u70ED\u529B\u56FE/\u9AD8\u5CF0\u95F2\u65F6/\u7F13\u5B58\u547D\u4E2D/\u6A21\u578B\u6392\u884C/\u4F1A\u8BDD\u6392\u884C\uFF09\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF08\u8BBE\u7F6E\uFF0C\u542B\u8F6E\u6B21 39 \u5E76\u5165\u7684\u5B98\u65B9\u5E73\u53F0\u94FE\u63A5\uFF09\u90FD\u4E0D\u52A0\n \u8FD9\u4E2A\u4FEE\u9970\u7C7B\uFF0C\u7EF4\u6301\u539F\u6709 .dq-card \u57FA\u7EBF\uFF0C\u5F62\u6210\u4E24\u6863\u6743\u91CD\u3002\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\u5347\u7EA7\u4E3A\u4E3B\u5361\u7247\u7684\u7406\u7531\uFF1A\u5B83\u548C\u4F59\u989D\u4E00\u6837\u662F\n \u6253\u5F00\u8FD9\u4E2A\u300C\u989D\u5EA6\u300Dtab \u65F6\u4E0A\u4E0B\u6587\u6700\u76F4\u63A5\u76F8\u5173\u7684\u6570\u5B57\uFF08\u8FD9\u4E2A tab \u6302\u5728\u5177\u4F53\u4F1A\u8BDD\u5185\u90E8\uFF0C\u89C1 DESIGN_NOTES\u300C\u4FE1\u606F\u5C42\u7EA7\u300D\uFF09\uFF0C\n \u4E0D\u5C5E\u4E8E\u539F\u6709\u8D26\u6237\u5168\u5C40\u5C42\u7EA7 1-3 \u7684\u4EFB\u4F55\u4E00\u7EA7\uFF0C\u800C\u662F\u4E0E\u4E4B\u6B63\u4EA4\u7684\u53E6\u4E00\u4E2A\u7EF4\u5EA6\uFF0C\u4F46\u540C\u6837\u662F\u9996\u5C4F\u5FC5\u8BFB\uFF0C\u56E0\u6B64\u540C\u6837\u7ED9\n primary \u6743\u91CD\uFF0C\u4E0D\u65B0\u5F00\u7B2C\u4E09\u6863\u3002 */\n.dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dq-card--primary .dq-card-title{font-size:13px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.08))}\n.dq-window-btn{border:0;border-radius:6px;padding:4px 8px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;white-space:nowrap;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}\n.dq-window-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.12)}\n.dq-window-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n@media (max-width:620px){.dq-card-head{align-items:flex-start;flex-wrap:wrap}.dq-card-actions{width:100%;justify-content:space-between}}\n.dq-export{position:relative;flex:none}\n.dq-export-btn{min-width:58px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease,background .15s ease}\n.dq-export-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-export-btn--done{border-color:rgba(45,164,78,.35);color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.08)}\n.dq-export-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:180px;padding:6px;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);display:flex;flex-direction:column;gap:2px}\n.dq-export-menu button{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;border:0;border-radius:6px;padding:7px 8px;background:transparent;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;text-align:left;cursor:pointer}\n.dq-export-menu button:hover,.dq-export-menu button:focus-visible{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-export-menu button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-1px}\n.dq-export-menu small{font-size:10.5px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap}\n/* Small-text tier system (\u8F6E\u6B21 41 \u6536\u655B): two whole-pixel tiers replace what used to be four values\n (11/11.5/12/12.5px) spaced only 0.5px apart \u2014 a gap browsers subpixel-round away, so it read as one\n size with extra maintenance cost, not an actual visual hierarchy. 11px = \"micro\" chrome the user\n scans rather than reads (dt-style stat/period labels, delta/status/coverage pills, share %, tags,\n the widget grip) \u2014 kept as-is. 12px = \"secondary text\" (section titles, hint/footnote/legend/sub\n prose, table cells, tooltip copy, alert/empty-state sentences, and filter/segmented-control button\n labels like .dq-window-btn/.dq-chart-switch-btn, which are the same control family and should share\n one size) \u2014 absorbed the old 11.5px and 12.5px selectors, all by rounding toward the nearer tier\n (11.5\u219212 grows slightly for legibility, safe because none of the affected containers are width-\n capped; 12.5\u219212 shrinks by 0.5px on two full-sentence blocks, imperceptible). Weight/color still do\n the job of separating titles from body text within the 12px tier, same as before this merge. Do not\n reintroduce a fractional-px value in this range; if a selector needs to stand apart from these two,\n it belongs in an already-established different tier (10px icon glyphs, 13px+ hero-adjacent text). */\n.dq-balance-grid{display:flex;flex-wrap:wrap;gap:12px 24px}\n.dq-stat{min-width:110px}\n.dq-stat-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dq-stat-value{font-size:18px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-stat-value--ok{color:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-stat-value--bad{color:var(--dsw-alias-state-error-primary,#cf222e)}\n/* \u8F6E\u6B21 43: .dq-runway is a controlled <details> (see dashboard.tsx) \u2014 hover/toggle state\n drives the real open attribute now, so .dq-runway-detail only needs to react to [open];\n an unopened <details>'s non-summary children don't actually render in current Chromium no\n matter what display value author CSS gives them (:hover{display:block} alone is a dead end). */\n.dq-runway{position:relative}\n.dq-runway>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-runway>summary::-webkit-details-marker{display:none}\n.dq-runway>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-runway-detail{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;max-width:240px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:normal;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-runway[open] .dq-runway-detail{display:block}\n/* hero number for the balance, matching .dq-period-cost; relies on cascade order (declared after\n .dq-stat-value, same specificity) to win over the .dq-stat-value it's paired with in JSX \u2014 keep it below */\n/* \u8F6E\u6B21 47: .dq-remaining is a controlled <details> too now (see dashboard.tsx), same reason as\n .dq-runway above \u2014 an unopened <details>'s non-summary children don't actually render in current\n Chromium regardless of author display value, so the old :hover{display:flex} branch was dead code. */\n.dq-remaining{position:relative;font-size:22px;font-weight:680;letter-spacing:-.01em;line-height:1.25}\n.dq-remaining>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-remaining>summary::-webkit-details-marker{display:none}\n.dq-remaining>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-remaining[open] .dq-remaining-breakdown{display:flex}\n.dq-remaining-granted{color:var(--dsw-alias-label-tertiary,#59636e);font-weight:400;font-size:11px}\n/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned\n .dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the\n next row (e.g. \"\u4F59\u989D\u660E\u7EC6\" open covering \"\u9884\u8BA1\u53EF\u7528\"'s clickable summary) \u2014 force each stat onto its own\n full-width row and switch both popovers to normal in-flow layout so an open one pushes its own row\n taller instead of overlaying a sibling; desktop keeps the original hover-overlay behavior unchanged */\n@media (max-width:620px){\n .dq-balance-grid .dq-stat{flex:1 1 100%}\n .dq-remaining-breakdown,.dq-runway-detail{position:static;margin-top:6px;box-shadow:none;max-width:none;white-space:normal}\n}\n.dq-period-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0}\n.dq-period{min-width:0;padding:4px 16px}\n.dq-period:first-child{padding-left:0}\n.dq-period:last-child{padding-right:0}\n.dq-period+.dq-period{border-left:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n@media (max-width:620px){.dq-period-grid{grid-template-columns:1fr}.dq-period,.dq-period:first-child,.dq-period:last-child{padding:12px 0}.dq-period:first-child{padding-top:2px}.dq-period+.dq-period{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-left:0}}\n.dq-period-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px}\n.dq-period-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-period-cost{font-size:22px;font-weight:680;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;line-height:1.25}\n.dq-period-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-top:2px;font-variant-numeric:tabular-nums;line-height:1.45}\n.dq-budget{margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-budget--unset{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-budget-action{border:0;background:transparent;padding:3px 4px;border-radius:5px;color:var(--dsw-alias-state-business-primary,#0969da);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer}\n.dq-budget-action:hover{text-decoration:underline;text-underline-offset:3px}\n.dq-budget-action:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dq-budget-head,.dq-budget-meta{display:flex;align-items:center;justify-content:space-between;gap:12px}\n.dq-budget-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-budget-amount{font-size:12px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-budget-track{height:8px;margin-top:8px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-budget-fill{width:100%;height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transform-origin:left center;transition:transform .35s cubic-bezier(.22,1,.36,1)}\n.dq-budget--risk .dq-budget-fill{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-budget--over .dq-budget-fill{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-budget-meta{margin-top:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-budget--risk .dq-budget-meta span:last-child{color:var(--dsw-alias-state-warning-primary,#9a6700);font-weight:600}\n.dq-budget--over .dq-budget-meta{color:var(--dsw-alias-state-error-primary,#cf222e);font-weight:600}\n@media (max-width:620px){.dq-budget-head,.dq-budget-meta,.dq-budget--unset{align-items:flex-start;flex-direction:column;gap:4px}}\n.dq-delta{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:600;padding:1px 6px;border-radius:999px;white-space:nowrap;cursor:help;font-variant-numeric:tabular-nums}\n.dq-delta--up{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-delta--down{color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.14)}\n.dq-delta--flat,.dq-delta--new{color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14);font-weight:500}\n.dq-delta-label{font-weight:400;opacity:.8}\n.dq-pricing{margin-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-top:10px}\n.dq-pricing-summary{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);cursor:pointer;list-style:none;width:fit-content;border-radius:6px;padding:2px 4px;margin-left:-4px}\n.dq-pricing-summary::-webkit-details-marker{display:none}\n.dq-pricing-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-pricing[open] .dq-pricing-summary::before{transform:rotate(90deg)}\n.dq-pricing-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-pricing-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-pricing-now{font-size:11px;font-weight:600;padding:1px 7px;border-radius:999px;color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14)}\n.dq-pricing-now--peak{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-pricing-body{margin-top:10px;overflow-x:auto}\n.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}\n.dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}\n.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}\n.dq-pricing-foot{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage{margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-coverage-summary{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;cursor:pointer;list-style:none;width:fit-content;max-width:100%;margin-left:-4px;padding:3px 4px;border-radius:6px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-summary::-webkit-details-marker{display:none}\n.dq-coverage-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-coverage[open] .dq-coverage-summary::before{transform:rotate(90deg)}\n.dq-coverage-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-coverage-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-status{padding:1px 7px;border-radius:999px;background:rgba(45,164,78,.14);color:var(--dsw-alias-state-success-primary,#1a7f37);font-size:11px;font-weight:600}\n.dq-coverage--warn .dq-coverage-status{background:rgba(191,135,0,.14);color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-coverage-brief{font-size:12px;font-variant-numeric:tabular-nums}\n.dq-coverage-body{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-coverage-metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 20px;margin:0}\n.dq-coverage-metrics div{min-width:0}\n.dq-coverage-metrics dt{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-coverage-metrics dd{margin:3px 0 0;font-size:13px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-coverage-range,.dq-coverage-note,.dq-coverage-warning{margin:9px 0 0;font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-range{font-variant-numeric:tabular-nums}\n.dq-coverage-warning{padding:8px 10px;border-radius:8px;background:rgba(191,135,0,.10);color:var(--dsw-alias-state-warning-primary,#7a5200)}\n@media (max-width:620px){.dq-coverage-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.dq-coverage-brief{flex-basis:100%;margin-left:19px}}\n.dq-peak-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85;overflow:hidden}\n.dq-peak-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-peak-legend{display:flex;flex-wrap:wrap;gap:6px 20px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-peak-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}\n.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:14px}\n.dq-peak-delta{font-size:11px;font-weight:500;margin-left:8px;color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-peak-delta--save{color:var(--dsw-alias-state-success-primary,#1a7f37)}\n.dq-peak-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}\n.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:550}\n.dq-stat-value--warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-cache-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);overflow:hidden}\n.dq-cache-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-cache-legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-cache-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--hit{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-legend-swatch--miss{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-cache-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-sessions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}\n.dq-session{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-session-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-session-title{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-session-cost{font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-session-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-session-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-business-primary,#4176e6);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-session-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n/* The \u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300Dcard uses this class on a bare <p> (no flex-gap parent\n to lean on, unlike the .dq-session usage above), so it needs its own\n explicit spacing rather than falling back to the browser's implicit <p>\n margin \u2014 this project always declares spacing explicitly. */\np.dq-session-sub{margin:8px 0}\n.dq-session-foot{margin:12px 0 0;font-size:12px;color:var(--dsw-alias-label-tertiary,#59636e)}\n/* \u8F6E\u6B21 46: each ranking row is now a <details> whose <summary> wraps the existing\n head/track/sub trio unchanged (in a flex-column body div) plus a leading chevron \u2014\n same disclosure grammar as .dq-pricing/.dq-coverage above (hidden native marker,\n rotating glyph, :focus-visible ring), just applied to a multi-line summary instead\n of a single line. .dq-session-details needs its own min-width:0 because it is now\n the flex item inside .dq-session that the ellipsis chain must shrink through. */\n.dq-session-details{min-width:0}\n.dq-session-details>summary{list-style:none;cursor:pointer}\n.dq-session-details>summary::-webkit-details-marker{display:none}\n.dq-session-summary{display:flex;align-items:flex-start;gap:8px;border-radius:8px;padding:4px;margin:-4px}\n.dq-session-summary:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}\n.dq-session-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-session-chevron{flex:none;width:12px;margin-top:4px;font-size:10px;line-height:1;color:var(--dsw-alias-label-tertiary,#59636e);transition:transform .15s ease}\n.dq-session-details[open] .dq-session-chevron{transform:rotate(90deg)}\n.dq-session-summary-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}\n/* Expanded body reuses the same shaded-inset language as .dq-coverage-body (10px\n gap from the summary, 11px/12px padding, 10px radius, bg-layer-2) rather than\n inventing a new \"nested panel\" look. SessionUsageCard renders inside unchanged. */\n.dq-session-expand{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-rank{display:flex;flex-direction:column;gap:14px}\n.dq-rank-row{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-rank-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-rank-name{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-rank-cost{display:inline-flex;align-items:baseline;gap:8px;font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-rank-share{font-size:11px;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);min-width:30px;text-align:right}\n.dq-rank-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-rank-fill{height:100%;border-radius:999px;transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-rank-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n.dq-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;font-size:12px;line-height:1.6;color:var(--dsw-alias-state-warning-primary,#7a5200);background:rgba(191,135,0,.12);border:1px solid rgba(191,135,0,.35)}\n.dq-alert-icon{flex:none;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#bf8700);color:#fff;font-weight:700;font-size:12px;line-height:18px;text-align:center}\n.dq-alert strong{font-weight:650}\n.dq-alert-link{margin-left:8px;color:inherit;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}\n.dq-setting{display:flex;align-items:flex-start;gap:12px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-setting-label{font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}\n.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}\n.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}\n/* Page footer: the package name + version, injected at build time from\n package.json (see build.mjs) so it can never drift from the release. */\n.dq-footer{margin:18px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}\n.dq-links-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}\n.dq-links{display:flex;flex-wrap:wrap;gap:8px}\n.dq-link{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-state-business-primary,#0969da);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}\n.dq-link:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}\n/* .dq-toggle is the first settings control after the links row (\u8F6E\u6B21 39 merge) \u2014 its own\n border-top separates the \"\u5FEB\u6377\u5165\u53E3\" links (originally the standalone \u5B98\u65B9\u5E73\u53F0 card, renamed\n in \u8F6E\u6B21 39 fix-round-1 to read as part of \u8BBE\u7F6E's own navigation area) from \u8BBE\u7F6E controls\n inside the merged card, same 14px/14px/border-l1 recipe as .dq-setting so the two\n sub-sections read at equal weight. */\n.dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-toggle input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}\n.dq-toggle-hint{margin:5px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:7px}\n.dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:6px 14px}\n.dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;cursor:pointer}\n.dq-widget-tab input{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-toggle input:focus-visible,.dq-model-item input:focus-visible,.dq-widget-tab input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-link:focus-visible,.dq-model-btn:focus-visible,.dq-chart-switch-btn:focus-visible,.dq-export-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-usage-totals{display:flex;flex-wrap:wrap;gap:12px 24px;margin-bottom:16px}\n/* value itself (number+unit, e.g. \"9999.9\u4E07\") must never break across lines inside its own column \u2014\n nowrap+ellipsis is a defensive floor for all widths; the actual fix for the realistic 320px 2-col\n case is the font-size drop below, which keeps every measured stress value comfortably under the\n 83px column width without ever hitting the ellipsis fallback (see round 38 PROGRESS entry) */\n.dq-usage-totals .dq-stat-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.dq-vision-share{display:inline-block;margin-left:6px;font-size:11px;font-weight:550;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-vision-sessions{margin-top:12px;display:flex;flex-direction:column;gap:8px;min-width:0}\n/* The vision card's session rows reuse .dq-session-head, but \u2014 unlike the\n ranking card's rows \u2014 they sit directly under .dq-session with no\n .dq-session-summary-body wrapper to carry min-width:0. Without the chain\n below, a long session title (nowrap) sets the row's min-content width and\n can stretch the card past its siblings / the container. */\n.dq-vision-sessions .dq-session{min-width:0}\n.dq-vision-sessions .dq-session-head{min-width:0}\n.dq-vision-sessions .dq-session-title{flex:1 1 auto;min-width:0}\n@media (max-width:620px){.dq-usage-totals{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}.dq-usage-totals .dq-stat{min-width:0}.dq-usage-totals .dq-stat-value{font-size:13px}}\n.dq-chart-peak{margin-left:10px;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}\n.dq-chart-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:16px 0 8px}\n.dq-chart-title:first-of-type{margin-top:0}\n.dq-chart-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:16px 0 8px}\n.dq-chart-block-head .dq-chart-title{margin:0}\n.dq-chart-controls{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:none;flex-wrap:wrap}\n@media (max-width:620px){.dq-chart-block-head{align-items:flex-start;flex-direction:column}.dq-chart-controls{width:100%}}\n.dq-model-picker{position:relative}\n.dq-model-btn{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}\n.dq-model-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-model-btn::after{content:\"\u25BE\";font-size:10px;opacity:.7}\n.dq-model-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:190px;max-height:280px;overflow-y:auto;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);padding:6px;display:flex;flex-direction:column;gap:2px}\n.dq-model-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;font-size:12px;cursor:pointer;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-model-item:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-model-item input{width:14px;height:14px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-model-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-model-item--all{border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-bottom:8px;margin-bottom:2px;border-radius:6px}\n.dq-model-tag{color:var(--dsw-alias-label-tertiary,#59636e);font-size:11px;flex:none}\n.dq-bar-group{display:flex;flex-direction:column-reverse;width:100%}\n.dq-bar-group .dq-bar{flex:0 0 auto;width:100%;border-radius:0}\n.dq-bar-group .dq-bar:first-child{border-radius:0 0 2px 2px}\n.dq-bar-group .dq-bar:last-child{border-radius:2px 2px 0 0}\n.dq-bar-group .dq-bar:not(:last-child){box-shadow:inset 0 1px 0 rgba(0,0,0,.18)}\n.dq-legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-legend-item{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch{width:10px;height:10px;border-radius:3px;flex:none}\n.dq-chart-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));border-radius:999px;flex:none}\n.dq-chart-switch-btn{border:none;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1;padding:5px 12px;border-radius:999px;cursor:pointer;transition:background .15s ease,color .15s ease}\n.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}\n.dq-chart-wrap{position:relative}\n.dq-bars-scroll{overflow-x:auto;overflow-y:hidden;padding-bottom:2px}\n.dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.55;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}\n.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}\n.dq-tip-head{font-weight:650;margin-bottom:2px}\n.dq-tip-row{opacity:.85}\n.dq-bars{display:flex;align-items:flex-end;gap:2px;width:100%}\n.dq-bar-col{flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch}\n.dq-bar{background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:2px 2px 0 0;min-height:1px;transition:filter .12s ease}\n.dq-bar-col{cursor:default}\n.dq-bar-col,.dq-bar,.dq-heat-cell{scroll-margin-block-start:24px;scroll-margin-block-end:160px}\n.dq-bar-col:hover .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar-col:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-bar-col--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar-col--pinned .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-heat-cell:not(.dq-heat-cell--pad):hover{outline:1.5px solid var(--dsw-alias-label-primary,#1f2328);outline-offset:1px}\n.dq-heat-cell:not(.dq-heat-cell--pad):focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-bar-label{font-size:9px;color:var(--dsw-alias-label-tertiary,#59636e);text-align:center;margin-top:3px;overflow:visible;white-space:nowrap}\n.dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}\n.dq-heat-scroll{overflow-x:auto;padding-bottom:2px}\n.dq-heatmap{display:flex;gap:3px}\n.dq-heat-week{display:flex;flex:1 1 0;min-width:6px;flex-direction:column;gap:3px}\n.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:3px;flex:none}\n.dq-heat-cell--pad{background:transparent}\n.dq-heat-months{display:flex;gap:3px;margin-top:4px}\n.dq-heat-month{flex:1 1 0;min-width:6px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap;overflow:visible}\n.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}\n.dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);align-self:flex-end}\n.dq-heat-scale>span:first-child{margin-right:2px}\n.dq-heat-scale>span:last-child{margin-left:2px}\n.dq-muted{color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-crash{margin:10px 0 14px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));border-radius:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary,#cf222e);white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\n.dq-empty{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.6;padding:6px 0}\n/* Error-state modifier for a card body that has nothing else to show (mirrors the .dq-stat-value--ok/\n --bad/--warn pattern: same layout as .dq-empty, declared after it so this color wins at equal\n specificity). Used by the current-session card when its own fetch failed and there is no prior\n data to fall back to \u2014 the status-row .dq-error/.dq-warn spans are for the account-wide balance/\n usage fetch, this is the per-card equivalent. */\n.dq-empty--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-skel{background:linear-gradient(90deg,rgba(125,135,155,.10) 25%,rgba(125,135,155,.20) 37%,rgba(125,135,155,.10) 63%);background-size:400% 100%;border-radius:5px;animation:dq-shimmer 1.5s ease-in-out infinite}\n.dq-stat .dq-skel+.dq-skel{margin-top:6px}\n.dq-skel-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:120px;margin-top:10px}\n.dq-skel-bar{flex:1 1 0;min-width:0;border-radius:2px 2px 0 0}\n@keyframes dq-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}\n.dq-status-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px}\n.dq-status-messages{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0}\n.dq-status-row .dq-warn,.dq-status-row .dq-error{font-size:12px;line-height:1.45}\n.dq-sync{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-sync-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--dsw-alias-label-tertiary,#8c959f)}\n.dq-sync--fresh .dq-sync-dot{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-sync--syncing .dq-sync-dot{background:var(--dsw-alias-state-business-primary,#4176e6);animation:dq-sync-pulse 1.2s ease-in-out infinite}\n.dq-sync--fallback{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-sync--fallback .dq-sync-dot{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-sync--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}\n@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}\n.dq-refresh-btn{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}\n.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-refresh-btn:disabled{cursor:default;opacity:.6}\n.dq-refresh-icon{display:inline-block;line-height:1}\n.dq-refresh-icon--spin{animation:dq-spin .9s linear infinite}\n@keyframes dq-spin{to{transform:rotate(360deg)}}\n@media (prefers-reduced-motion:reduce){\n .dsh-quota-root,.dsh-quota-root *,\n .dsh-quota-root *::before,.dsh-quota-root *::after,\n .dq-message-cost,.dq-message-cost::after,\n .dq-balance,.dq-balance *,\n .dq-balance *::before,.dq-balance *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}\n}\n`\n", "/** The draggable bottom-right floating balance widget.\n *\n * Where it sits (corner) and whether it is collapsed are user choices, so both\n * are persisted (see ./prefs.ts) and restored before the first paint; the\n * widget also re-snaps to its corner when the frame resizes (window resize,\n * sidebar collapse) instead of stranding itself at stale pixel coordinates.\n */\nimport { useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react'\nimport {\n fetchBalance,\n fetchSessionUsage,\n fetchUsage,\n getCachedBalance,\n getCachedSessionUsage,\n getCachedUsage,\n getCachedUsageAt,\n subscribeSessionUsage,\n subscribeUsage,\n} from './api.ts'\nimport { fmt } from './charts.tsx'\nimport type { BalanceData, SessionUsageData, UsageData } from '../contract.ts'\nimport { getActiveConversationViewId, getMainColumn, getShellFrame, slotBox } from './dom.ts'\nimport { localizeApiError, useI18n } from './i18n.tsx'\nimport { fmtTurnCost } from './message-cost.tsx'\nimport { isBoolean, loadPref, savePref } from './prefs.ts'\nimport { lowBalanceStore, quotaViewActiveStore, widgetTabIdsStore, widgetVisibleStore } from './store.ts'\nimport type { ConversationViewTab, ConversationViewsSource } from './views.ts'\n\nconst MARGIN = 16\n\ntype Corner = 'tl' | 'tr' | 'bl' | 'br'\n\nconst CORNER_KEY = 'widget.corner'\nconst COLLAPSED_KEY = 'widget.collapsed'\n\nconst isCorner = (value: unknown): boolean =>\n value === 'tl' || value === 'tr' || value === 'bl' || value === 'br'\n\ninterface Bounds {\n left: number\n top: number\n right: number\n bottom: number\n}\n\ninterface DragSession {\n pointerId: number\n startX: number\n startY: number\n originX: number\n originY: number\n width: number\n height: number\n bounds: Bounds\n}\n\n/** Below this the region is too short to park a widget in, so an exclusion is\n * skipped rather than squeezing the widget out of the frame. */\nconst MIN_REGION_HEIGHT = 140\n\n/**\n * Draggable area = the main column minus the session header and the composer.\n *\n * Using the main column (rather than the whole frame minus the sidebar) also\n * keeps the widget clear of the right-hand details panel when it is open.\n * The header is excluded so the widget can never park on the Chat / Trajectory\n * / \u989D\u5EA6 tab row and swallow its clicks, and the composer is excluded because a\n * bottom-right widget otherwise covers the model picker and the send button \u2014\n * the two controls the user reaches for most.\n */\nfunction getBounds(node: HTMLElement | null): Bounds {\n const fallback: Bounds = { left: 0, top: 0, right: 100000, bottom: 100000 }\n if (node === null) return fallback\n const frame = getShellFrame(node)\n if (frame === null) return fallback\n const frameRect = frame.getBoundingClientRect()\n let left = frameRect.left\n let right = frameRect.right\n let top = frameRect.top\n const mainRect = getMainColumn(frame)?.getBoundingClientRect() ?? null\n if (mainRect !== null && mainRect.width > 0) {\n left = mainRect.left\n right = mainRect.right\n top = mainRect.top\n } else {\n const sidebarRect = (frame.children[0] as HTMLElement | undefined)?.getBoundingClientRect() ?? null\n if (sidebarRect !== null) left = sidebarRect.right\n }\n let bottom = frameRect.bottom\n const headerRect = slotBox(frame, 'conversation.session.header', 'child')\n if (headerRect !== null && headerRect.bottom > top && headerRect.bottom < bottom - MIN_REGION_HEIGHT) {\n top = headerRect.bottom\n }\n const composerRect = slotBox(frame, 'conversation.composer.dock', 'ancestor')\n if (composerRect !== null && composerRect.top < bottom && composerRect.top > top + MIN_REGION_HEIGHT) {\n bottom = composerRect.top\n }\n return { left, top, right, bottom }\n}\n\nfunction cornerPos(corner: Corner, node: HTMLElement | null, bounds: Bounds): { x: number; y: number } {\n const rect = node?.getBoundingClientRect()\n const w = rect?.width ?? 0\n const h = rect?.height ?? 0\n const xLeft = bounds.left + MARGIN\n const yTop = bounds.top + MARGIN\n const xRight = bounds.right - w - MARGIN\n const yBottom = bounds.bottom - h - MARGIN\n if (corner === 'tl') return { x: xLeft, y: yTop }\n if (corner === 'tr') return { x: xRight, y: yTop }\n if (corner === 'bl') return { x: xLeft, y: yBottom }\n return { x: xRight, y: yBottom }\n}\n\ninterface SessionListState {\n current?: string\n}\n\nexport type UseSessionsHook = <T>(selector: (state: SessionListState) => T) => T\n\nexport function QuotaWidget(props: {\n useSessions: UseSessionsHook\n views: ConversationViewsSource\n}): ReactElement | null {\n const { t } = useI18n()\n const sessionId = props.useSessions(state => state.current)\n const [visible, setVisible] = useState(widgetVisibleStore.get())\n useEffect(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), [])\n const [quotaViewActive, setQuotaViewActive] = useState(quotaViewActiveStore.get())\n useEffect(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), [])\n const [lowBalance, setLowBalance] = useState(lowBalanceStore.get())\n useEffect(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), [])\n const [selectedTabIds, setSelectedTabIds] = useState(widgetTabIdsStore.get())\n useEffect(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), [])\n const [viewTabs, setViewTabs] = useState<ConversationViewTab[]>(() => props.views.list())\n useEffect(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views])\n const [activeViewId, setActiveViewId] = useState<string | null>(null)\n\n const cachedBalance = getCachedBalance()\n const [data, setData] = useState<BalanceData | null>(cachedBalance?.data ?? null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(cachedBalance === null)\n const [collapsed, setCollapsed] = useState(() => loadPref<boolean>(COLLAPSED_KEY, isBoolean, false))\n const [pos, setPos] = useState<{ x: number; y: number } | null>(null)\n const [corner, setCorner] = useState<Corner>(() => loadPref<Corner>(CORNER_KEY, isCorner, 'br'))\n const [dragging, setDragging] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const dragRef = useRef<DragSession | null>(null)\n\n // Today's spend comes from the shared usage cache \u2014 never fetched on the\n // widget's own poll, since aggregating the session logs takes seconds. A\n // manual refresh (below) is the one place the widget pays for it.\n const readUsage = (): { data: UsageData | null; at: number | null } => {\n const cached = getCachedUsage()\n return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() }\n }\n const [usage, setUsage] = useState(readUsage)\n useEffect(() => subscribeUsage(() => setUsage(readUsage())), [])\n\n const readSessionUsage = (id: string | undefined): SessionUsageData | null => {\n if (id === undefined) return null\n const response = getCachedSessionUsage(id)\n return response?.ok === true ? response.data ?? null : null\n }\n const [sessionUsage, setSessionUsage] = useState<SessionUsageData | null>(() => readSessionUsage(sessionId))\n useEffect(() => {\n setSessionUsage(readSessionUsage(sessionId))\n if (sessionId === undefined) return\n const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)))\n void fetchSessionUsage(sessionId)\n return unsubscribe\n }, [sessionId])\n\n // The root-scoped overlay does not receive the session's active view store.\n // Read the host's semantic tablist instead and map its selected button by\n // index to the same live slot ledger used to build that tablist.\n useLayoutEffect(() => {\n const viewIds = viewTabs.map(tab => tab.id)\n const read = (): void => {\n const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds)\n setActiveViewId(previous => previous === next ? previous : next)\n }\n read()\n const frame = getShellFrame(rootRef.current)\n let observer: MutationObserver | null = null\n if (frame !== null && typeof MutationObserver === 'function') {\n observer = new MutationObserver(read)\n observer.observe(frame, { subtree: true, childList: true, attributes: true, attributeFilter: ['aria-selected'] })\n }\n const poll = setInterval(read, 1000)\n return () => {\n observer?.disconnect()\n clearInterval(poll)\n }\n }, [sessionId, viewTabs])\n\n const load = async (showLoading: boolean, force = false): Promise<void> => {\n if (showLoading) setLoading(true)\n try {\n const res = await fetchBalance(force)\n if (res.ok && res.data !== undefined) {\n setData(res.data)\n setError(null)\n } else {\n setData(null)\n setError(res.error ?? '')\n }\n } catch (err) {\n setData(null)\n setError((err as { message?: string } | null)?.message ?? String(err))\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n void load(true)\n const id = setInterval(() => { void load(false) }, 60_000)\n return () => clearInterval(id)\n }, [])\n\n /**\n * Keep the widget parked in its corner: on mount (restoring the persisted\n * corner before the first paint), when collapse/expand changes its size, and\n * whenever the layout moves under it \u2014 window resize, sidebar collapse or\n * the details panel opening (observed), plus a slow poll that catches the\n * one change no observer reports: the session header mounting *after* the\n * overlay, which would otherwise leave the widget parked over the tab row.\n *\n * `place` keeps the previous object when the corner has not actually moved,\n * so repeated triggers cost one rect read and no re-render.\n */\n useLayoutEffect(() => {\n if (!visible || quotaViewActive) return\n const node = rootRef.current\n if (node === null) return\n const place = (): void => {\n if (dragRef.current !== null) return\n const next = cornerPos(corner, node, getBounds(node))\n setPos(prev => (prev !== null && prev.x === next.x && prev.y === next.y ? prev : next))\n }\n place()\n const frame = getShellFrame(node)\n let observer: ResizeObserver | null = null\n if (frame !== null && typeof ResizeObserver === 'function') {\n observer = new ResizeObserver(place)\n observer.observe(frame)\n const main = getMainColumn(frame)\n if (main !== null) observer.observe(main)\n }\n window.addEventListener('resize', place)\n const poll = setInterval(place, 1000)\n return () => {\n observer?.disconnect()\n window.removeEventListener('resize', place)\n clearInterval(poll)\n }\n }, [visible, quotaViewActive, corner, collapsed])\n\n const snapToNearest = (session: DragSession): void => {\n const node = rootRef.current\n if (node === null) return\n const { width: w, height: h, bounds: b } = session\n const corners: Array<{ c: Corner; x: number; y: number }> = [\n { c: 'tl', x: b.left + MARGIN, y: b.top + MARGIN },\n { c: 'tr', x: b.right - w - MARGIN, y: b.top + MARGIN },\n { c: 'bl', x: b.left + MARGIN, y: b.bottom - h - MARGIN },\n { c: 'br', x: b.right - w - MARGIN, y: b.bottom - h - MARGIN },\n ]\n const rect = node.getBoundingClientRect()\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n let best = corners[0]\n let bestDistance = Number.POSITIVE_INFINITY\n for (const candidate of corners) {\n const ccx = candidate.x + w / 2\n const ccy = candidate.y + h / 2\n const distance = (ccx - cx) ** 2 + (ccy - cy) ** 2\n if (distance < bestDistance) {\n bestDistance = distance\n best = candidate\n }\n }\n setCorner(best.c)\n savePref(CORNER_KEY, best.c)\n setPos({ x: best.x, y: best.y })\n }\n\n /** Idempotent end-of-drag; also the missed-pointerup backstop. */\n const endDrag = (snap: boolean): void => {\n const session = dragRef.current\n if (session === null) return\n dragRef.current = null\n setDragging(false)\n const node = rootRef.current\n if (node !== null && typeof node.releasePointerCapture === 'function') {\n try {\n node.releasePointerCapture(session.pointerId)\n } catch {\n // capture already released\n }\n }\n if (snap) {\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => snapToNearest(session))\n } else {\n snapToNearest(session)\n }\n }\n }\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0) return\n const target = e.target as Element | null\n if (target !== null && typeof target.closest === 'function' && target.closest('button') !== null) return\n const node = rootRef.current\n if (node === null) return\n const bounds = getBounds(node)\n const rect = node.getBoundingClientRect()\n dragRef.current = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startY: e.clientY,\n originX: rect.left,\n originY: rect.top,\n width: rect.width,\n height: rect.height,\n bounds,\n }\n node.setPointerCapture(e.pointerId)\n setDragging(true)\n e.preventDefault()\n }\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const session = dragRef.current\n if (session === null || session.pointerId !== e.pointerId) return\n if ((e.buttons & 1) === 0) {\n endDrag(true)\n return\n }\n const dx = e.clientX - session.startX\n const dy = e.clientY - session.startY\n const maxX = session.bounds.right - session.width - MARGIN\n const maxY = session.bounds.bottom - session.height - MARGIN\n const minX = session.bounds.left + MARGIN\n const minY = session.bounds.top + MARGIN\n setPos({\n x: Math.max(minX, Math.min(session.originX + dx, maxX)),\n y: Math.max(minY, Math.min(session.originY + dy, maxY)),\n })\n }\n\n const onPointerUp = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const session = dragRef.current\n if (session === null || session.pointerId !== e.pointerId) return\n endDrag(true)\n }\n\n if (!visible) return null\n\n const allowedOnActiveView = activeViewId === null\n ? sessionId === undefined\n : activeViewId !== 'balance' && (selectedTabIds === null || selectedTabIds.includes(activeViewId))\n const hidden = quotaViewActive || !allowedOnActiveView\n\n const primary = data !== null && data.balances.length > 0 ? data.balances[0] : null\n const balanceValue = primary !== null ? Number(primary.total) : Number.NaN\n const low = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance\n const dotClass = primary === null\n ? 'dsh-quota-dot dsh-quota-dot--idle'\n : data?.isAvailable === false\n ? 'dsh-quota-dot dsh-quota-dot--error'\n : low ? 'dsh-quota-dot dsh-quota-dot--warn' : 'dsh-quota-dot'\n\n // The usage figure can be minutes old (it is only refreshed on demand), so\n // say how old rather than presenting it as live.\n const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 60_000)\n const usageAgeText = ageMinutes === null || ageMinutes < 1\n ? t('widget.updatedNow')\n : t('widget.stale', { minutes: ageMinutes })\n const sessionCostRow = sessionUsage === null ? null : (\n <div className=\"dsh-quota-row\" title={t('widget.sessionCostTitle')}>\n <span className=\"dsh-quota-label\">{t('widget.sessionCost')}</span>\n <span className=\"dsh-quota-value\">\u00A5 {fmtTurnCost(sessionUsage.cost)}</span>\n </div>\n )\n\n let body: ReactElement | null = null\n if (!collapsed) {\n if (loading && data === null && error === null) {\n body = <div className=\"dsh-quota-body\"><div>{t('widget.loading')}</div>{sessionCostRow}</div>\n } else if (error !== null && data === null) {\n body = (\n <div className=\"dsh-quota-body\">\n <div className=\"dsh-quota-error\">{localizeApiError(error, t, 'error.query')}</div>\n {sessionCostRow}\n </div>\n )\n } else if (primary !== null) {\n body = (\n <div className=\"dsh-quota-body\">\n <div className=\"dsh-quota-remaining-label\">{t('balance.remaining')}</div>\n <div title={low ? t('widget.lowTitle', { amount: fmt(lowBalance) }) : undefined}>\n <span className={`dsh-quota-total${low ? ' dsh-quota-total--low' : ''}`}>{fmt(primary.total)}</span>\n <span className=\"dsh-quota-currency\">{primary.currency}</span>\n </div>\n {usage.data !== null && (\n <div className=\"dsh-quota-row\" title={t('widget.todayCostTitle', { age: usageAgeText })}>\n <span className=\"dsh-quota-label\">{t('widget.todayCost')}</span>\n <span className=\"dsh-quota-value\">\u00A5 {fmt(usage.data.summary.today.cost)}</span>\n </div>\n )}\n {sessionCostRow}\n {data?.isAvailable === false && (\n <div className=\"dsh-quota-row\">\n <span className=\"dsh-quota-label\">{t('common.status')}</span>\n <span className=\"dsh-quota-value dsh-quota-error\">{t('common.unavailable')}</span>\n </div>\n )}\n </div>\n )\n } else {\n body = (\n <div className=\"dsh-quota-body\">\n <div className=\"dsh-quota-error\">{t('widget.noBalance')}</div>\n {sessionCostRow}\n </div>\n )\n }\n }\n\n const style: CSSProperties | undefined = pos !== null\n ? { left: `${pos.x}px`, top: `${pos.y}px`, right: 'auto', bottom: 'auto' }\n : undefined\n\n return (\n <div\n className={`dsh-quota-root${dragging ? ' dsh-quota-dragging' : ''}${hidden ? ' dsh-quota-root--hidden' : ''}`}\n style={style}\n aria-hidden={hidden}\n >\n <div\n ref={rootRef}\n className=\"dsh-quota-card\"\n title={t('widget.dragCard')}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={() => endDrag(false)}\n onLostPointerCapture={() => endDrag(false)}\n >\n <div className=\"dsh-quota-header\">\n <div className=\"dsh-quota-title\">\n <span className=\"dsh-quota-grip\" title={t('widget.dragGrip')}>\u2807</span>\n <span className={dotClass} />\n <span className=\"dsh-quota-name\">{t('widget.title')}</span>\n </div>\n <div className=\"dsh-quota-actions\">\n <button\n className=\"dsh-quota-btn\"\n type=\"button\"\n title={t('widget.refresh')}\n aria-label={t('widget.refresh')}\n disabled={loading}\n onClick={() => {\n void load(true, true)\n void fetchUsage(true)\n if (sessionId !== undefined) void fetchSessionUsage(sessionId, true)\n }}\n >\n \u21BB\n </button>\n <button\n className=\"dsh-quota-btn\"\n type=\"button\"\n title={collapsed ? t('widget.expand') : t('widget.collapse')}\n aria-label={collapsed ? t('widget.expandAria') : t('widget.collapseAria')}\n aria-expanded={!collapsed}\n onClick={() => {\n const next = !collapsed\n setCollapsed(next)\n savePref(COLLAPSED_KEY, next)\n }}\n >\n {collapsed ? '+' : '\u2212'}\n </button>\n </div>\n </div>\n {collapsed && primary !== null && (\n <div className=\"dsh-quota-collapsed-total-row\">\n <span className={`dsh-quota-collapsed-total${low ? ' dsh-quota-total--low' : ''}`}>{fmt(primary.total)} {primary.currency}</span>\n </div>\n )}\n {body}\n </div>\n </div>\n )\n}\n", "/** Live projection of the host's `conversation.view` slot ledger. */\n\nexport interface ConversationViewTab {\n id: string\n label: string\n}\n\nexport interface ConversationViewsSource {\n list(): ConversationViewTab[]\n subscribe(fn: () => void): () => void\n}\n\nexport interface ConversationViewSlotEntry {\n options: {\n id?: string\n label?: string | (() => string)\n }\n}\n\nexport interface ConversationViewSlots {\n entries(name: string): ConversationViewSlotEntry[]\n subscribe(name: string, fn: () => void): () => void\n}\n\nconst labelOf = (entry: ConversationViewSlotEntry): string => {\n const { id = '', label } = entry.options\n try {\n const resolved = typeof label === 'function' ? label() : label\n return typeof resolved === 'string' && resolved !== '' ? resolved : id\n } catch {\n return id\n }\n}\n\n/** Build a source once at plugin apply-time. The ledger owns ordering, so the\n * same index also maps to the host's rendered tab buttons without relying on\n * hashed class names or translated labels. */\nexport function conversationViewsSource(slots: ConversationViewSlots): ConversationViewsSource {\n return {\n list: () => slots.entries('conversation.view')\n .flatMap(entry => entry.options.id === undefined\n ? []\n : [{ id: entry.options.id, label: labelOf(entry) }]),\n subscribe: fn => slots.subscribe('conversation.view', fn),\n }\n}\n"],
5
- "mappings": "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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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",
3
+ "sources": ["../src/client/index.tsx", "../src/client/boundary.tsx", "../src/client/cache.ts", "../src/contract.ts", "../src/client/api.ts", "../src/client/dashboard.tsx", "../src/client/budget.ts", "../src/client/charts.tsx", "../src/client/chart-focus.ts", "../src/client/i18n.tsx", "../src/client/locales.ts", "../src/client/dom.ts", "../src/client/export.ts", "../src/client/freshness.ts", "../src/client/metric.ts", "../src/client/prefs.ts", "../src/client/store.ts", "../src/client/message-cost.tsx", "../src/client/styles.ts", "../src/client/widget.tsx", "../src/client/sessions.ts", "../src/client/views.ts"],
4
+ "sourcesContent": ["/**\n * dsh-usage-dashboard client half: mounts the floating balance widget\n * (`shell.overlay`) and the \u300C\u989D\u5EA6\u300D view tab (`conversation.view`).\n * Data arrives through the package's own host API via same-origin fetch.\n */\nimport { ErrorBoundary } from './boundary.tsx'\nimport type { ClientContext } from './context.ts'\nimport { fetchBalance, fetchUsage } from './api.ts'\nimport { BalanceDashboard } from './dashboard.tsx'\nimport { LocaleProvider, type Translate } from './i18n.tsx'\nimport { en, NS, zh } from './locales.ts'\nimport { MessageCost } from './message-cost.tsx'\nimport { css } from './styles.ts'\nimport { QuotaWidget, type UseSessionsHook } from './widget.tsx'\nimport { conversationViewsSource } from './views.ts'\n\n/** Plugin identity for the client bundle. */\nexport const name = 'dsh-usage-dashboard'\n\n/** Services required before load. */\nexport const inject = ['slots', 'locale']\n\n/**\n * Mount the two surfaces.\n * @param ctx - client Cordis context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-usage-dashboard: browser dictionaries')\n const t = ctx.locale.bind(NS)\n const views = conversationViewsSource(ctx.slots)\n // One stylesheet for the whole plugin; the module loader claims and removes\n // plugin-owned style tags on unload.\n if (typeof document !== 'undefined') {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-usage-dashboard'\n tag.textContent = css\n document.head.appendChild(tag)\n }\n\n // Warm both caches the moment the client bundle loads, rather than waiting\n // for the \u300C\u989D\u5EA6\u300D tab to mount. Usage aggregation alone takes seconds (it\n // replays every session log), so without this the tab's first open always\n // paid that latency; with it, by the time someone actually clicks the tab\n // the fetch has usually already landed and the tab renders from cache. Not\n // awaited and not surfaced: a failure here just leaves the widget/tab to\n // fetch (and report) it themselves on mount, same as before this existed.\n void fetchBalance().catch(() => {})\n void fetchUsage().catch(() => {})\n\n ctx.slots.inject('shell.overlay', () => ctx.slots.register(\n { name: 'shell.overlay', id: 'deepseek-quota', order: 1000, locale: NS, label: () => t('widget.title') },\n ({ t: slotT, useSessions }: { t: Translate; useSessions: UseSessionsHook }) => (\n <LocaleProvider t={slotT}>\n <ErrorBoundary t={slotT} silent><QuotaWidget useSessions={useSessions} views={views} /></ErrorBoundary>\n </LocaleProvider>\n ),\n ))\n\n ctx.slots.inject('conversation.chat.assistant-actions', () => ctx.slots.register(\n {\n name: 'conversation.chat.assistant-actions',\n id: 'dsh-usage-turn-cost',\n order: 5,\n locale: NS,\n label: () => t('messageCost.label'),\n },\n ({ t: slotT, sessionId, messageId }: { t: Translate; sessionId: string; messageId: string }) => (\n <LocaleProvider t={slotT}>\n <ErrorBoundary t={slotT} silent>\n <MessageCost sessionId={sessionId} messageId={messageId} />\n </ErrorBoundary>\n </LocaleProvider>\n ),\n ))\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register(\n { name: 'conversation.view', id: 'balance', order: 20, locale: NS, label: () => t('nav.quota') },\n // `conversation.view` is a session-scoped slot, so the host injects its\n // standard session props (including `sessionId`) alongside `t` \u2014 this\n // plugin only destructures the two fields it needs, per the existing\n // \"hand-write the minimal structural type\" convention (see context.ts).\n // `sessionId` is typed optional defensively even though the host is\n // expected to always supply it: BalanceDashboard renders the current-\n // session card only when it actually has one, never throws either way.\n ({ t: slotT, sessionId }: { t: Translate; sessionId?: string }) => (\n <LocaleProvider t={slotT}>\n <ErrorBoundary t={slotT}><BalanceDashboard sessionId={sessionId} views={views} /></ErrorBoundary>\n </LocaleProvider>\n ),\n ))\n}\n", "/**\n * Error boundary for the plugin's two slot entries.\n *\n * A slot component that throws while rendering takes its whole subtree down \u2014\n * for the \u300C\u989D\u5EA6\u300D view that meant a blank tab, and the only thing that fixed it\n * was a browser profile with no localStorage. A crash should degrade to\n * something the user can act on, and it should never be the tab's silent\n * default state.\n */\nimport { Component, type ErrorInfo, type ReactNode } from 'react'\nimport type { Translate } from './i18n.tsx'\n\nconst PREFIX = 'dsh-usage-dashboard:'\n\n/** Drop every key this plugin owns \u2014 caches and preferences alike. */\nfunction clearPluginStorage(): void {\n if (typeof localStorage === 'undefined') return\n try {\n for (const key of Object.keys(localStorage)) {\n if (key.startsWith(PREFIX)) localStorage.removeItem(key)\n }\n } catch {\n // Nothing better to do; the reload below is still worth attempting.\n }\n}\n\ninterface Props {\n /** Shown instead of the fallback UI when the surface has no room for it. */\n silent?: boolean\n t: Translate\n children: ReactNode\n}\n\ninterface State {\n message: string | null\n}\n\nexport class ErrorBoundary extends Component<Props, State> {\n state: State = { message: null }\n\n static getDerivedStateFromError(error: unknown): State {\n return { message: (error as { message?: string } | null)?.message ?? String(error) }\n }\n\n componentDidCatch(error: unknown, info: ErrorInfo): void {\n console.error('[dsh-usage-dashboard] render failed', error, info.componentStack)\n }\n\n private readonly reset = (): void => {\n clearPluginStorage()\n location.reload()\n }\n\n render(): ReactNode {\n const { message } = this.state\n if (message === null) return this.props.children\n if (this.props.silent === true) return null\n const { t } = this.props\n return (\n <div className=\"dq-balance\">\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('boundary.title')}</div>\n <p className=\"dq-empty\">{t('boundary.body')}</p>\n <pre className=\"dq-crash\">{message}</pre>\n <button type=\"button\" className=\"dq-refresh-btn\" onClick={this.reset}>\n {t('boundary.reset')}\n </button>\n </div>\n </div>\n )\n }\n}\n", "/**\n * Package-local TTL caches shared by the widget, the dashboard, and every\n * tab switch. Cached values outlive the TTL: `get()` still returns them\n * (marked by `at`) so a reopening tab can render instantly and refresh in\n * the background, while `getFresh()` is the cache-aware fetch path.\n *\n * When a `storageKey` is given, every `put()` also mirrors the entry to\n * localStorage so a page refresh keeps the instant first paint (the data\n * shown is the last known good value; a background refresh updates it).\n */\n\n/** Bump whenever the persisted shape changes in a way `isUsable` cannot catch. */\nconst LS_VERSION = 2\n\nexport interface CacheHit<T> {\n data: T\n at: number\n}\n\nexport interface ResourceCache<T> {\n /** Last cached value even when stale; null when never cached. */\n get(): CacheHit<T> | null\n /** Value only when it is still within the TTL. */\n getFresh(): T | null\n put(data: T): void\n}\n\n/**\n * Read a persisted entry, but only trust it if it still matches the shape the\n * current code reads. A payload written by an older build can be missing fields\n * this one dereferences, and because the first render is seeded straight from\n * here, an unchecked stale entry crashes the view \u2014 for the user it looks like\n * a blank tab that only a fresh browser profile fixes.\n */\nfunction loadPersisted<T>(storageKey: string, isUsable?: (data: T) => boolean): CacheHit<T> | null {\n if (typeof localStorage === 'undefined') return null\n const key = `${storageKey}:v${LS_VERSION}`\n try {\n const raw = localStorage.getItem(key)\n if (raw === null) return null\n const parsed = JSON.parse(raw) as { data?: T; at?: number }\n if (parsed.data === undefined || typeof parsed.at !== 'number') return null\n if (isUsable !== undefined && !isUsable(parsed.data)) {\n localStorage.removeItem(key)\n return null\n }\n return { data: parsed.data, at: parsed.at }\n } catch {\n return null\n }\n}\n\n/** Drop entries this key wrote under earlier versions; nothing reads them again. */\nfunction pruneOldVersions(storageKey: string): void {\n if (typeof localStorage === 'undefined') return\n try {\n const current = `${storageKey}:v${LS_VERSION}`\n for (const key of Object.keys(localStorage)) {\n if (key.startsWith(`${storageKey}:v`) && key !== current) localStorage.removeItem(key)\n }\n } catch {\n // Leaving stale keys behind is harmless; they are never read.\n }\n}\n\nfunction persist<T>(storageKey: string, hit: CacheHit<T>): void {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(`${storageKey}:v${LS_VERSION}`, JSON.stringify(hit))\n } catch {\n // Quota / private-mode failures must never break the in-memory path.\n }\n}\n\nexport function createCache<T>(ttlMs: number, storageKey?: string, isUsable?: (data: T) => boolean): ResourceCache<T> {\n if (storageKey !== undefined) pruneOldVersions(storageKey)\n let hit: CacheHit<T> | null = storageKey === undefined ? null : loadPersisted<T>(storageKey, isUsable)\n return {\n get: () => hit,\n getFresh: () => (hit !== null && Date.now() - hit.at < ttlMs ? hit.data : null),\n put: (data) => {\n hit = { data, at: Date.now() }\n if (storageKey !== undefined) persist(storageKey, hit)\n },\n }\n}\n", "/** Wire contracts shared by the host API and the client fetchers. */\n\nexport interface BalanceInfo {\n currency: string\n total: string\n granted: string\n toppedUp: string\n}\n\nexport interface BalanceData {\n isAvailable: boolean\n balances: BalanceInfo[]\n}\n\nexport interface BalanceResponse {\n ok: boolean\n error?: string\n data?: BalanceData\n}\n\nexport interface DailyUsage {\n date: string\n input: number\n output: number\n cache: number\n total: number\n cost: number\n calls: number\n}\n\nexport interface HourlyUsage {\n hour: number\n total: number\n cost: number\n calls: number\n}\n\nexport interface HeatmapUsage {\n date: string\n total: number\n cost: number\n calls: number\n}\n\nexport interface UsageTotals {\n input: number\n output: number\n cache: number\n reasoning: number\n total: number\n cost: number\n calls: number\n /**\n * What the cache-hit tokens would have cost at cache-miss rates, minus what\n * they actually cost \u2014 i.e. the money prefix caching saved. Folded per\n * record so it respects each record's model and time-of-day rates.\n */\n cacheSavings: number\n}\n\n/** One per-day / per-hour point of a single model's series. */\nexport interface ModelSeriesPoint {\n total: number\n cost: number\n calls: number\n}\n\n/** Per-model usage aggregation: overall totals plus 30-day and 24-hour series. */\nexport interface ModelUsage {\n provider: string\n model: string\n input: number\n output: number\n cache: number\n total: number\n cost: number\n calls: number\n daily: ModelSeriesPoint[]\n hourly: ModelSeriesPoint[]\n}\n\n/** Usage rolled up over one time window. */\nexport interface PeriodUsage {\n total: number\n cost: number\n calls: number\n}\n\n/**\n * Time-anchored rollups. \"Accumulated since forever\" answers no question a\n * user actually has; these do: what did today cost, what has this month cost,\n * and is that more or less than the comparable window before it.\n */\nexport interface UsageSummary {\n today: PeriodUsage\n yesterday: PeriodUsage\n /** Current calendar month, to date. */\n month: PeriodUsage\n /** Previous calendar month up to the same day-of-month, so a mid-month\n * comparison is against a same-length window rather than a full month. */\n lastMonthToDate: PeriodUsage\n}\n\n/** CNY per 1M tokens for one model tier. */\nexport interface PricingRates {\n cacheHit: number\n input: number\n output: number\n}\n\nexport interface PricingTier {\n model: string\n peak: PricingRates\n offPeak: PricingRates\n}\n\n/** What the cost column was computed with, so the estimate can be audited. */\nexport interface PricingInfo {\n currency: string\n /** Date peak/off-peak pricing took effect; earlier usage is costed at the\n * flat rates that were actually charged then. */\n switchDate: string\n inPeakNow: boolean\n peakWindows: string[]\n tiers: PricingTier[]\n}\n\n/**\n * How usage divides between DeepSeek's peak and off-peak windows, plus the\n * same usage re-priced as if everything had landed off-peak \u2014 answering\n * \"what would shifting batch work off-peak save me\" under the peak/off-peak\n * table in effect since 2026-08-17.\n */\nexport interface PeakSplit {\n peak: PeriodUsage\n offPeak: PeriodUsage\n /** Every record priced under the peak/off-peak table, as actually classified. */\n peakEraCost: number\n /** Every record priced under that table as if it had landed off-peak. */\n offPeakEraCost: number\n}\n\n/** One session's share of the bill. */\nexport interface SessionCost {\n id: string\n /** The session's title from its log; falls back to a short id. */\n title: string\n total: number\n cost: number\n calls: number\n /** Epoch ms of the last usage record in this session. */\n lastActive: number\n}\n\n/**\n * Multimodal (image) usage, folded from the same log replay: image blocks in\n * user messages and tool results are counted and sized, then estimated with\n * the official resizing rule (see `estimateImageTokens`). Each image is\n * attributed to the next usage record \u2014 the model call whose prompt carried\n * it \u2014 and priced at that call's input (cache-miss) rate, so the share answer\n * \"how much of the bill came from pictures\" has a real number.\n */\nexport interface VisionStats {\n /** Image blocks counted in the scope. */\n images: number\n /** Estimated image tokens under the official conversion rule. */\n imageTokens: number\n /** Estimated CNY cost of those image tokens at the consuming calls' rates. */\n cost: number\n /** Total image payload bytes when the attachment refs carried a size. */\n bytes: number\n}\n\n/** One day's image usage for the per-window / per-day series. */\nexport interface VisionDayPoint {\n date: string\n images: number\n imageTokens: number\n cost: number\n}\n\n/** One session's image usage, ranked for \"which run kept sending pictures\". */\nexport interface VisionSession {\n id: string\n title: string\n images: number\n imageTokens: number\n cost: number\n}\n\n/**\n * What the local log replay could actually inspect. Totals can otherwise look\n * authoritative even when one corrupt session was skipped, so this metadata\n * travels with every usage response and lets the UI disclose partial results.\n */\nexport interface UsageCoverage {\n scope: 'local-dsh-session-logs'\n /** Session headers with a usable id returned by the local persistence. */\n listedSessions: number\n /** Session logs read successfully, including logs with no usage. */\n scannedSessions: number\n /** Valid assistant usage records folded into the totals. */\n usageRecords: number\n /** Usage records rejected because their time or token data was invalid. */\n skippedRecords: number\n /** Session logs that could not be read at all. */\n failedSessions: number\n /** Epoch ms of the first/last valid usage record across all local logs. */\n earliestAt: number | null\n latestAt: number | null\n}\n\nexport const USAGE_WINDOW_DAYS = [7, 30, 90, 365] as const\nexport type UsageWindowDays = (typeof USAGE_WINDOW_DAYS)[number]\n\n/**\n * Session ids observed in this codebase (host-issued, e.g.\n * `session-484a1c14-c6fe-4d6a-abfd-a2d8d2f664d5`) are always plain\n * ASCII-safe tokens \u2014 no documented format contract exists for them outside\n * this repo (`SessionPersistenceFace` is implemented by the DSH host, not\n * here), so this is a defensive charset whitelist rather than an\n * exact-format check: letters, digits, `-`, `_` only, with a generous length\n * cap. It exists specifically to stop path-separator / `..` / null-byte\n * payloads from ever reaching the session-log read \u2014 `id` on the\n * `/session` route arrives verbatim from the browser-controlled `?id=` query\n * parameter, unlike `fetchUsage()`'s session ids, which the host itself\n * enumerates via `persistence.list()`.\n */\nexport const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/\n\nexport const isValidSessionId = (id: string): boolean => SESSION_ID_PATTERN.test(id)\n\n/** One internally consistent view of the same local logs over a calendar window. */\nexport interface UsageWindow {\n days: UsageWindowDays\n daily: DailyUsage[]\n hourly: HourlyUsage[]\n totals: UsageTotals\n models: ModelUsage[]\n /** Most expensive sessions inside this window, capped at SESSION_TOP_N. */\n sessions: SessionCost[]\n /** All sessions that contributed usage inside this window. */\n sessionCount: number\n vision: VisionStats\n visionDaily: VisionDayPoint[]\n /** Sessions that sent the most images inside this window, capped. */\n visionSessions: VisionSession[]\n}\n\nexport interface UsageData {\n daily: DailyUsage[]\n hourly: HourlyUsage[]\n heatmap: HeatmapUsage[]\n totals: UsageTotals\n models: ModelUsage[]\n summary: UsageSummary\n pricing: PricingInfo\n peakSplit: PeakSplit\n /** Most expensive sessions first, capped \u2014 see SESSION_TOP_N. */\n sessions: SessionCost[]\n /** How many sessions contributed usage in total. */\n sessionCount: number\n coverage: UsageCoverage\n windows: UsageWindow[]\n vision: VisionStats\n visionDaily: VisionDayPoint[]\n /** Sessions that sent the most images, capped \u2014 see SESSION_TOP_N. */\n visionSessions: VisionSession[]\n}\n\nexport interface UsageResponse {\n ok: boolean\n error?: string\n data?: UsageData\n}\n\n/**\n * One model's contribution to a single session's bill \u2014 the same field\n * shape as `ModelUsage` minus the `daily`/`hourly` series, which only make\n * sense aggregated across many sessions.\n */\nexport interface SessionModelUsage {\n provider: string\n model: string\n input: number\n output: number\n cache: number\n total: number\n cost: number\n calls: number\n}\n\n/** One completed conversation turn's usage. `messageId` is the durable id of\n * the turn's final assistant message \u2014 the same identity the chat action-strip\n * slot exposes to client plugins. Tool-call steps inside the turn are folded\n * into this one figure rather than shown as misleading separate charges. */\nexport interface TurnUsage {\n messageId: string\n total: number\n cost: number\n calls: number\n}\n\n/**\n * Usage for exactly one session \u2014 what the \u300C\u989D\u5EA6\u300Dtab's own conversation\n * is costing, as opposed to `UsageData`'s account-wide totals. Fetched on\n * demand (see `fetchSessionUsage`), not part of the 5-minute `usage` memo:\n * a single session read is cheap and must reflect the session's live state.\n */\nexport interface SessionUsageData {\n sessionId: string\n /** The session's own title from its log; falls back to a short id. */\n title: string\n total: number\n cost: number\n calls: number\n /** Epoch ms of the first/last valid usage record in this session, or null\n * when the session has produced no usage yet. */\n firstActive: number | null\n lastActive: number | null\n /** Most expensive model first. */\n models: SessionModelUsage[]\n /** Completed turns, in log order, addressed by their final message id. */\n turns: TurnUsage[]\n}\n\nexport interface SessionUsageResponse {\n ok: boolean\n error?: string\n data?: SessionUsageData\n}\n", "/**\n * Same-origin fetchers for the host half's JSON API, backed by package-local\n * TTL caches (see ./cache.ts). A fresh cache hit resolves without any network\n * work, so tab switches render instantly; `force` bypasses both the memory\n * cache and the HTTP cache (fetch cache: 'no-store') and asks the host to\n * bypass its own memo via `?refresh=1`.\n */\nimport { createCache } from './cache.ts'\nimport { USAGE_WINDOW_DAYS } from '../contract.ts'\nimport type { BalanceResponse, SessionUsageResponse, UsageResponse } from '../contract.ts'\n\n/** Mirror the host memos: balance 60s, usage 5min. */\nconst BALANCE_TTL_MS = 60_000\nconst USAGE_TTL_MS = 5 * 60_000\n\nasync function getJson<T>(path: string, cache: RequestCache): Promise<T> {\n const res = await fetch(path, { headers: { accept: 'application/json' }, cache })\n if (!res.ok) {\n return { ok: false, error: `HTTP ${res.status}` } as unknown as T\n }\n return res.json() as Promise<T>\n}\n\n/**\n * Every field the UI dereferences off a cached payload. A persisted entry that\n * predates one of them would otherwise be rendered as if it were current and\n * take the whole view down; listing them here keeps that failure impossible\n * instead of merely unlikely. Add a line whenever the UI starts reading a new\n * top-level field.\n */\nconst usageIsUsable = (res: UsageResponse): boolean => {\n if (res.ok !== true) return false\n const data = res.data\n return data !== undefined\n && Array.isArray(data.daily) && Array.isArray(data.hourly) && Array.isArray(data.heatmap)\n && Array.isArray(data.models) && Array.isArray(data.sessions)\n && typeof data.sessionCount === 'number'\n && data.coverage?.scope === 'local-dsh-session-logs'\n && typeof data.coverage.scannedSessions === 'number'\n && typeof data.coverage.skippedRecords === 'number'\n && Array.isArray(data.windows) && data.windows.length === USAGE_WINDOW_DAYS.length\n && USAGE_WINDOW_DAYS.every(days => data.windows.some(window => window.days === days))\n && data.windows.every(window => Array.isArray(window.daily)\n && Array.isArray(window.hourly) && Array.isArray(window.models) && Array.isArray(window.sessions))\n && data.vision !== undefined && typeof data.vision.images === 'number'\n && typeof data.vision.imageTokens === 'number' && typeof data.vision.cost === 'number'\n && Array.isArray(data.visionDaily) && Array.isArray(data.visionSessions)\n && data.windows.every(window => window.vision !== undefined\n && Array.isArray(window.visionDaily) && Array.isArray(window.visionSessions))\n && data.totals !== undefined && typeof data.totals.cacheSavings === 'number'\n && data.summary?.today !== undefined\n && Array.isArray(data.pricing?.tiers)\n && data.peakSplit?.peak !== undefined\n}\n\nconst balanceIsUsable = (res: BalanceResponse): boolean =>\n res.ok === true && Array.isArray(res.data?.balances)\n\nconst balanceCache = createCache<BalanceResponse>(BALANCE_TTL_MS, 'dsh-usage-dashboard:balance', balanceIsUsable)\nconst usageCache = createCache<UsageResponse>(USAGE_TTL_MS, 'dsh-usage-dashboard:usage', usageIsUsable)\n\n/** Last cached value (possibly stale), for an instant first render. */\nexport const getCachedBalance = (): BalanceResponse | null => balanceCache.get()?.data ?? null\nexport const getCachedUsage = (): UsageResponse | null => usageCache.get()?.data ?? null\n/** When the cached usage was fetched, so a consumer can say how old it is. */\nexport const getCachedUsageAt = (): number | null => usageCache.get()?.at ?? null\n\n/**\n * Notified whenever usage is (re)fetched. The floating widget reads usage\n * straight from this cache rather than fetching on its own: aggregating every\n * session log takes seconds, and a background widget must not pay that\n * repeatedly. It subscribes here so it still updates when the dashboard \u2014 or\n * its own refresh button \u2014 brings fresh data in.\n */\nconst usageListeners = new Set<() => void>()\n\nexport const subscribeUsage = (fn: () => void): (() => void) => {\n usageListeners.add(fn)\n return () => {\n usageListeners.delete(fn)\n }\n}\n\n// Non-force calls made while an identical one is already in flight join it\n// instead of firing a second request \u2014 the case that matters is the\n// preload kicked off at plugin load (see index.tsx) landing moments before\n// the widget's or the dashboard's own mount-time fetch. `force` (the\n// refresh button) always fires its own request, since the user asked for a\n// bypass specifically.\nlet balanceInflight: Promise<BalanceResponse> | null = null\nlet usageInflight: Promise<UsageResponse> | null = null\n\nexport async function fetchBalance(force = false): Promise<BalanceResponse> {\n if (!force) {\n const hit = balanceCache.getFresh()\n if (hit !== null) return hit\n if (balanceInflight !== null) return balanceInflight\n }\n const suffix = force ? '?refresh=1' : ''\n const task = getJson<BalanceResponse>(`/api/dsh-usage-dashboard/balance${suffix}`, force ? 'no-store' : 'default')\n .then(res => {\n if (res.ok) balanceCache.put(res)\n return res\n })\n if (!force) {\n balanceInflight = task.finally(() => { balanceInflight = null })\n return balanceInflight\n }\n return task\n}\n\nexport async function fetchUsage(force = false): Promise<UsageResponse> {\n if (!force) {\n const hit = usageCache.getFresh()\n if (hit !== null) return hit\n if (usageInflight !== null) return usageInflight\n }\n const suffix = force ? '?refresh=1' : ''\n // `cache: 'no-store'` on every path, not just force: the host memoizes the\n // expensive replay anyway, and our own TTL cache prevents repeat fetches \u2014\n // but the browser's HTTP cache would happily serve a payload captured before\n // a host upgrade (the old body lacks fields the current UI reads, e.g. the\n // vision block) for up to the max-age it was tagged with.\n const task = getJson<UsageResponse>(`/api/dsh-usage-dashboard/usage${suffix}`, 'no-store')\n .then(res => {\n // Only shapes the current UI dereferences may enter the cache. A host\n // still serving a pre-vision payload (not yet restarted) is therefore\n // never persisted \u2014 the dashboard renders it defensively instead of\n // crashing on the next mount.\n if (res.ok && usageIsUsable(res)) {\n usageCache.put(res)\n for (const listener of [...usageListeners]) listener()\n }\n return res\n })\n if (!force) {\n usageInflight = task.finally(() => { usageInflight = null })\n return usageInflight\n }\n return task\n}\n\n/**\n * Session usage stays memory-only: the widget, dashboard, and every visible\n * turn-cost cell share the latest read without persisting one entry per\n * session. A newly completed turn whose message id is absent from this cache\n * triggers one fresh read; the in-flight map coalesces all cells mounting in\n * the same transcript render.\n */\nconst sessionUsageInflight = new Map<string, Promise<SessionUsageResponse>>()\nconst sessionUsageCache = new Map<string, SessionUsageResponse>()\nconst sessionUsageListeners = new Map<string, Set<() => void>>()\n\nexport const getCachedSessionUsage = (sessionId: string): SessionUsageResponse | null =>\n sessionUsageCache.get(sessionId) ?? null\n\nexport const subscribeSessionUsage = (sessionId: string, fn: () => void): (() => void) => {\n let listeners = sessionUsageListeners.get(sessionId)\n if (listeners === undefined) {\n listeners = new Set()\n sessionUsageListeners.set(sessionId, listeners)\n }\n listeners.add(fn)\n return () => {\n listeners?.delete(fn)\n if (listeners?.size === 0) sessionUsageListeners.delete(sessionId)\n }\n}\n\nconst publishSessionUsage = (sessionId: string, response: SessionUsageResponse): void => {\n if (!response.ok) return\n sessionUsageCache.set(sessionId, response)\n for (const listener of [...(sessionUsageListeners.get(sessionId) ?? [])]) listener()\n}\n\nexport async function fetchSessionUsage(sessionId: string, force = false): Promise<SessionUsageResponse> {\n if (typeof sessionId !== 'string' || sessionId === '') {\n return { ok: false, error: '\u7F3A\u5C11\u4F1A\u8BDD id' }\n }\n if (!force) {\n const inflight = sessionUsageInflight.get(sessionId)\n if (inflight !== undefined) return inflight\n }\n const task = getJson<SessionUsageResponse>(`/api/dsh-usage-dashboard/session?id=${encodeURIComponent(sessionId)}`, 'no-store')\n .then(response => {\n publishSessionUsage(sessionId, response)\n return response\n })\n if (force) return task\n const tracked = task.finally(() => {\n if (sessionUsageInflight.get(sessionId) === tracked) sessionUsageInflight.delete(sessionId)\n })\n sessionUsageInflight.set(sessionId, tracked)\n return tracked\n}\n", "/** The \u300C\u989D\u5EA6\u300D conversation view tab: full balance & usage dashboard.\n *\n * Caching: the first render comes straight from the package-local cache\n * (see ./api.ts), so reopening the tab shows data instantly. A background\n * refresh then updates it without blocking; a full-screen \u52A0\u8F7D\u4E2D only\n * appears when nothing has ever been cached.\n */\nimport { Fragment, useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type ReactElement, type ReactNode } from 'react'\nimport { USAGE_WINDOW_DAYS } from '../contract.ts'\nimport { fetchBalance, fetchSessionUsage, fetchUsage, getCachedBalance, getCachedUsage, getCachedUsageAt } from './api.ts'\nimport { budgetSnapshot } from './budget.ts'\nimport { Bars, GroupedBars, Heatmap, MODEL_COLORS, fmt, fmtCompact, fmtInt } from './charts.tsx'\nimport { getComposerElement, getComposerInputElement, getShellFrame } from './dom.ts'\nimport { dailyUsageCsv, downloadText, exportDateStamp, fullUsageJson, modelUsageCsv } from './export.ts'\nimport { syncStatusText, type SyncState } from './freshness.ts'\nimport { fallbackT, localizeApiError, useI18n, type Translate } from './i18n.tsx'\nimport { CHART_METRICS, chartMetricName, chartMetricValue, type ChartMetric } from './metric.ts'\nimport type { BalanceData, ModelUsage, PeakSplit, PeriodUsage, PricingInfo, SessionCost, SessionUsageData, UsageCoverage, UsageData, UsageWindowDays, VisionDayPoint, VisionSession, VisionStats } from '../contract.ts'\nimport { chartMetricStore, lowBalanceStore, monthlyBudgetStore, quotaViewActiveStore, usageWindowStore, widgetTabIdsStore, widgetVisibleStore } from './store.ts'\nimport type { ConversationViewTab, ConversationViewsSource } from './views.ts'\n\n/** `<package name> v<version>`, injected by build.mjs from package.json so the\n * footer on the \u989D\u5EA6 page reports exactly the version that was built. */\ndeclare const __PLUGIN_LABEL__: string\n\n/** Fallback composer height for the narrow-screen bottom safe area, used only\n * until the real measurement below lands (or if the host markup ever stops\n * exposing the `conversation.composer.dock` slot). DSH has no `--dsw-*`\n * variable for composer height \u2014 this number is not a guess: it is the\n * measured `[data-slot=\"conversation.composer.dock\"]` seat height on a live\n * DSH instance at 620 / 480 / 390px viewports (126px, stable across all\n * three; see LEARNINGS.md). Kept in sync with the small margin added in\n * styles.ts's `.dq-balance` narrow media query. */\nconst COMPOSER_FALLBACK_PX = 126\n\n/** One shimmering placeholder block, sized by the caller. */\nfunction Skel(props: { w: number; h: number }): ReactElement {\n return <div className=\"dq-skel\" style={{ width: `${props.w}px`, height: `${props.h}px` }} />\n}\n\n/** Placeholder for the balance card while the first fetch is in flight. */\nfunction BalanceSkeleton(): ReactElement {\n return (\n <div className=\"dq-balance-grid\">\n <div className=\"dq-stat\"><Skel w={56} h={11} /><Skel w={150} h={22} /></div>\n <div className=\"dq-stat\"><Skel w={28} h={11} /><Skel w={48} h={22} /></div>\n </div>\n )\n}\n\n/** Placeholder for the current-session card: same three-`Stat` grammar as\n * the balance card's skeleton above, just one more column. */\nfunction SessionUsageSkeleton(): ReactElement {\n return (\n <div className=\"dq-balance-grid\">\n <div className=\"dq-stat\"><Skel w={64} h={11} /><Skel w={96} h={18} /></div>\n <div className=\"dq-stat\"><Skel w={70} h={11} /><Skel w={80} h={18} /></div>\n <div className=\"dq-stat\"><Skel w={64} h={11} /><Skel w={40} h={18} /></div>\n </div>\n )\n}\n\n/** Placeholder for the usage trend card: replaying every session log takes\n * seconds, and an empty-state string there would read as \"you have no usage\". */\nfunction UsageSkeleton(): ReactElement {\n return (\n <>\n <div className=\"dq-usage-totals\">\n {[44, 44, 62, 62].map((w, i) => (\n <div key={i} className=\"dq-stat\"><Skel w={w} h={11} /><Skel w={w > 70 ? 84 : 56} h={20} /></div>\n ))}\n </div>\n <Skel w={140} h={12} />\n <div className=\"dq-skel-bars\">\n {Array.from({ length: 30 }, (_, i) => (\n <div key={i} className=\"dq-skel dq-skel-bar\" style={{ height: `${18 + ((i * 37) % 82)}px` }} />\n ))}\n </div>\n </>\n )\n}\n\n/** Placeholder for the heatmap card, same loading grammar as the trend card's\n * bar skeleton above \u2014 the heatmap has no separate fetch, it just renders\n * once `usage` lands, so the two cards share one loading vocabulary. */\nfunction HeatmapSkeleton(): ReactElement {\n return (\n <>\n <Skel w={140} h={12} />\n <div className=\"dq-skel-bars\" style={{ height: '88px' }}>\n {Array.from({ length: 52 }, (_, i) => (\n <div key={i} className=\"dq-skel dq-skel-bar\" style={{ height: `${14 + ((i * 29) % 60)}px` }} />\n ))}\n </div>\n </>\n )\n}\n\n/** Percentage chip comparing a window against the one before it. */\nfunction Delta(props: { current: number; previous: number; label: string }): ReactElement | null {\n const { t } = useI18n()\n const { current, previous, label } = props\n if (previous <= 0) {\n if (current <= 0) return null\n return <span className=\"dq-delta dq-delta--new\" title={t('delta.noBaselineTitle', { label })}>{t('delta.noBaseline')}</span>\n }\n const percent = Math.round(((current - previous) / previous) * 100)\n const title = `${label} \u00A5${fmt(previous)}`\n if (percent === 0) return <span className=\"dq-delta dq-delta--flat\" title={title}>{t('delta.flat')}</span>\n const up = percent > 0\n return (\n <span className={`dq-delta ${up ? 'dq-delta--up' : 'dq-delta--down'}`} title={title}>\n {up ? '\u2191' : '\u2193'}{Math.abs(percent)}% <span className=\"dq-delta-label\">{label}</span>\n </span>\n )\n}\n\n/** One window of the \u6D88\u8017\u6982\u89C8 card: cost headline, volume underneath. */\nfunction Period(props: {\n label: string\n period: PeriodUsage\n previous?: PeriodUsage\n compare?: string\n}): ReactElement {\n const { t, locale } = useI18n()\n const { period, previous, compare } = props\n return (\n <div className=\"dq-period\">\n <div className=\"dq-period-head\">\n <span className=\"dq-period-label\">{props.label}</span>\n {previous !== undefined && compare !== undefined && (\n <Delta current={period.cost} previous={previous.cost} label={compare} />\n )}\n </div>\n <div className=\"dq-period-cost\">\u00A5 {fmt(period.cost)}</div>\n <div className=\"dq-period-sub\">{t('period.volume', {\n tokens: fmtCompact(period.total, locale),\n calls: t('common.calls', { count: fmtInt(period.calls) }),\n })}</div>\n </div>\n )\n}\n\n/** Monthly guardrail, kept inside the overview rather than adding another card. */\nfunction BudgetMeter(props: { spent: number; budget: number; onConfigure: () => void }): ReactElement {\n const { t } = useI18n()\n if (props.budget <= 0) {\n return (\n <div className=\"dq-budget dq-budget--unset\">\n <span>{t('budget.unset')}</span>\n <button type=\"button\" className=\"dq-budget-action\" onClick={props.onConfigure}>{t('budget.configure')}</button>\n </div>\n )\n }\n\n const snapshot = budgetSnapshot(props.spent, props.budget)\n const usedPercent = snapshot.ratio * 100\n const barPercent = Math.min(100, usedPercent)\n const forecastText = snapshot.status === 'over'\n ? t('budget.over', { amount: fmt(Math.abs(snapshot.remaining)) })\n : snapshot.forecastOver > 0\n ? t('budget.forecastOver', { amount: fmt(snapshot.forecastOver) })\n : t('budget.forecast', { amount: fmt(snapshot.forecast) })\n\n return (\n <div className={`dq-budget dq-budget--${snapshot.status}`}>\n <div className=\"dq-budget-head\">\n <span className=\"dq-budget-title\">{t('budget.title')}</span>\n <span className=\"dq-budget-amount\">\u00A5 {fmt(snapshot.spent)} / \u00A5 {fmt(snapshot.budget)}</span>\n </div>\n <div\n className=\"dq-budget-track\"\n role=\"progressbar\"\n aria-label={t('budget.progressLabel')}\n aria-valuemin={0}\n aria-valuemax={snapshot.budget}\n aria-valuenow={Math.min(snapshot.spent, snapshot.budget)}\n aria-valuetext={t('budget.used', { percent: usedPercent.toFixed(1), forecast: forecastText })}\n >\n <div className=\"dq-budget-fill\" style={{ transform: `scaleX(${barPercent / 100})` }} />\n </div>\n <div className=\"dq-budget-meta\">\n <span>{snapshot.remaining >= 0\n ? t('budget.remaining', { amount: fmt(snapshot.remaining) })\n : t('budget.exceeded', { amount: fmt(Math.abs(snapshot.remaining)) })}</span>\n <span>{forecastText}</span>\n </div>\n </div>\n )\n}\n\nconst rate = (value: number): string => `\u00A5${value.toLocaleString(undefined, { maximumFractionDigits: 3 })}`\n\n/** Auditable breakdown of the rates the cost estimate was computed with \u2014\n * a `<details>` so it is keyboard-reachable without any extra state. */\nfunction PricingNote(props: { pricing: PricingInfo }): ReactElement {\n const { t } = useI18n()\n const { pricing } = props\n return (\n <details className=\"dq-pricing\">\n <summary className=\"dq-pricing-summary\">\n {t('pricing.title')}\n <span className={`dq-pricing-now${pricing.inPeakNow ? ' dq-pricing-now--peak' : ''}`}>\n {pricing.inPeakNow ? t('pricing.currentPeak') : t('pricing.currentOffPeak')}\n </span>\n </summary>\n <div className=\"dq-pricing-body\">\n <table className=\"dq-pricing-table\">\n <thead>\n <tr>\n <th>{t('pricing.model')}</th>\n <th>{t('pricing.period')}</th>\n <th>{t('pricing.inputCacheHit')}</th>\n <th>{t('pricing.inputCacheMiss')}</th>\n <th>{t('pricing.output')}</th>\n </tr>\n </thead>\n <tbody>\n {pricing.tiers.map(tier => (\n <Fragment key={tier.model}>\n <tr>\n <td rowSpan={2}>{tier.model}</td>\n <td>{t('pricing.peak')}</td>\n <td>{rate(tier.peak.cacheHit)}</td>\n <td>{rate(tier.peak.input)}</td>\n <td>{rate(tier.peak.output)}</td>\n </tr>\n <tr>\n <td>{t('pricing.offPeak')}</td>\n <td>{rate(tier.offPeak.cacheHit)}</td>\n <td>{rate(tier.offPeak.input)}</td>\n <td>{rate(tier.offPeak.output)}</td>\n </tr>\n </Fragment>\n ))}\n </tbody>\n </table>\n <p className=\"dq-pricing-foot\">\n {t('pricing.unit', { currency: pricing.currency })}\n {t('pricing.splitNote', { windows: pricing.peakWindows.join(t('common.listSeparator')) })}\n {t('pricing.visionNote')}\n {t('pricing.modelNote')}\n {t('pricing.unknown')}\n </p>\n </div>\n </details>\n )\n}\n\n/** Make a locally-computed bill honest about which logs it could inspect. */\nfunction CoverageDiagnostics(props: { coverage: UsageCoverage }): ReactElement {\n const { t, locale } = useI18n()\n const { coverage } = props\n const hasGaps = coverage.failedSessions > 0\n || coverage.skippedRecords > 0\n || coverage.scannedSessions < coverage.listedSessions\n const coverageTime = new Intl.DateTimeFormat(locale === 'en' ? 'en-US' : 'zh-CN', {\n timeZone: 'Asia/Shanghai', year: 'numeric', month: 'numeric', day: 'numeric',\n hour: '2-digit', minute: '2-digit', hour12: false,\n })\n const range = coverage.earliestAt === null || coverage.latestAt === null\n ? t('coverage.noRange')\n : `${coverageTime.format(coverage.earliestAt)} \u2013 ${coverageTime.format(coverage.latestAt)}`\n\n return (\n <details className={`dq-coverage${hasGaps ? ' dq-coverage--warn' : ''}`}>\n <summary className=\"dq-coverage-summary\">\n <span className=\"dq-coverage-title\">{t('coverage.title')}</span>\n <span className=\"dq-coverage-status\">{hasGaps ? t('coverage.gaps') : t('coverage.complete')}</span>\n <span className=\"dq-coverage-brief\">{t('coverage.brief', {\n scanned: fmtInt(coverage.scannedSessions), listed: fmtInt(coverage.listedSessions), records: fmtInt(coverage.usageRecords),\n })}</span>\n </summary>\n <div className=\"dq-coverage-body\">\n <dl className=\"dq-coverage-metrics\">\n <div><dt>{t('coverage.scanned')}</dt><dd>{t('common.sessions', { count: fmtInt(coverage.scannedSessions) })}</dd></div>\n <div><dt>{t('coverage.failed')}</dt><dd>{t('common.sessions', { count: fmtInt(coverage.failedSessions) })}</dd></div>\n <div><dt>{t('coverage.skipped')}</dt><dd>{t('common.records', { count: fmtInt(coverage.skippedRecords) })}</dd></div>\n </dl>\n <p className=\"dq-coverage-range\">{t('coverage.time', { range })}</p>\n <p className=\"dq-coverage-note\">{t('coverage.scope')}</p>\n {hasGaps && (\n <p className=\"dq-coverage-warning\">{t('coverage.warning')}</p>\n )}\n </div>\n </details>\n )\n}\n\nfunction Stat(props: { label: string; children: ReactNode }): ReactElement {\n return (\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{props.label}</div>\n {props.children}\n </div>\n )\n}\n\nfunction Link(props: { href: string; children: ReactNode }): ReactElement {\n return (\n <a className=\"dq-link\" href={props.href} target=\"_blank\" rel=\"noreferrer\">{props.children}</a>\n )\n}\n\n/** Download menu kept compact in the usage card header. */\nfunction ExportMenu(props: { usage: UsageData }): ReactElement {\n const { t } = useI18n()\n const [open, setOpen] = useState(false)\n const [done, setDone] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const triggerRef = useRef<HTMLButtonElement | null>(null)\n const menuRef = useRef<HTMLDivElement | null>(null)\n const doneTimerRef = useRef<number | null>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current !== null && !rootRef.current.contains(event.target as Node)) setOpen(false)\n }\n const onKey = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n setOpen(false)\n triggerRef.current?.focus()\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n window.requestAnimationFrame(() => menuRef.current?.querySelector<HTMLButtonElement>('button')?.focus())\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n useEffect(() => () => {\n if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current)\n }, [])\n\n const finish = (): void => {\n setOpen(false)\n setDone(true)\n if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current)\n doneTimerRef.current = window.setTimeout(() => setDone(false), 1400)\n }\n\n const exportDaily = (): void => {\n const stamp = exportDateStamp()\n downloadText(`dsh-usage-${stamp}.csv`, dailyUsageCsv(props.usage), 'text/csv;charset=utf-8')\n finish()\n }\n const exportModels = (): void => {\n const stamp = exportDateStamp()\n downloadText(`dsh-usage-models-${stamp}.csv`, modelUsageCsv(props.usage), 'text/csv;charset=utf-8')\n finish()\n }\n const exportJson = (): void => {\n const stamp = exportDateStamp()\n downloadText(`dsh-usage-${stamp}.json`, fullUsageJson(props.usage), 'application/json;charset=utf-8')\n finish()\n }\n\n return (\n <div className=\"dq-export\" ref={rootRef}>\n <button\n ref={triggerRef}\n type=\"button\"\n className={`dq-export-btn${done ? ' dq-export-btn--done' : ''}`}\n aria-haspopup=\"true\"\n aria-expanded={open}\n title={t('export.title')}\n onClick={() => setOpen(value => !value)}\n >\n {done ? t('export.done') : t('export.action')}\n </button>\n {open && (\n <div ref={menuRef} className=\"dq-export-menu\" aria-label={t('export.menuLabel')}>\n <button type=\"button\" onClick={exportDaily}>\n <span>{t('export.dailyCsv')}</span><small>{t('common.rows', { count: props.usage.daily.length })}</small>\n </button>\n <button type=\"button\" onClick={exportModels}>\n <span>{t('export.modelCsv')}</span><small>{t('common.rows', { count: props.usage.models.length })}</small>\n </button>\n <button type=\"button\" onClick={exportJson}>\n <span>{t('export.fullJson')}</span><small>{t('export.jsonHint')}</small>\n </button>\n </div>\n )}\n </div>\n )\n}\n\nexport const modelKeyOf = (m: ModelUsage): string => (m.provider !== '' ? `${m.provider}/${m.model}` : m.model)\n\nexport const modelNameOf = (m: ModelUsage, t: Translate = fallbackT): string => (m.model !== '' ? m.model : t('common.unknownModel'))\n\n/**\n * Usage for exactly the session the \u300C\u989D\u5EA6\u300D tab is opened inside \u2014 the one\n * dimension every other card on this page does not answer (they are all\n * account-wide, replayed across every local session log). Deliberately\n * reuses the same `.dq-balance-grid`/`Stat` headline layout as the balance\n * card above it and the same `.dq-rank`/`MODEL_COLORS` breakdown as the\n * model cost ranking card further down, rather than inventing a third\n * visual language for numbers that are structurally the same kind of thing.\n */\nfunction SessionUsageCard(props: { data: SessionUsageData }): ReactElement {\n const { t, locale } = useI18n()\n const { data } = props\n if (data.calls === 0) return <div className=\"dq-empty\">{t('sessionUsage.empty')}</div>\n\n const rangeTime = new Intl.DateTimeFormat(locale === 'en' ? 'en-US' : 'zh-CN', {\n timeZone: 'Asia/Shanghai', year: 'numeric', month: 'numeric', day: 'numeric',\n hour: '2-digit', minute: '2-digit', hour12: false,\n })\n const range = data.firstActive !== null && data.lastActive !== null\n ? (data.firstActive === data.lastActive\n ? rangeTime.format(data.firstActive)\n : `${rangeTime.format(data.firstActive)} \u2013 ${rangeTime.format(data.lastActive)}`)\n : null\n\n return (\n <>\n <div className=\"dq-balance-grid\">\n <Stat label={t('sessionUsage.cost')}><div className=\"dq-stat-value\">\u00A5 {fmt(data.cost)}</div></Stat>\n <Stat label={t('sessionUsage.tokens')}><div className=\"dq-stat-value\">{fmtCompact(data.total, locale)}</div></Stat>\n <Stat label={t('sessionUsage.calls')}><div className=\"dq-stat-value\">{fmtInt(data.calls)}</div></Stat>\n </div>\n {range !== null && <p className=\"dq-session-sub\">{t('sessionUsage.range', { range })}</p>}\n {data.models.length > 0 && (\n <div className=\"dq-rank\">\n {data.models.map((m, index) => {\n const key = m.provider !== '' ? `${m.provider}/${m.model}` : (m.model !== '' ? m.model : `model-${index}`)\n const name = m.model !== '' ? m.model : t('common.unknownModel')\n const color = MODEL_COLORS[index % MODEL_COLORS.length]\n const share = data.cost > 0 ? m.cost / data.cost : 0\n return (\n <div key={key} className=\"dq-rank-row\">\n <div className=\"dq-rank-head\">\n <span className=\"dq-rank-name\" title={key}>\n <span className=\"dq-legend-swatch\" style={{ background: color }} />\n {name}\n </span>\n <span className=\"dq-rank-cost\">\n \u00A5 {fmt(m.cost)}\n <span className=\"dq-rank-share\">{(share * 100).toFixed(1)}%</span>\n </span>\n </div>\n <div\n className=\"dq-rank-track\"\n role=\"img\"\n aria-label={t('models.shareAria', { name, percent: (share * 100).toFixed(1) })}\n >\n <div className=\"dq-rank-fill\" style={{ width: `${Math.max(share * 100, 1.5)}%`, background: color }} />\n </div>\n <div className=\"dq-rank-sub\">\n {t('models.detail', {\n tokens: fmtCompact(m.total, locale),\n calls: t('common.calls', { count: fmtInt(m.calls) }),\n input: fmtCompact(m.input, locale), output: fmtCompact(m.output, locale), cache: fmtCompact(m.cache, locale),\n })}\n </div>\n </div>\n )\n })}\n </div>\n )}\n </>\n )\n}\n\n/**\n * What prefix caching is doing for the bill. Cache-hit tokens are priced at a\n * small fraction of cache-miss tokens, so on a DSH workload this is usually the\n * single biggest lever on cost \u2014 and it was invisible before.\n */\nfunction CacheCard(props: { totals: UsageData['totals'] }): ReactElement {\n const { t, locale } = useI18n()\n const { totals } = props\n const prompt = totals.input + totals.cache\n if (prompt === 0) return <div className=\"dq-empty\">{t('cache.empty')}</div>\n const rate = totals.cache / prompt\n const wouldHaveCost = totals.cost + totals.cacheSavings\n const low = rate < 0.6\n return (\n <div className=\"dq-cache\">\n <div className=\"dq-cache-figures\">\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('cache.rate')}</div>\n <div className={`dq-stat-value${low ? ' dq-stat-value--warn' : ' dq-stat-value--ok'}`}>\n {(rate * 100).toFixed(1)}%\n </div>\n </div>\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('cache.saved')}</div>\n <div className=\"dq-stat-value\">\u00A5 {fmt(totals.cacheSavings)}</div>\n </div>\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('cache.allMiss')}</div>\n <div className=\"dq-stat-value dq-muted-value\">\u00A5 {fmt(wouldHaveCost)}</div>\n </div>\n </div>\n <div className=\"dq-cache-track\" role=\"img\" aria-label={t('cache.aria', { hit: (rate * 100).toFixed(1), miss: ((1 - rate) * 100).toFixed(1) })}>\n <div className=\"dq-cache-fill\" style={{ width: `${rate * 100}%` }} />\n </div>\n <div className=\"dq-cache-legend\">\n <span><span className=\"dq-legend-swatch dq-legend-swatch--hit\" />{t('cache.hitLegend', { tokens: fmtCompact(totals.cache, locale) })}</span>\n <span><span className=\"dq-legend-swatch dq-legend-swatch--miss\" />{t('cache.missLegend', { tokens: fmtCompact(totals.input, locale) })}</span>\n </div>\n <p className=\"dq-cache-foot\">\n {low\n ? t('cache.lowHint')\n : t('cache.goodHint')}\n </p>\n </div>\n )\n}\n\n/**\n * Image / multimodal usage: how much of the bill came from pictures. Image\n * blocks are collected from user messages and tool results during the same\n * log replay that feeds the rest of the dashboard (no attachment bytes are\n * read \u2014 the refs already carry width/height), so this dimension costs the\n * replay nothing extra. Tokens are estimated with the official resizing rule\n * and priced at the consuming call's rates; shares are against the window's\n * reported input tokens and cost.\n */\nfunction VisionCard(props: {\n vision: VisionStats\n daily: VisionDayPoint[]\n sessions: VisionSession[]\n windowLabel: string\n inputTokens: number\n costTotal: number\n}): ReactElement {\n const { t, locale } = useI18n()\n const { vision, daily, sessions, windowLabel, inputTokens, costTotal } = props\n if (vision.images === 0) return <div className=\"dq-empty\">{t('vision.empty', { window: windowLabel })}</div>\n const tokensShare = inputTokens > 0 ? (vision.imageTokens / inputTokens) * 100 : 0\n const costShare = costTotal > 0 ? (vision.cost / costTotal) * 100 : 0\n const labelEvery = daily.length <= 7 ? 1 : daily.length <= 30 ? 5 : daily.length <= 90 ? 15 : 30\n const bars = daily.map(d => ({\n label: d.date.slice(8, 10),\n value: d.images,\n title: t('vision.barTitle', { date: d.date, images: fmtInt(d.images), tokens: fmtCompact(d.imageTokens, locale), cost: fmt(d.cost) }),\n }))\n return (\n <>\n <div className=\"dq-usage-totals\">\n <Stat label={t('vision.images')}><div className=\"dq-stat-value\">{fmtInt(vision.images)}</div></Stat>\n <Stat label={t('vision.tokens')}>\n <div className=\"dq-stat-value\">\n {fmtCompact(vision.imageTokens, locale)}\n {tokensShare > 0 && <span className=\"dq-vision-share\">{t('vision.tokensShare', { percent: tokensShare.toFixed(1) })}</span>}\n </div>\n </Stat>\n <Stat label={t('vision.cost')}>\n <div className=\"dq-stat-value\">\n \u00A5 {fmt(vision.cost)}\n {costShare > 0 && <span className=\"dq-vision-share\">{t('vision.costShare', { percent: costShare.toFixed(1) })}</span>}\n </div>\n </Stat>\n </div>\n <div className=\"dq-chart-title\">{t('vision.dailyTitle', { window: windowLabel })}</div>\n <Bars data={bars} height={80} labelEvery={labelEvery} />\n {sessions.length > 0 && (\n <div className=\"dq-vision-sessions\">\n <div className=\"dq-chart-title\">{t('vision.sessionsTitle')}</div>\n {sessions.map(session => (\n <div key={session.id} className=\"dq-session\">\n <div className=\"dq-session-head\">\n <span className=\"dq-session-title\" title={session.id}>{session.title}</span>\n <span className=\"dq-session-cost\">\n {t('vision.sessionRow', { images: fmtInt(session.images), cost: fmt(session.cost) })}\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n <p className=\"dq-cache-foot\">{t('vision.note')}</p>\n </>\n )\n}\n\n/**\n * Peak vs off-peak: which side of DeepSeek's price windows the usage falls on,\n * and what shifting the peak share off-peak would have saved.\n */\nfunction PeakCard(props: { split: PeakSplit; pricing: PricingInfo }): ReactElement {\n const { t, locale } = useI18n()\n const { split, pricing } = props\n const total = split.peak.total + split.offPeak.total\n if (total === 0) return <div className=\"dq-empty\">{t('peak.empty')}</div>\n const peakShare = split.peak.total / total\n const shiftSaving = split.peakEraCost - split.offPeakEraCost\n return (\n <div className=\"dq-peak\">\n <div\n className=\"dq-peak-track\"\n role=\"img\"\n aria-label={t('peak.aria', { peak: (peakShare * 100).toFixed(1), offPeak: ((1 - peakShare) * 100).toFixed(1) })}\n >\n <div className=\"dq-peak-fill\" style={{ width: `${peakShare * 100}%` }} />\n </div>\n <div className=\"dq-peak-legend\">\n <span><span className=\"dq-legend-swatch dq-legend-swatch--peak\" />{t('peak.peakLegend', {\n share: (peakShare * 100).toFixed(1), tokens: fmtCompact(split.peak.total, locale), calls: t('common.callsShort', { count: fmtInt(split.peak.calls) }),\n })}</span>\n <span><span className=\"dq-legend-swatch dq-legend-swatch--offpeak\" />{t('peak.offPeakLegend', {\n share: ((1 - peakShare) * 100).toFixed(1), tokens: fmtCompact(split.offPeak.total, locale), calls: t('common.callsShort', { count: fmtInt(split.offPeak.calls) }),\n })}</span>\n </div>\n <div className=\"dq-peak-figures\">\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('peak.shift')}</div>\n <div className=\"dq-stat-value\">\n \u00A5 {fmt(split.offPeakEraCost)}\n <span className=\"dq-peak-delta dq-peak-delta--save\">{t('peak.saving', { amount: fmt(shiftSaving) })}</span>\n </div>\n </div>\n </div>\n <p className=\"dq-peak-foot\">\n {t('peak.schedule', { windows: pricing.peakWindows.join(t('common.listSeparator')) })}\n {t('peak.activeHint')}\n </p>\n </div>\n )\n}\n\n/** Coarse \"how long ago\", enough to tell a live session from last week's. */\nfunction agoText(ms: number, t: Translate): string {\n const minutes = Math.floor((Date.now() - ms) / 60_000)\n if (minutes < 1) return t('common.justNow')\n if (minutes < 60) return t('common.minutesAgo', { count: minutes })\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return t('common.hoursAgo', { count: hours })\n return t('common.daysAgo', { count: Math.floor(hours / 24) })\n}\n\n/** Per-row fetch state for the ranking's on-demand expand \u2014 mirrors the\n * three-way {loading, error, data} shape `loadSessionUsage` already uses\n * for the current-session card, just keyed per row instead of per tab. */\ninterface SessionDetailState {\n error: string | null\n data: SessionUsageData | null\n}\n\n/** Body of one expanded ranking row: same loading/error/data three-way as the\n * current-session card, and the same `SessionUsageCard` rendering the data \u2014\n * this is deliberately not a second implementation of that layout. */\nfunction SessionDetail(props: { state: SessionDetailState | undefined }): ReactElement {\n const { t } = useI18n()\n const { state } = props\n if (state === undefined) return <SessionUsageSkeleton />\n if (state.data !== null) return <SessionUsageCard data={state.data} />\n if (state.error !== null) return <div className=\"dq-empty dq-empty--error\">{localizeApiError(state.error, t, 'error.query')}</div>\n return <SessionUsageSkeleton />\n}\n\n/**\n * Which sessions burned the money. Per-model and per-day views say what and\n * when; this says *which run*, which is the one an agent user can act on \u2014\n * a single expensive session is a prompt or a loop worth looking at.\n *\n * Each row is a native `<details>` (same disclosure grammar as `.dq-pricing`/\n * `.dq-coverage` above \u2014 no custom open/close state machine, keyboard and\n * touch come for free). Opening a row for the first time fires an on-demand\n * `fetchSessionUsage(s.id)` and renders the result with the exact same\n * `SessionUsageCard` the \u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300Dcard uses \u2014 the row only has the\n * summary fields (`SessionCost`), the full per-model breakdown only exists\n * once that fetch lands. `startedRef` guards against re-fetching on a\n * collapse/re-expand: once a row's fetch has been kicked off, later toggles\n * just show whatever is already cached in `details`.\n */\nfunction SessionRanking(props: { sessions: SessionCost[]; count: number; totalCost: number }): ReactElement {\n const { t, locale } = useI18n()\n const { sessions, count, totalCost } = props\n const [details, setDetails] = useState<Record<string, SessionDetailState>>({})\n const startedRef = useRef<Set<string>>(new Set())\n\n const loadDetail = (id: string): void => {\n if (startedRef.current.has(id)) return\n startedRef.current.add(id)\n fetchSessionUsage(id).then(res => {\n setDetails(prev => ({\n ...prev,\n [id]: res.ok && res.data !== undefined\n ? { error: null, data: res.data }\n : { error: res.error ?? null, data: null },\n }))\n }, err => {\n const message = (err as { message?: string } | null)?.message ?? String(err)\n setDetails(prev => ({ ...prev, [id]: { error: message, data: null } }))\n })\n }\n\n if (sessions.length === 0) return <div className=\"dq-empty\">{t('sessions.empty')}</div>\n const top = sessions[0].cost\n return (\n <>\n <ol className=\"dq-sessions\">\n {sessions.map(s => (\n <li key={s.id} className=\"dq-session\">\n <details\n className=\"dq-session-details\"\n onToggle={e => { if (e.currentTarget.open) loadDetail(s.id) }}\n >\n <summary className=\"dq-session-summary\">\n <span className=\"dq-session-chevron\" aria-hidden=\"true\">\u25B8</span>\n <div className=\"dq-session-summary-body\">\n <div className=\"dq-session-head\">\n <span className=\"dq-session-title\" title={`${s.title}\\n${s.id}`}>{s.title || t('sessions.fallbackTitle', { id: s.id })}</span>\n <span className=\"dq-session-cost\">\u00A5 {fmt(s.cost)}</span>\n </div>\n <div className=\"dq-session-track\">\n <div className=\"dq-session-fill\" style={{ width: `${Math.max((s.cost / (top || 1)) * 100, 1.5)}%` }} />\n </div>\n <div className=\"dq-session-sub\">\n {t('sessions.detail', {\n tokens: fmtCompact(s.total, locale),\n calls: t('common.calls', { count: fmtInt(s.calls) }),\n ago: agoText(s.lastActive, t),\n })}\n {totalCost > 0 && <> \u00B7 {t('sessions.share', { percent: ((s.cost / totalCost) * 100).toFixed(1) })}</>}\n </div>\n </div>\n </summary>\n <div className=\"dq-session-expand\">\n <SessionDetail state={details[s.id]} />\n </div>\n </details>\n </li>\n ))}\n </ol>\n {count > sessions.length && (\n <p className=\"dq-session-foot\">{t('sessions.more', { count: fmtInt(count), shown: sessions.length })}</p>\n )}\n </>\n )\n}\n\n/** Which model the money actually goes to, most expensive first. */\nfunction ModelRanking(props: {\n models: ModelUsage[]\n totalCost: number\n colorOf: (key: string) => string\n}): ReactElement {\n const { t, locale } = useI18n()\n const { models, totalCost } = props\n if (models.length === 0) return <div className=\"dq-empty\">{t('models.empty')}</div>\n return (\n <div className=\"dq-rank\">\n {models.map(m => {\n const key = modelKeyOf(m)\n const share = totalCost > 0 ? m.cost / totalCost : 0\n return (\n <div key={key} className=\"dq-rank-row\">\n <div className=\"dq-rank-head\">\n <span className=\"dq-rank-name\" title={key}>\n <span className=\"dq-legend-swatch\" style={{ background: props.colorOf(key) }} />\n {modelNameOf(m, t)}\n </span>\n <span className=\"dq-rank-cost\">\n \u00A5 {fmt(m.cost)}\n <span className=\"dq-rank-share\">{(share * 100).toFixed(1)}%</span>\n </span>\n </div>\n <div\n className=\"dq-rank-track\"\n role=\"img\"\n aria-label={t('models.shareAria', { name: modelNameOf(m, t), percent: (share * 100).toFixed(1) })}\n >\n <div className=\"dq-rank-fill\" style={{ width: `${Math.max(share * 100, 1.5)}%`, background: props.colorOf(key) }} />\n </div>\n <div className=\"dq-rank-sub\">\n {t('models.detail', {\n tokens: fmtCompact(m.total, locale),\n calls: t('common.calls', { count: fmtInt(m.calls) }),\n input: fmtCompact(m.input, locale), output: fmtCompact(m.output, locale), cache: fmtCompact(m.cache, locale),\n })}\n </div>\n </div>\n )\n })}\n </div>\n )\n}\n\n/** Multi-select model dropdown feeding the grouped bar chart. */\nfunction ModelPicker(props: {\n models: ModelUsage[]\n selected: string[]\n colorOf: (key: string) => string\n onChange: (keys: string[]) => void\n}): ReactElement | null {\n const { t, locale } = useI18n()\n const { models } = props\n const [open, setOpen] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const triggerRef = useRef<HTMLButtonElement | null>(null)\n const menuRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent): void => {\n if (rootRef.current !== null && !rootRef.current.contains(e.target as Node)) setOpen(false)\n }\n const onKey = (e: KeyboardEvent): void => {\n if (e.key !== 'Escape') return\n e.preventDefault()\n setOpen(false)\n triggerRef.current?.focus()\n }\n document.addEventListener('mousedown', onDown)\n document.addEventListener('keydown', onKey)\n window.requestAnimationFrame(() => menuRef.current?.querySelector<HTMLInputElement>('input')?.focus())\n return () => {\n document.removeEventListener('mousedown', onDown)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n if (models.length === 0) return null\n\n const toggle = (key: string): void => {\n props.onChange(\n props.selected.includes(key)\n ? props.selected.filter(k => k !== key)\n : [...props.selected, key],\n )\n }\n\n const label = props.selected.length === 0 ? t('models.all') : t('models.selected', { count: props.selected.length })\n\n return (\n <div className=\"dq-model-picker\" ref={rootRef}>\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"dq-model-btn\"\n aria-haspopup=\"true\"\n aria-expanded={open}\n aria-controls=\"dq-model-menu\"\n title={t('models.filterTitle')}\n onClick={() => setOpen(value => !value)}\n >\n {label}\n </button>\n {open && (\n <div id=\"dq-model-menu\" ref={menuRef} className=\"dq-model-menu\" role=\"group\" aria-label={t('models.filterLabel')}>\n <label className=\"dq-model-item dq-model-item--all\">\n <input\n type=\"checkbox\"\n checked={props.selected.length === 0}\n onChange={() => props.onChange([])}\n />\n <span>{t('models.all')}</span>\n </label>\n {models.map(m => {\n const key = modelKeyOf(m)\n return (\n <label key={key} className=\"dq-model-item\">\n <input type=\"checkbox\" checked={props.selected.includes(key)} onChange={() => toggle(key)} />\n <span className=\"dq-legend-swatch\" style={{ background: props.colorOf(key) }} />\n <span>{modelNameOf(m, t)}</span>\n <span className=\"dq-model-tag\">{fmtCompact(m.total, locale)}</span>\n </label>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\nconst windowName = (days: UsageWindowDays, t: Translate = fallbackT): string => days === 365\n ? t('window.year')\n : t('window.days', { days })\n\n/** One range choice controls every metric whose title carries that range. */\nfunction UsageWindowPicker(props: { value: UsageWindowDays; onChange: (days: UsageWindowDays) => void }): ReactElement {\n const { t } = useI18n()\n return (\n <div className=\"dq-window-switch\" role=\"group\" aria-label={t('window.groupLabel')}>\n {USAGE_WINDOW_DAYS.map(days => (\n <button\n key={days}\n type=\"button\"\n className={`dq-window-btn${props.value === days ? ' dq-window-btn--on' : ''}`}\n aria-pressed={props.value === days}\n title={t('window.viewTitle', { window: windowName(days, t) })}\n onClick={() => props.onChange(days)}\n >\n {days === 365 ? t('window.yearButton') : t('window.daysButton', { days })}\n </button>\n ))}\n </div>\n )\n}\n\nconst chartMetricLabel = (metric: ChartMetric, t: Translate = fallbackT): string => metric === 'tokens'\n ? t('metric.tokens')\n : chartMetricName(metric, t)\n\nfunction ChartMetricPicker(props: { value: ChartMetric; onChange: (metric: ChartMetric) => void }): ReactElement {\n const { t } = useI18n()\n return (\n <div className=\"dq-chart-switch\" role=\"group\" aria-label={t('metric.groupLabel')}>\n {CHART_METRICS.map(metric => (\n <button\n key={metric}\n type=\"button\"\n className={`dq-chart-switch-btn${props.value === metric ? ' dq-chart-switch-btn--on' : ''}`}\n aria-pressed={props.value === metric}\n title={t('metric.compareTitle', { metric: chartMetricLabel(metric, t) })}\n onClick={() => props.onChange(metric)}\n >\n {chartMetricLabel(metric, t)}\n </button>\n ))}\n </div>\n )\n}\n\nexport function BalanceDashboard(props: { sessionId?: string; views: ConversationViewsSource }): ReactElement {\n const { t, locale } = useI18n()\n // Seed state from the cache so the tab never waits on the network when\n // data has been fetched before (stale-while-revalidate).\n const cachedBalance = getCachedBalance()\n const cachedUsage = getCachedUsage()\n const cachedUsageAt = getCachedUsageAt()\n const [balance, setBalance] = useState<BalanceData | null>(cachedBalance?.data ?? null)\n const [usage, setUsage] = useState<UsageData | null>(cachedUsage?.data ?? null)\n const [error, setError] = useState<string | null>(null)\n const [notice, setNotice] = useState<{ kind: 'all' | 'balance' | 'usage'; error: string } | null>(null)\n // Balance answers in well under a second, usage replays every session log and\n // takes seconds \u2014 so they get their own loading flags and paint independently\n // instead of the fast one waiting on the slow one.\n const [loadingBalance, setLoadingBalance] = useState(cachedBalance === null)\n const [loadingUsage, setLoadingUsage] = useState(cachedUsage === null)\n const [refreshing, setRefreshing] = useState(false)\n const [usageUpdatedAt, setUsageUpdatedAt] = useState<number | null>(cachedUsageAt)\n const [syncState, setSyncState] = useState<SyncState>(cachedUsageAt === null ? 'syncing' : 'cached')\n const [freshnessNow, setFreshnessNow] = useState(Date.now())\n const hadDataRef = useRef(cachedBalance !== null || cachedUsage !== null)\n const [widgetOn, setWidgetOn] = useState(widgetVisibleStore.get())\n const [widgetTabIds, setWidgetTabIds] = useState<string[] | null>(widgetTabIdsStore.get())\n const [viewTabs, setViewTabs] = useState<ConversationViewTab[]>(() => props.views.list())\n useEffect(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views])\n const [lowBalance, setLowBalance] = useState(lowBalanceStore.get())\n const [monthlyBudget, setMonthlyBudget] = useState(monthlyBudgetStore.get())\n const [barMode, setBarMode] = useState<'daily' | 'hourly'>('daily')\n const [selectedModels, setSelectedModels] = useState<string[]>([])\n const [windowDays, setWindowDays] = useState<UsageWindowDays>(usageWindowStore.get())\n const [chartMetric, setChartMetric] = useState<ChartMetric>(chartMetricStore.get())\n // \"\u9884\u8BA1\u53EF\u7528\" hover preview: an unopened <details> skips rendering its\n // non-summary children in Chromium (a content-visibility-like platform\n // behavior), so CSS-only `:hover{display:block}` never paints anything\n // when the element isn't actually open. Drive `open` from state instead \u2014\n // hovering opens it for a live preview; a manual click/keyboard/touch open\n // keeps it open independent of hover.\n const [runwayHovering, setRunwayHovering] = useState(false)\n const [runwayManuallyOpened, setRunwayManuallyOpened] = useState(false)\n // \u8F6E\u6B21 47: same platform limitation applies to \"\u4F59\u989D\u660E\u7EC6\" \u2014 it used the same\n // CSS-only `:hover{display:flex}` on an uncontrolled <details> since round 29,\n // which never actually painted in current Chromium either. Same fix, same shape.\n const [remainingHovering, setRemainingHovering] = useState(false)\n const [remainingManuallyOpened, setRemainingManuallyOpened] = useState(false)\n const budgetInputRef = useRef<HTMLInputElement | null>(null)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const [composerHeight, setComposerHeight] = useState<number | null>(null)\n // The composer's measured width caps the dashboard column: on narrower\n // viewports the fixed 860px max-width would otherwise overhang the input\n // box the cards visually belong to (user-facing parity requirement).\n const [composerWidth, setComposerWidth] = useState<number | null>(null)\n // The current-session card: fetched per session id, not through the\n // account-wide balance/usage caches (see DESIGN_NOTES \u2014 session-scoped\n // data does not go through the localStorage-persisted cache). No cached\n // seed on first render since there is nothing to seed from.\n const [sessionUsage, setSessionUsage] = useState<SessionUsageData | null>(null)\n const [loadingSessionUsage, setLoadingSessionUsage] = useState(props.sessionId !== undefined && props.sessionId !== '')\n const [sessionUsageError, setSessionUsageError] = useState<string | null>(null)\n const sessionUsageIdRef = useRef<string | null>(null)\n\n // Narrow viewports only: keep the last card scrollable clear of the host's\n // fixed composer, which otherwise overlaps the bottom of a long dashboard\n // (see LEARNINGS.md). Reuses the same `conversation.composer.dock` slot\n // widget.tsx already excludes from its own drag bounds, so both readings of\n // \"where is the composer\" stay in sync. Desktop layout never reads the\n // height \u2014 the CSS variable is only consumed inside the 620px media query;\n // the width is consumed by .dq-balance at every viewport.\n useLayoutEffect(() => {\n const node = rootRef.current\n if (node === null) return\n const measure = (): void => {\n const frame = getShellFrame(node)\n const composer = getComposerElement(frame)\n const rect = composer?.getBoundingClientRect() ?? null\n const nextHeight = rect !== null && rect.height > 0 ? Math.round(rect.height) : null\n setComposerHeight(prev => (prev === nextHeight ? prev : nextHeight))\n // Width parity uses the composer's input card (the rounded panel with\n // the textarea), not the dock seat \u2014 the seat spans the whole pane, so\n // it can never narrow the dashboard column below 860px.\n const input = getComposerInputElement(frame)\n const inputRect = input?.getBoundingClientRect() ?? null\n const nextWidth = inputRect !== null && inputRect.width > 0 ? Math.round(inputRect.width) : null\n setComposerWidth(prev => (prev === nextWidth ? prev : nextWidth))\n }\n measure()\n const frame = getShellFrame(node)\n const composer = getComposerElement(frame)\n const input = getComposerInputElement(frame)\n let observer: ResizeObserver | null = null\n if (typeof ResizeObserver === 'function') {\n observer = new ResizeObserver(measure)\n if (composer !== null) observer.observe(composer)\n if (input !== null) observer.observe(input)\n if (frame !== null) observer.observe(frame)\n }\n window.addEventListener('resize', measure)\n return () => {\n observer?.disconnect()\n window.removeEventListener('resize', measure)\n }\n }, [])\n\n // All conversation view tabs (Chat / Trajectory / \u989D\u5EA6 \u2026) share one host\n // scrollport (`[data-conversation-scroll]`), and the host keeps scroll\n // memory only for Chat itself \u2014 switching to \u989D\u5EA6 from a scrolled-down Chat\n // otherwise opens the tab near the settings card instead of at the account\n // balance. Reset before paint on every mount; the view is remounted per tab\n // switch, and `sessionId` re-runs it if the host reuses the view for\n // another session.\n useLayoutEffect(() => {\n const node = rootRef.current\n if (node === null) return\n const scrollport = node.closest('[data-conversation-scroll]')\n if (scrollport instanceof HTMLElement) scrollport.scrollTop = 0\n }, [props.sessionId])\n\n // The full dashboard and the floating summary should never compete for the\n // same pixels. This does not touch the persisted widget preference: leaving\n // the tab restores it exactly as the user left it.\n useLayoutEffect(() => {\n quotaViewActiveStore.set(true)\n return () => quotaViewActiveStore.set(false)\n }, [])\n\n const errText = (err: unknown): string => (err as { message?: string } | null)?.message ?? String(err)\n\n /** Load both resources; each resolves to null on success or to its error\n * string on failure, so a partial failure can name the half that broke. */\n const load = async (force: boolean): Promise<void> => {\n const usageAtBefore = getCachedUsageAt()\n setError(null)\n setNotice(null)\n setRefreshing(true)\n setSyncState('syncing')\n const balanceTask = fetchBalance(force).then(res => {\n if (res.ok && res.data !== undefined) {\n setBalance(res.data)\n hadDataRef.current = true\n return null\n }\n return res.error ?? '\u4F59\u989D\u52A0\u8F7D\u5931\u8D25'\n }, err => errText(err)).then(result => {\n setLoadingBalance(false)\n return result\n })\n const usageTask = fetchUsage(force).then(res => {\n if (res.ok && res.data !== undefined) {\n setUsage(res.data)\n const updatedAt = getCachedUsageAt()\n setUsageUpdatedAt(updatedAt)\n setFreshnessNow(Date.now())\n setSyncState(updatedAt !== null && updatedAt !== usageAtBefore ? 'fresh' : 'cached')\n hadDataRef.current = true\n return null\n }\n return res.error ?? '\u7528\u91CF\u52A0\u8F7D\u5931\u8D25'\n }, err => errText(err)).then(result => {\n setLoadingUsage(false)\n return result\n })\n const [balanceError, usageError] = await Promise.all([balanceTask, usageTask])\n setRefreshing(false)\n if (usageError !== null) {\n const fallbackAt = getCachedUsageAt()\n setUsageUpdatedAt(fallbackAt)\n setFreshnessNow(Date.now())\n setSyncState(fallbackAt === null ? 'error' : 'fallback')\n }\n if (balanceError !== null && usageError !== null) {\n // Nothing came back at all: an error only when there is nothing to show.\n if (hadDataRef.current) setNotice({ kind: 'all', error: balanceError })\n else setError(balanceError)\n } else if (balanceError !== null) {\n setNotice({ kind: 'balance', error: balanceError })\n } else if (usageError !== null) {\n setNotice({ kind: 'usage', error: usageError })\n }\n // The current-session card rides along with an explicit force refresh so\n // the one refresh button covers all three data sources; the initial\n // mount-time fetch is handled separately below, keyed on the session id\n // itself, so it is not duplicated here (force=false would otherwise fire\n // twice on first render \u2014 once from this effect, once from that one).\n if (force && props.sessionId !== undefined && props.sessionId !== '') {\n void loadSessionUsage(props.sessionId, true)\n }\n }\n\n /** Stale-while-revalidate for the current-session card: on success, swap\n * in the new data; on failure, keep whatever is already on screen (a\n * refresh going bad should not blank out a card that was working). Guards\n * every state write against the session id having changed mid-flight, so\n * a slow request for a session the user has since navigated away from\n * cannot clobber the next one's numbers. */\n const loadSessionUsage = async (id: string, force: boolean): Promise<void> => {\n if (!force) setLoadingSessionUsage(true)\n try {\n const res = await fetchSessionUsage(id, force)\n if (sessionUsageIdRef.current !== id) return\n if (res.ok && res.data !== undefined) {\n setSessionUsage(res.data)\n setSessionUsageError(null)\n } else {\n setSessionUsageError(res.error ?? null)\n }\n } catch (err) {\n if (sessionUsageIdRef.current === id) setSessionUsageError(errText(err))\n } finally {\n if (sessionUsageIdRef.current === id) setLoadingSessionUsage(false)\n }\n }\n\n useEffect(() => {\n void load(false)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Mount-time (and session-switch-time) fetch for the current-session card.\n // Independent of the balance/usage effect above: this data is scoped to\n // whichever session the tab happens to be open inside, not to the account.\n useEffect(() => {\n const id = props.sessionId\n sessionUsageIdRef.current = id ?? null\n if (id === undefined || id === '') {\n setSessionUsage(null)\n setSessionUsageError(null)\n setLoadingSessionUsage(false)\n return\n }\n setSessionUsage(null)\n setSessionUsageError(null)\n setLoadingSessionUsage(true)\n void loadSessionUsage(id, false)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.sessionId])\n\n useEffect(() => {\n const timer = window.setInterval(() => setFreshnessNow(Date.now()), 30_000)\n return () => window.clearInterval(timer)\n }, [])\n\n const toggle = (): void => {\n const next = !widgetOn\n setWidgetOn(next)\n widgetVisibleStore.set(next)\n }\n\n const widgetTabs = viewTabs.filter(tab => tab.id !== 'balance')\n const toggleWidgetTab = (id: string): void => {\n const selected = widgetTabIds ?? widgetTabs.map(tab => tab.id)\n const next = selected.includes(id) ? selected.filter(tabId => tabId !== id) : [...selected, id]\n setWidgetTabIds(next)\n widgetTabIdsStore.set(next)\n }\n\n const changeLowBalance = (value: number): void => {\n setLowBalance(value)\n lowBalanceStore.set(value)\n }\n\n const changeMonthlyBudget = (value: number): void => {\n setMonthlyBudget(value)\n monthlyBudgetStore.set(value)\n }\n\n const changeWindowDays = (value: UsageWindowDays): void => {\n setWindowDays(value)\n usageWindowStore.set(value)\n }\n\n const changeChartMetric = (value: ChartMetric): void => {\n setChartMetric(value)\n chartMetricStore.set(value)\n }\n\n const primary = balance !== null && balance.balances.length > 0 ? balance.balances[0] : null\n\n // Runway: balance divided by the last 7 calendar days' average spend. Idle\n // days are included on purpose \u2014 that is the burn rate, not the busy-day rate.\n const recentDays = (usage?.daily ?? []).slice(-7)\n const avgDailyCost = recentDays.length > 0\n ? recentDays.reduce((sum, d) => sum + d.cost, 0) / recentDays.length\n : 0\n const balanceValue = primary !== null ? Number(primary.total) : Number.NaN\n // 0 disables the warning; an unreadable balance never triggers it.\n const balanceLow = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance\n const daysLeft = avgDailyCost > 0 && Number.isFinite(balanceValue) ? balanceValue / avgDailyCost : null\n const daysLeftText = daysLeft === null\n ? '\u2014'\n : daysLeft >= 365 ? t('balance.overYear') : t('balance.days', { count: daysLeft < 10 ? daysLeft.toFixed(1) : Math.round(daysLeft) })\n const runwayTitle = daysLeft === null\n ? t('balance.noRunway', { days: recentDays.length || 7 })\n : t('balance.runwayTitle', { days: recentDays.length, cost: fmt(avgDailyCost) })\n\n const activeWindow = usage?.windows.find(window => window.days === windowDays) ?? null\n const activeWindowName = windowName(windowDays, t)\n\n const dailyBars = (activeWindow?.daily ?? []).map(d => ({\n label: windowDays === 365 ? d.date.slice(5).replace('-', '/') : d.date.slice(8, 10),\n value: chartMetricValue(chartMetric, d),\n title: t('chart.tooltip', {\n head: d.date, tokens: fmtInt(d.total), cost: fmt(d.cost), calls: t('common.calls', { count: d.calls }),\n }),\n }))\n const hourlyBars = (activeWindow?.hourly ?? []).map(h => ({\n label: String(h.hour),\n value: chartMetricValue(chartMetric, h),\n title: t('chart.tooltip', {\n head: t('chart.hourPoint', { hour: h.hour }), tokens: fmtInt(h.total), cost: fmt(h.cost), calls: t('common.calls', { count: h.calls }),\n }),\n }))\n\n // One canonical model order \u2014 most expensive first \u2014 shared by the ranking\n // card, the picker and the chart, so a model keeps its colour everywhere\n // instead of being coloured by the order it happened to be ticked in.\n // Tallest bar currently on screen \u2014 the chart has no y axis, so this is the\n // only thing giving the bars a magnitude.\n const chartPeak = (barMode === 'daily' ? dailyBars : hourlyBars)\n .reduce((peak, bar) => (bar.value > peak ? bar.value : peak), 0)\n const chartPeakText = chartMetric === 'cost'\n ? `\u00A5${fmt(chartPeak)}`\n : chartMetric === 'calls'\n ? t('common.callsShort', { count: fmtInt(chartPeak) })\n : `${fmtCompact(chartPeak, locale)} tokens`\n\n const modelList = [...(activeWindow?.models ?? [])].sort((a, b) => b.cost - a.cost)\n const canonicalModels = [...(usage?.models ?? [])].sort((a, b) => b.cost - a.cost)\n const availableModelKeys = new Set(modelList.map(modelKeyOf))\n const effectiveSelection = selectedModels.filter(k => availableModelKeys.has(k))\n const colorOf = (key: string): string => {\n const index = canonicalModels.findIndex(m => modelKeyOf(m) === key)\n return MODEL_COLORS[(index < 0 ? 0 : index) % MODEL_COLORS.length]\n }\n const modelCostTotal = modelList.reduce((sum, m) => sum + m.cost, 0)\n\n // Per-model series for the grouped bar chart (only when models are selected).\n const grouped = effectiveSelection.map(key => {\n const m = modelList.find(x => modelKeyOf(x) === key)\n const name = m === undefined ? key : modelNameOf(m, t)\n const color = colorOf(key)\n if (barMode === 'daily') {\n return {\n key,\n name,\n color,\n bars: (m?.daily ?? []).map((p, i) => ({\n label: windowDays === 365\n ? ((activeWindow?.daily ?? [])[i]?.date.slice(5).replace('-', '/') ?? '')\n : ((activeWindow?.daily ?? [])[i]?.date.slice(8, 10) ?? ''),\n value: chartMetricValue(chartMetric, p),\n title: t('chart.tooltip', {\n head: `${name} \u00B7 ${(activeWindow?.daily ?? [])[i]?.date ?? ''}`,\n tokens: fmtInt(p.total), cost: fmt(p.cost), calls: t('common.calls', { count: p.calls }),\n }),\n })),\n }\n }\n return {\n key,\n name,\n color,\n bars: (m?.hourly ?? []).map((p, i) => ({\n label: String(i),\n value: chartMetricValue(chartMetric, p),\n title: t('chart.tooltip', {\n head: `${name} \u00B7 ${t('chart.hourPoint', { hour: i })}`,\n tokens: fmtInt(p.total), cost: fmt(p.cost), calls: t('common.calls', { count: p.calls }),\n }),\n })),\n }\n })\n const dailyLabelEvery = windowDays === 7 ? 1 : windowDays === 30 ? 5 : windowDays === 90 ? 15 : 30\n const dailyMinWidth = windowDays === 365 ? 1825 : undefined\n const noticeText = notice === null ? null : t(\n notice.kind === 'all'\n ? 'error.refreshCached'\n : notice.kind === 'balance' ? 'error.balanceRefreshCached' : 'error.usageRefreshCached',\n { error: localizeApiError(notice.error, t, 'error.query') },\n )\n const errorText = error === null ? null : localizeApiError(error, t, 'error.query')\n\n const composerSafeAreaStyle = {\n '--dq-composer-h': `${composerHeight ?? COMPOSER_FALLBACK_PX}px`,\n // Only set once measured; .dq-balance's max-width falls back to 860px\n // without it (pre-measurement render keeps the settled wide-viewport\n // column, then narrows in place \u2014 no flash of the old width).\n ...(composerWidth !== null ? { '--dq-composer-w': `${composerWidth}px` } : {}),\n } as CSSProperties\n\n return (\n <div className=\"dq-balance\" ref={rootRef} style={composerSafeAreaStyle}>\n <div className=\"dq-status-row\">\n <div className=\"dq-status-messages\">\n <span\n className={`dq-sync dq-sync--${syncState}`}\n role=\"status\"\n aria-live=\"polite\"\n title={usageUpdatedAt === null\n ? t('status.neverSynced')\n : t('status.usageTime', { time: new Date(usageUpdatedAt).toLocaleString(locale === 'en' ? 'en-US' : 'zh-CN', { timeZone: 'Asia/Shanghai' }) })}\n >\n <span className=\"dq-sync-dot\" aria-hidden=\"true\" />\n {syncStatusText(syncState, usageUpdatedAt, freshnessNow, t)}\n </span>\n {noticeText !== null && <span className=\"dq-warn\">{noticeText}</span>}\n {errorText !== null && <span className=\"dq-error\">{errorText}</span>}\n </div>\n <button\n type=\"button\"\n className=\"dq-refresh-btn\"\n title={t('status.forceRefresh')}\n disabled={refreshing}\n aria-busy={refreshing}\n onClick={() => { void load(true) }}\n >\n <span className={`dq-refresh-icon${refreshing ? ' dq-refresh-icon--spin' : ''}`}>\u21BB</span>\n {refreshing ? t('status.refreshing') : t('status.refresh')}\n </button>\n </div>\n\n {balanceLow && (\n <div className=\"dq-alert\" role=\"status\">\n <span className=\"dq-alert-icon\" aria-hidden=\"true\">!</span>\n <div>\n <strong>{t('alert.lowTitle', { balance: fmt(balanceValue), currency: primary?.currency, threshold: fmt(lowBalance) })}</strong>\n {daysLeft !== null && t('alert.runway', { days: daysLeftText })}{t('common.period')}\n <a className=\"dq-alert-link\" href=\"https://platform.deepseek.com/top_up\" target=\"_blank\" rel=\"noreferrer\">{t('alert.topUp')}</a>\n </div>\n </div>\n )}\n\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-title\">{t('balance.title')}</div>\n {primary !== null ? (\n <div className=\"dq-balance-grid\">\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('balance.remaining')}</div>\n <details\n className=\"dq-stat-value dq-remaining\"\n open={remainingHovering || remainingManuallyOpened}\n onPointerEnter={() => setRemainingHovering(true)}\n onPointerLeave={() => setRemainingHovering(false)}\n >\n <summary\n title={t('balance.breakdownTitle')}\n onClick={e => {\n // Own the toggle fully instead of the native default action: a controlled\n // <details> can be forced open by hover, and a plain native toggle here\n // would fight that (e.g. it flips to \"closed\" while the mouse is still\n // hovering). Preventing default and flipping our own \"manually opened\"\n // state keeps hover-preview and click/keyboard/tap-to-pin independent.\n e.preventDefault()\n setRemainingManuallyOpened(v => !v)\n }}\n >\n {fmt(primary.total)} {primary.currency}\n </summary>\n <div className=\"dq-remaining-breakdown\">\n <span>{t('balance.toppedUp', { amount: fmt(primary.toppedUp) })}</span>\n <span className=\"dq-remaining-granted\">{t('balance.granted', { amount: fmt(primary.granted) })}</span>\n </div>\n </details>\n </div>\n <div className=\"dq-stat\">\n <div className=\"dq-stat-label\">{t('balance.runway')}</div>\n <details\n className={`dq-stat-value dq-runway${daysLeft !== null && daysLeft < 3 ? ' dq-stat-value--bad' : ''}`}\n open={runwayHovering || runwayManuallyOpened}\n onPointerEnter={() => setRunwayHovering(true)}\n onPointerLeave={() => setRunwayHovering(false)}\n >\n <summary\n title={t('balance.runwayBreakdownTitle')}\n onClick={e => {\n // Own the toggle fully instead of the native default action: a controlled\n // <details> can be forced open by hover, and a plain native toggle here\n // would fight that (e.g. it flips to \"closed\" while the mouse is still\n // hovering). Preventing default and flipping our own \"manually opened\"\n // state keeps hover-preview and click/keyboard/tap-to-pin independent.\n e.preventDefault()\n setRunwayManuallyOpened(v => !v)\n }}\n >\n {daysLeftText}\n </summary>\n <div className=\"dq-runway-detail\">{runwayTitle}</div>\n </details>\n </div>\n <Stat label={t('common.status')}>\n <div className={`dq-stat-value${balance?.isAvailable === false ? ' dq-stat-value--bad' : ' dq-stat-value--ok'}`}>\n {balance?.isAvailable === false ? t('common.unavailable') : t('common.available')}\n </div>\n </Stat>\n </div>\n ) : loadingBalance ? (\n <BalanceSkeleton />\n ) : (\n <div className=\"dq-empty\">{t('balance.empty')}</div>\n )}\n </div>\n\n {props.sessionId !== undefined && props.sessionId !== '' && (\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-title\">{t('sessionUsage.title')}</div>\n {sessionUsage !== null ? (\n <SessionUsageCard data={sessionUsage} />\n ) : loadingSessionUsage ? (\n <SessionUsageSkeleton />\n ) : sessionUsageError !== null ? (\n <div className=\"dq-empty dq-empty--error\">{localizeApiError(sessionUsageError, t, 'error.query')}</div>\n ) : (\n <div className=\"dq-empty\">{t('sessionUsage.empty')}</div>\n )}\n </div>\n )}\n\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-title\">{t('overview.title')}</div>\n {usage !== null ? (\n <div className=\"dq-period-grid\">\n <Period label={t('overview.today')} period={usage.summary.today} previous={usage.summary.yesterday} compare={t('overview.vsYesterday')} />\n <Period label={t('overview.month')} period={usage.summary.month} previous={usage.summary.lastMonthToDate} compare={t('overview.vsLastMonth')} />\n <Period\n label={t('overview.total')}\n period={{ total: usage.totals.total, cost: usage.totals.cost, calls: usage.totals.calls }}\n />\n </div>\n ) : loadingUsage ? (\n\n <div className=\"dq-period-grid\">\n {[0, 1, 2].map(i => (\n <div key={i} className=\"dq-period\">\n <Skel w={40} h={11} /><Skel w={104} h={24} /><Skel w={132} h={11} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('overview.empty')}</div>\n )}\n {usage !== null && (\n <BudgetMeter\n spent={usage.summary.month.cost}\n budget={monthlyBudget}\n onConfigure={() => budgetInputRef.current?.focus()}\n />\n )}\n {usage !== null && <PricingNote pricing={usage.pricing} />}\n </div>\n\n {usage !== null && activeWindow !== null && activeWindow.vision !== undefined\n && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions) ? (\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('vision.title')}</div>\n <VisionCard\n vision={activeWindow.vision}\n daily={activeWindow.visionDaily}\n sessions={activeWindow.visionSessions}\n windowLabel={activeWindowName}\n inputTokens={activeWindow.totals.input}\n costTotal={activeWindow.totals.cost}\n />\n </div>\n ) : usage === null ? (\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('vision.title')}</div>\n {loadingUsage ? (\n <div className=\"dq-usage-totals\">\n {[64, 88, 72].map((w, i) => (\n <div key={i} className=\"dq-stat\"><Skel w={w} h={11} /><Skel w={72} h={20} /></div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('vision.empty', { window: activeWindowName })}</div>\n )}\n </div>\n ) : (\n // Host serving a pre-vision payload (e.g. not restarted yet): hide the\n // whole card instead of leaving a titled shell \u2014 it reappears as soon\n // as the host half serves the new shape.\n null\n )}\n\n <div className=\"dq-card dq-card--primary\">\n <div className=\"dq-card-head\">\n <div className=\"dq-card-title\">{t('usage.trendTitle')}</div>\n {usage !== null && (\n <div className=\"dq-card-actions\">\n <UsageWindowPicker value={windowDays} onChange={changeWindowDays} />\n <ExportMenu usage={usage} />\n </div>\n )}\n </div>\n {usage !== null && activeWindow !== null ? (\n <>\n <div className=\"dq-usage-totals\">\n <Stat label={t('usage.windowInput', { window: activeWindowName })}><div className=\"dq-stat-value\">{fmtCompact(activeWindow.totals.input, locale)}</div></Stat>\n <Stat label={t('common.output')}><div className=\"dq-stat-value\">{fmtCompact(activeWindow.totals.output, locale)}</div></Stat>\n <Stat label={t('common.cacheHit')}><div className=\"dq-stat-value\">{fmtCompact(activeWindow.totals.cache, locale)}</div></Stat>\n <Stat label={t('usage.modelCalls')}><div className=\"dq-stat-value\">{fmtInt(activeWindow.totals.calls)}</div></Stat>\n </div>\n <div className=\"dq-chart-block-head\">\n <div className=\"dq-chart-title\">\n {barMode === 'daily'\n ? t('chart.dailyTitle', { window: activeWindowName, metric: chartMetricName(chartMetric, t) })\n : t('chart.hourlyTitle', { window: activeWindowName, metric: chartMetricName(chartMetric, t) })}\n {chartPeak > 0 && <span className=\"dq-chart-peak\">{t('chart.peak', { value: chartPeakText })}</span>}\n </div>\n <div className=\"dq-chart-controls\">\n <ModelPicker models={modelList} selected={effectiveSelection} colorOf={colorOf} onChange={setSelectedModels} />\n <ChartMetricPicker value={chartMetric} onChange={changeChartMetric} />\n <div className=\"dq-chart-switch\" role=\"tablist\" aria-label={t('chart.dimensionLabel')}>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={barMode === 'daily'}\n className={`dq-chart-switch-btn${barMode === 'daily' ? ' dq-chart-switch-btn--on' : ''}`}\n onClick={() => setBarMode('daily')}\n >\n {t('chart.daily')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={barMode === 'hourly'}\n className={`dq-chart-switch-btn${barMode === 'hourly' ? ' dq-chart-switch-btn--on' : ''}`}\n onClick={() => setBarMode('hourly')}\n >\n {t('chart.hourly')}\n </button>\n </div>\n </div>\n </div>\n {activeWindow.totals.calls === 0 ? (\n <div className=\"dq-empty\">{t('chart.noWindowData', { window: activeWindowName })}</div>\n ) : grouped.length > 0 ? (\n <>\n <GroupedBars\n series={grouped}\n height={barMode === 'daily' ? 120 : 100}\n labelEvery={barMode === 'daily' ? dailyLabelEvery : 3}\n minWidth={barMode === 'daily' ? dailyMinWidth : undefined}\n />\n <div className=\"dq-legend\">\n {grouped.map(s => (\n <span key={s.key} className=\"dq-legend-item\">\n <span className=\"dq-legend-swatch\" style={{ background: s.color }} />\n {s.name}\n </span>\n ))}\n </div>\n </>\n ) : barMode === 'daily' ? (\n <Bars data={dailyBars} height={120} labelEvery={dailyLabelEvery} minWidth={dailyMinWidth} />\n ) : (\n <Bars data={hourlyBars} height={100} labelEvery={3} />\n )}\n <CoverageDiagnostics coverage={usage.coverage} />\n </>\n ) : loadingUsage ? (\n <UsageSkeleton />\n ) : (\n <div className=\"dq-empty\">{t('chart.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('heatmap.title')}</div>\n {usage !== null ? (\n <>\n <div className=\"dq-chart-title\">{t('chart.heatmapTitle')}</div>\n <Heatmap data={usage.heatmap} />\n </>\n ) : loadingUsage ? (\n <HeatmapSkeleton />\n ) : (\n <div className=\"dq-empty\">{t('chart.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('peak.title')}</div>\n {usage !== null ? (\n <PeakCard split={usage.peakSplit} pricing={usage.pricing} />\n ) : loadingUsage ? (\n <>\n <div className=\"dq-peak-track\" />\n <div className=\"dq-peak-figures\"><Skel w={120} h={11} /><Skel w={96} h={20} /></div>\n </>\n ) : (\n <div className=\"dq-empty\">{t('peak.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('cache.title')}</div>\n {usage !== null ? (\n <CacheCard totals={usage.totals} />\n ) : loadingUsage ? (\n <div className=\"dq-cache-figures\">\n {[60, 96, 72].map((w, i) => (\n <div key={i} className=\"dq-stat\"><Skel w={w} h={11} /><Skel w={72} h={20} /></div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('cache.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('models.title', { window: activeWindowName })}</div>\n {activeWindow !== null ? (\n <ModelRanking models={modelList} totalCost={modelCostTotal} colorOf={colorOf} />\n ) : loadingUsage ? (\n <div className=\"dq-rank\">\n {[0, 1].map(i => (\n <div key={i} className=\"dq-rank-row\">\n <Skel w={148} h={13} />\n <div className=\"dq-rank-track\" />\n <Skel w={230} h={11} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('models.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('sessions.title', { window: activeWindowName })}</div>\n {activeWindow !== null ? (\n <SessionRanking sessions={activeWindow.sessions} count={activeWindow.sessionCount} totalCost={activeWindow.totals.cost} />\n ) : loadingUsage ? (\n <div className=\"dq-sessions\">\n {[0, 1, 2].map(i => (\n <div key={i} className=\"dq-session\">\n <Skel w={200} h={13} />\n <div className=\"dq-session-track\" />\n <Skel w={250} h={11} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"dq-empty\">{t('sessions.empty')}</div>\n )}\n </div>\n\n <div className=\"dq-card\">\n <div className=\"dq-card-title\">{t('settings.title')}</div>\n <div className=\"dq-links-title\">{t('links.title')}</div>\n <div className=\"dq-links\">\n <Link href=\"https://platform.deepseek.com/usage\">{t('links.usage')}</Link>\n <Link href=\"https://platform.deepseek.com/api_keys\">{t('links.apiKey')}</Link>\n <Link href=\"https://status.deepseek.com\">{t('links.status')}</Link>\n </div>\n <label className=\"dq-toggle\">\n <input type=\"checkbox\" checked={widgetOn} onChange={toggle} />\n <span>{t('settings.widget')}</span>\n </label>\n <div className=\"dq-toggle-hint\">{t('settings.widgetHint')}</div>\n <div className=\"dq-setting dq-setting--tabs\">\n <div className=\"dq-setting-label\" id=\"dq-widget-tabs-label\">{t('settings.widgetTabs')}</div>\n <div className=\"dq-setting-control dq-widget-tabs\" role=\"group\" aria-labelledby=\"dq-widget-tabs-label\">\n <div className=\"dq-widget-tab-list\">\n {widgetTabs.map(tab => (\n <label className=\"dq-widget-tab\" key={tab.id}>\n <input\n type=\"checkbox\"\n checked={widgetTabIds === null || widgetTabIds.includes(tab.id)}\n onChange={() => toggleWidgetTab(tab.id)}\n />\n <span>{tab.label}</span>\n </label>\n ))}\n </div>\n <span className=\"dq-setting-hint\">\n {widgetTabs.length === 0 ? t('settings.widgetTabsEmpty') : t('settings.widgetTabsHint')}\n </span>\n </div>\n </div>\n <div className=\"dq-setting\">\n <label className=\"dq-setting-label\" htmlFor=\"dq-low-balance\">{t('settings.lowBalance')}</label>\n <div className=\"dq-setting-control\">\n <input\n id=\"dq-low-balance\"\n className=\"dq-number\"\n type=\"number\"\n min={0}\n step={1}\n value={lowBalance}\n onChange={e => changeLowBalance(Math.max(0, Number(e.target.value) || 0))}\n />\n <span className=\"dq-setting-hint\">\n {lowBalance > 0\n ? t('settings.lowBalanceOn', { amount: fmt(lowBalance), currency: primary?.currency ?? 'CNY' })\n : t('settings.lowBalanceOff')}\n </span>\n </div>\n </div>\n <div className=\"dq-setting\">\n <label className=\"dq-setting-label\" htmlFor=\"dq-monthly-budget\">{t('settings.monthlyBudget')}</label>\n <div className=\"dq-setting-control\">\n <input\n ref={budgetInputRef}\n id=\"dq-monthly-budget\"\n className=\"dq-number\"\n type=\"number\"\n min={0}\n step={10}\n value={monthlyBudget}\n aria-describedby=\"dq-monthly-budget-hint\"\n onChange={e => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))}\n />\n <span id=\"dq-monthly-budget-hint\" className=\"dq-setting-hint\">\n {monthlyBudget > 0\n ? t('settings.monthlyBudgetOn', { amount: fmt(monthlyBudget) })\n : t('settings.monthlyBudgetOff')}\n </span>\n </div>\n </div>\n </div>\n\n <p className=\"dq-footer\">{__PLUGIN_LABEL__}</p>\n </div>\n )\n}\n", "export type BudgetStatus = 'healthy' | 'risk' | 'over'\n\nexport interface BudgetSnapshot {\n spent: number\n budget: number\n ratio: number\n remaining: number\n forecast: number\n forecastOver: number\n status: BudgetStatus\n}\n\nconst BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000\n\n/**\n * Project the current month's cost to month end. DeepSeek's billing boundary\n * is Beijing time, so this stays deterministic even when the browser runs in\n * another timezone.\n */\nexport function budgetSnapshot(spentValue: number, budgetValue: number, nowMs = Date.now()): BudgetSnapshot {\n const spent = Number.isFinite(spentValue) ? Math.max(0, spentValue) : 0\n const budget = Number.isFinite(budgetValue) ? Math.max(0, budgetValue) : 0\n const beijingNow = new Date(nowMs + BEIJING_OFFSET_MS)\n const year = beijingNow.getUTCFullYear()\n const month = beijingNow.getUTCMonth()\n const start = Date.UTC(year, month, 1) - BEIJING_OFFSET_MS\n const end = Date.UTC(year, month + 1, 1) - BEIJING_OFFSET_MS\n const elapsedRatio = Math.min(1, Math.max(1 / (end - start), (nowMs - start) / (end - start)))\n const forecast = spent / elapsedRatio\n const ratio = budget > 0 ? spent / budget : 0\n const remaining = budget - spent\n const forecastOver = budget > 0 ? Math.max(0, forecast - budget) : 0\n const status: BudgetStatus = ratio >= 1\n ? 'over'\n : (ratio >= 0.8 || forecastOver > 0 ? 'risk' : 'healthy')\n\n return { spent, budget, ratio, remaining, forecast, forecastOver, status }\n}\n", "/** Dependency-free bar chart and heatmap primitives. */\nimport { useEffect, useRef, useState, type FocusEvent as ReactFocusEvent, type KeyboardEvent as ReactKeyboardEvent, type MutableRefObject, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react'\nimport { isTapGesture, lastPopulatedIndex, maxOrOne, nextChartFocus, nextPinnedIndex, stackedTotals, type ChartFocusKey } from './chart-focus.ts'\nimport { useI18n, type LocaleId } from './i18n.tsx'\nimport type { LocaleKey } from './locales.ts'\n\nexport interface BarDatum {\n label: string\n value: number\n /** Tooltip text, composed as `heading \u00B7 row \u00B7 row`; each ` \u00B7 ` segment is\n * rendered on its own line, the first as the heading. */\n title?: string\n}\n\ninterface TipState {\n x: number\n y: number\n lines: string[]\n above: boolean\n}\n\n/**\n * Cursor-following tooltip shared by every chart. The native `title` attribute\n * this replaces waited about a second, could not be styled to match the shell,\n * and never appeared on touch.\n *\n * Touch has no hover, so a tap pins the tooltip open instead of showing it only\n * while the finger is down (which produced the original one-flicker-then-gone\n * complaint). `pinned` and `hide()` share one `pinnedRef` so a `touchend`\n * followed synchronously by `pointerleave` \u2014 both native events fired in the\n * same tick, before React re-renders \u2014 never reads a stale \"nothing pinned\"\n * state and hides the tooltip it was just asked to pin.\n */\nfunction useChartTip(): {\n wrapRef: MutableRefObject<HTMLDivElement | null>\n tip: TipState | null\n pinned: number | null\n show: (e: ReactPointerEvent<HTMLElement>, text: string | undefined) => void\n showFocused: (e: ReactFocusEvent<HTMLElement>, text: string | undefined) => void\n hide: () => void\n onPointStart: (e: ReactPointerEvent<HTMLElement>) => void\n onPointEnd: (e: ReactPointerEvent<HTMLElement>, index: number, text: string | undefined) => void\n} {\n const wrapRef = useRef<HTMLDivElement | null>(null)\n const [tip, setTip] = useState<TipState | null>(null)\n const [pinned, setPinned] = useState<number | null>(null)\n const pinnedRef = useRef<number | null>(null)\n const touchStart = useRef<{ x: number; y: number; t: number } | null>(null)\n const setPin = (next: number | null): void => {\n pinnedRef.current = next\n setPinned(next)\n }\n const showAt = (target: HTMLElement, text: string | undefined, clientX?: number, clientY?: number): void => {\n const wrap = wrapRef.current\n if (text === undefined || text === '' || wrap === null) return\n const rect = wrap.getBoundingClientRect()\n const targetRect = target.getBoundingClientRect()\n // Keep the (centred) bubble inside the chart's own box.\n const x = Math.min(Math.max((clientX ?? targetRect.left + targetRect.width / 2) - rect.left, 70), Math.max(rect.width - 70, 70))\n const viewportY = clientY ?? targetRect.top\n const y = viewportY - rect.top\n setTip({ x, y, lines: text.split(' \u00B7 '), above: viewportY > window.innerHeight - 160 })\n }\n const show = (e: ReactPointerEvent<HTMLElement>, text: string | undefined): void => {\n // Touch has no hover; a bare `pointermove` while a finger drags across the\n // chart (e.g. scrolling the page) would otherwise flash tooltips at every\n // point it crosses. Touch only opens a tooltip via the deliberate tap\n // handled in `onPointEnd`.\n if (e.pointerType === 'touch') return\n showAt(e.currentTarget, text, e.clientX, e.clientY)\n }\n const showFocused = (e: ReactFocusEvent<HTMLElement>, text: string | undefined): void => {\n showAt(e.currentTarget, text)\n }\n const hide = (): void => {\n if (pinnedRef.current !== null) return\n setTip(null)\n }\n const onPointStart = (e: ReactPointerEvent<HTMLElement>): void => {\n if (e.pointerType === 'touch') touchStart.current = { x: e.clientX, y: e.clientY, t: Date.now() }\n }\n const onPointEnd = (e: ReactPointerEvent<HTMLElement>, index: number, text: string | undefined): void => {\n if (e.pointerType !== 'touch') return\n const start = touchStart.current\n touchStart.current = null\n if (start === null || !isTapGesture(e.clientX - start.x, e.clientY - start.y, Date.now() - start.t)) return\n const next = nextPinnedIndex(pinnedRef.current, index)\n setPin(next)\n if (next === null) setTip(null)\n else showAt(e.currentTarget, text, e.clientX, e.clientY)\n }\n useEffect(() => {\n if (pinned === null) return undefined\n const onDocPointerDown = (e: PointerEvent): void => {\n const wrap = wrapRef.current\n if (wrap !== null && e.target instanceof Node && wrap.contains(e.target)) return\n setPin(null)\n setTip(null)\n }\n document.addEventListener('pointerdown', onDocPointerDown)\n return () => document.removeEventListener('pointerdown', onDocPointerDown)\n }, [pinned])\n return { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd }\n}\n\nfunction useRovingChartPoints(count: number, initial: number, horizontalStep = 1): {\n active: number\n refs: MutableRefObject<Array<HTMLElement | null>>\n onKeyDown: (event: ReactKeyboardEvent<HTMLElement>, index: number) => void\n} {\n const refs = useRef<Array<HTMLElement | null>>([])\n const [active, setActive] = useState(initial)\n useEffect(() => setActive(Math.min(Math.max(0, initial), Math.max(0, count - 1))), [count, initial])\n const onKeyDown = (event: ReactKeyboardEvent<HTMLElement>, index: number): void => {\n if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) return\n event.preventDefault()\n const next = nextChartFocus(index, count, event.key as ChartFocusKey, horizontalStep)\n setActive(next)\n const target = refs.current[next]\n target?.focus({ preventScroll: true })\n target?.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }\n return { active, refs, onKeyDown }\n}\n\nfunction ChartTip(props: { tip: TipState | null }): ReactElement | null {\n const { tip } = props\n if (tip === null) return null\n const [head, ...rows] = tip.lines\n return (\n <div className={`dq-tip${tip.above ? ' dq-tip--above' : ''}`} style={{ left: `${tip.x}px`, top: `${tip.y}px` }} role=\"tooltip\">\n <div className=\"dq-tip-head\">{head}</div>\n {rows.map((row, i) => <div key={i} className=\"dq-tip-row\">{row}</div>)}\n </div>\n )\n}\n\n/** Color palette for per-model grouped bars, cycled by model order. */\nexport const MODEL_COLORS = ['#4176e6', '#2da44e', '#e16f24', '#8250df', '#bf8700', '#cf222e', '#1f883d', '#0969da']\n\nexport function Bars(props: { data: BarDatum[]; height?: number; labelEvery?: number; minWidth?: number }): ReactElement {\n const { data, height = 120, labelEvery = 1, minWidth } = props\n const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip()\n const scrollRef = useRef<HTMLDivElement | null>(null)\n const initialFocus = lastPopulatedIndex(data.map(datum => datum.value))\n const roving = useRovingChartPoints(data.length, initialFocus)\n useEffect(() => {\n const scroll = scrollRef.current\n if (scroll !== null && minWidth !== undefined) scroll.scrollLeft = scroll.scrollWidth\n }, [data.length, minWidth])\n let max = 0\n for (const datum of data) if (datum.value > max) max = datum.value\n const scale = max > 0 ? max : 1\n return (\n <div className=\"dq-chart-wrap\" ref={wrapRef} onPointerLeave={hide}>\n <div className=\"dq-bars-scroll\" ref={scrollRef}>\n <div className=\"dq-bars\" style={{ height: `${height + 18}px`, minWidth: minWidth === undefined ? undefined : `${minWidth}px` }}>\n {data.map((datum, i) => {\n const h = Math.max(1, Math.round((datum.value / scale) * height))\n const text = datum.title ?? `${datum.label}: ${datum.value.toLocaleString()}`\n return (\n <div\n key={i}\n className={`dq-bar-col${pinned === i ? ' dq-bar-col--pinned' : ''}`}\n role=\"img\"\n aria-label={text}\n aria-keyshortcuts=\"ArrowLeft ArrowRight ArrowUp ArrowDown Home End\"\n tabIndex={i === roving.active ? 0 : -1}\n ref={element => { roving.refs.current[i] = element }}\n onPointerMove={e => show(e, text)}\n onPointerDown={onPointStart}\n onPointerUp={e => onPointEnd(e, i, text)}\n onFocus={e => showFocused(e, text)}\n onBlur={hide}\n onKeyDown={e => roving.onKeyDown(e, i)}\n >\n <div className=\"dq-bar\" style={{ height: `${h}px` }} />\n <div className=\"dq-bar-label\" style={{ visibility: i % labelEvery === 0 ? 'visible' : 'hidden' }}>{datum.label}</div>\n </div>\n )\n })}\n </div>\n </div>\n <ChartTip tip={tip} />\n </div>\n )\n}\n\nexport interface HeatDatum {\n date: string\n total: number\n cost: number\n calls: number\n}\n\n/**\n * Multi-series bar chart: one bar per time slot, each series' value stacked\n * inside it (series[0] at the bottom, later series stacked above, same order\n * in every column). Segment height is scaled against the largest *per-slot\n * total* across all slots (not the largest single value) so the stacked\n * height reflects that slot's combined usage and columns stay comparable to\n * each other. Each segment stays its own focusable/hoverable/tappable point \u2014\n * `pointIndex = i * series.length + seriesIndex` is unchanged from the old\n * side-by-side layout, so `chart-focus.ts`'s roving tabindex (plain \u00B11 /\n * Home / End, no 2-D semantics) needs no changes: arrow keys still move one\n * point at a time, they just now walk bottom-to-top through one column's\n * models before advancing to the next column, instead of left-to-right\n * through side-by-side bars.\n */\nexport function GroupedBars(props: {\n series: Array<{ key: string; color: string; bars: BarDatum[] }>\n height?: number\n labelEvery?: number\n minWidth?: number\n}): ReactElement {\n const { series, height = 120, labelEvery = 1, minWidth } = props\n const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip()\n const scrollRef = useRef<HTMLDivElement | null>(null)\n const count = series.length > 0 ? series[0].bars.length : 0\n const pointValues = Array.from({ length: count }, (_, i) => series.map(s => s.bars[i]?.value ?? 0)).flat()\n const roving = useRovingChartPoints(pointValues.length, lastPopulatedIndex(pointValues))\n useEffect(() => {\n const scroll = scrollRef.current\n if (scroll !== null && minWidth !== undefined) scroll.scrollLeft = scroll.scrollWidth\n }, [count, minWidth])\n const scale = maxOrOne(stackedTotals(series, count))\n return (\n <div className=\"dq-chart-wrap\" ref={wrapRef} onPointerLeave={hide}>\n <div className=\"dq-bars-scroll\" ref={scrollRef}>\n <div className=\"dq-bars\" style={{ height: `${height + 18}px`, minWidth: minWidth === undefined ? undefined : `${minWidth}px` }}>\n {Array.from({ length: count }, (_, i) => (\n <div\n key={i}\n className=\"dq-bar-col\"\n >\n <div className=\"dq-bar-group\">\n {series.map((s, seriesIndex) => {\n const datum = s.bars[i]\n const h = Math.max(1, Math.round((datum.value / scale) * height))\n const text = datum.title ?? `${s.key}: ${datum.value.toLocaleString()}`\n const pointIndex = i * series.length + seriesIndex\n return (\n <div\n key={s.key}\n className={`dq-bar${pinned === pointIndex ? ' dq-bar--pinned' : ''}`}\n style={{ height: `${h}px`, background: s.color }}\n role=\"img\"\n aria-label={text}\n aria-keyshortcuts=\"ArrowLeft ArrowRight ArrowUp ArrowDown Home End\"\n tabIndex={pointIndex === roving.active ? 0 : -1}\n ref={element => { roving.refs.current[pointIndex] = element }}\n onPointerMove={e => show(e, text)}\n onPointerDown={onPointStart}\n onPointerUp={e => onPointEnd(e, pointIndex, text)}\n onFocus={e => showFocused(e, text)}\n onBlur={hide}\n onKeyDown={e => roving.onKeyDown(e, pointIndex)}\n />\n )\n })}\n </div>\n <div className=\"dq-bar-label\" style={{ visibility: i % labelEvery === 0 ? 'visible' : 'hidden' }}>\n {series[0].bars[i].label}\n </div>\n </div>\n ))}\n </div>\n </div>\n <ChartTip tip={tip} />\n </div>\n )\n}\n\nfunction heatColor(level: number): string {\n if (level <= 0) return 'rgba(120,130,150,0.12)'\n if (level === 1) return 'rgba(65,118,230,0.25)'\n if (level === 2) return 'rgba(65,118,230,0.5)'\n if (level === 3) return 'rgba(65,118,230,0.75)'\n return 'rgba(65,118,230,1)'\n}\n\n/**\n * Calendar heatmap: one flex column per week, cells filling the card's full\n * width (rather than a fixed pixel size hugging the left edge), month labels\n * below the grid and a \u5C11\u2192\u591A scale legend \u2014 modelled on Codex's activity\n * graph. No weekday row: at this cell count a day is identified by hovering\n * it, not by which row it sits in.\n */\nexport function Heatmap(props: { data: HeatDatum[] }): ReactElement {\n const { data } = props\n const { t, locale } = useI18n()\n const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip()\n const roving = useRovingChartPoints(data.length, lastPopulatedIndex(data.map(datum => datum.total)), 7)\n let max = 0\n for (const datum of data) if (datum.total > max) max = datum.total\n let firstDow = 0\n if (data.length > 0) {\n const dt = new Date(`${data[0].date}T00:00:00`)\n firstDow = Number.isNaN(dt.getTime()) ? 0 : dt.getDay()\n }\n const cells: Array<HeatDatum | null> = []\n for (let i = 0; i < firstDow; i++) cells.push(null)\n for (const datum of data) cells.push(datum)\n while (cells.length % 7 !== 0) cells.push(null)\n\n const weeks: Array<Array<HeatDatum | null>> = []\n for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7))\n\n // Label a column with its month when it is the first column of that month.\n let lastMonth = ''\n const monthLabels = weeks.map(week => {\n const first = week.find(cell => cell !== null)\n if (first === undefined || first === null) return ''\n const month = first.date.slice(0, 7)\n if (month === lastMonth) return ''\n lastMonth = month\n return t(`chart.month${Number(first.date.slice(5, 7))}` as LocaleKey)\n })\n\n const levelOf = (total: number): number => {\n if (total <= 0) return 0\n const r = max > 0 ? total / max : 0\n return r < 0.25 ? 1 : r < 0.5 ? 2 : r < 0.75 ? 3 : 4\n }\n\n return (\n <div className=\"dq-heat dq-chart-wrap\" ref={wrapRef} onPointerLeave={hide}>\n <div className=\"dq-heat-scroll\">\n <div className=\"dq-heatmap\">\n {weeks.map((week, w) => (\n <div key={w} className=\"dq-heat-week\">\n {week.map((datum, d) => {\n if (datum === null) return <div key={d} className=\"dq-heat-cell dq-heat-cell--pad\" />\n const text = t('chart.tooltip', {\n head: datum.date,\n tokens: datum.total.toLocaleString(locale === 'en' ? 'en-US' : 'zh-CN'),\n cost: datum.cost.toFixed(2),\n calls: t('common.calls', { count: datum.calls }),\n })\n const index = w * 7 + d - firstDow\n return (\n <div\n key={d}\n className={`dq-heat-cell${pinned === index ? ' dq-heat-cell--pinned' : ''}`}\n style={{ background: heatColor(levelOf(datum.total)) }}\n role=\"img\"\n aria-label={text}\n aria-keyshortcuts=\"ArrowLeft ArrowRight ArrowUp ArrowDown Home End\"\n tabIndex={index === roving.active ? 0 : -1}\n ref={element => { roving.refs.current[index] = element }}\n onPointerMove={e => show(e, text)}\n onPointerDown={onPointStart}\n onPointerUp={e => onPointEnd(e, index, text)}\n onFocus={e => showFocused(e, text)}\n onBlur={hide}\n onKeyDown={e => roving.onKeyDown(e, index)}\n />\n )\n })}\n </div>\n ))}\n </div>\n <div className=\"dq-heat-months\" aria-hidden=\"true\">\n {monthLabels.map((label, i) => <div key={i} className=\"dq-heat-month\">{label}</div>)}\n </div>\n </div>\n <ChartTip tip={tip} />\n <div className=\"dq-heat-scale\">\n <span>{t('chart.less')}</span>\n {[0, 1, 2, 3, 4].map(level => (\n <span key={level} className=\"dq-heat-key\" style={{ background: heatColor(level) }} />\n ))}\n <span>{t('chart.more')}</span>\n </div>\n </div>\n )\n}\n\nexport const fmt = (v: string | number): string => {\n const n = Number(v)\n if (!Number.isFinite(n)) return String(v)\n return n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })\n}\n\nexport const fmtInt = (v: number | undefined): string => Math.round(Number(v) || 0).toLocaleString()\n\nexport const fmtCompact = (v: number | undefined, locale: LocaleId = 'zh'): string => {\n const n = Number(v) || 0\n if (locale === 'en') {\n return new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(n)\n }\n if (n >= 100_000_000) return `${(n / 100_000_000).toFixed(1)}\u4EBF`\n if (n >= 10_000) return `${(n / 10_000).toFixed(1)}\u4E07`\n return Math.round(n).toLocaleString()\n}\n", "export type ChartFocusKey = 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown' | 'Home' | 'End'\n\n/** Resolve roving focus without adding every chart point to the page Tab order. */\nexport function nextChartFocus(\n current: number,\n count: number,\n key: ChartFocusKey,\n horizontalStep = 1,\n): number {\n if (count <= 0) return 0\n const index = Math.min(Math.max(0, current), count - 1)\n if (key === 'Home') return 0\n if (key === 'End') return count - 1\n const step = key === 'ArrowLeft' || key === 'ArrowRight' ? Math.max(1, horizontalStep) : 1\n const direction = key === 'ArrowLeft' || key === 'ArrowUp' ? -1 : 1\n return Math.min(Math.max(0, index + direction * step), count - 1)\n}\n\nexport function lastPopulatedIndex(values: number[]): number {\n for (let index = values.length - 1; index >= 0; index -= 1) {\n if (values[index] > 0) return index\n }\n return Math.max(0, values.length - 1)\n}\n\n/**\n * Touch tap toggles a chart point's tooltip pin: tapping the already-pinned\n * point releases it, tapping any other point (or none pinned yet) pins that\n * one instead. Shared by bar, grouped-bar and heatmap touch handling so pin\n * state never forks into a parallel machine per chart type.\n */\nexport function nextPinnedIndex(current: number | null, tapped: number): number | null {\n return current === tapped ? null : tapped\n}\n\n/**\n * Distinguishes a tap from a scroll/drag using the touch/pointer start-to-end\n * displacement and elapsed time. A page scroll that merely passes over a data\n * point moves well past `moveThreshold` before release; a long press held past\n * `timeThreshold` without lifting is not a quick tap either.\n */\nexport function isTapGesture(dx: number, dy: number, dt: number, moveThreshold = 10, timeThreshold = 500): boolean {\n return Math.hypot(dx, dy) <= moveThreshold && dt <= timeThreshold\n}\n\n/**\n * Per-slot totals across every series of a stacked bar chart: each column's\n * height must reflect the *summed* usage of every selected model at that\n * time slot, not any single model's value \u2014 otherwise a column with many\n * small models would look shorter than a column with one large one even\n * though it represents more total usage.\n */\nexport function stackedTotals(series: Array<{ bars: Array<{ value: number }> }>, count: number): number[] {\n return Array.from({ length: count }, (_, i) => series.reduce((sum, s) => sum + (s.bars[i]?.value ?? 0), 0))\n}\n\n/**\n * Highest value in the list, or 1 when every value is zero \u2014 the shared\n * fallback scale so an all-zero chart still divides safely instead of by 0.\n */\nexport function maxOrOne(values: number[]): number {\n let max = 0\n for (const v of values) if (v > max) max = v\n return max > 0 ? max : 1\n}\n", "import { createContext, useContext, type ReactElement, type ReactNode } from 'react'\nimport { zh, type LocaleKey } from './locales.ts'\n\nexport type LocaleId = 'zh' | 'en'\nexport type Translate = (key: LocaleKey, params?: Record<string, unknown>) => string\n\nexport const fallbackT: Translate = (key, params) => {\n const template = zh[key]\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n}\n\nconst TranslationContext = createContext<Translate>(fallbackT)\n\nexport function LocaleProvider(props: { t: Translate; children: ReactNode }): ReactElement {\n return <TranslationContext.Provider value={props.t}>{props.children}</TranslationContext.Provider>\n}\n\nexport function useI18n(): { t: Translate; locale: LocaleId } {\n const t = useContext(TranslationContext)\n return { t, locale: t('meta.locale') === 'en' ? 'en' : 'zh' }\n}\n\n/** Translate host error copy that predates stable machine-readable error codes. */\nexport function localizeApiError(message: string | undefined, t: Translate, fallback: LocaleKey): string {\n if (message === undefined || message === '') return t(fallback)\n if (message === '\u51ED\u8BC1\u670D\u52A1\u4E0D\u53EF\u7528') return t('error.credentialsUnavailable')\n if (message === '\u67E5\u8BE2\u5931\u8D25') return t('error.query')\n if (message === '\u4F59\u989D\u52A0\u8F7D\u5931\u8D25') return t('error.balanceLoad')\n if (message === '\u7528\u91CF\u52A0\u8F7D\u5931\u8D25') return t('error.usageLoad')\n if (message === '\u672A\u914D\u7F6E DEEPSEEK_API_KEY\uFF08\u53EF\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u4E2D\u586B\u5199\uFF09') return t('error.noApiKey')\n if (message === '\u8D26\u53F7\u4F59\u989D\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u4E14\u672A\u914D\u7F6E DEEPSEEK_API_KEY') return t('error.accountBalanceUnavailable')\n if (message === '\u89E3\u6790\u4F59\u989D\u54CD\u5E94\u5931\u8D25') return t('error.balanceParse')\n if (message === '\u4F1A\u8BDD\u6301\u4E45\u5316\u670D\u52A1\u4E0D\u53EF\u7528') return t('error.persistenceUnavailable')\n if (message === '\u7F3A\u5C11\u4F1A\u8BDD id') return t('error.missingSessionId')\n const mappings: Array<[RegExp, LocaleKey, string]> = [\n [/^\u8BFB\u53D6 API Key \u5931\u8D25\uFF1A(.*)$/, 'error.credentialRead', 'detail'],\n [/^\u8BF7\u6C42\u4F59\u989D\u63A5\u53E3\u5931\u8D25\uFF1A(.*)$/, 'error.balanceRequest', 'detail'],\n [/^\u4F59\u989D\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP (\\d+)\uFF09$/, 'error.balanceHttp', 'status'],\n [/^\u8BFB\u53D6\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF1A(.*)$/, 'error.sessionList', 'detail'],\n [/^\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A(.*)$/, 'error.sessionLogRead', 'detail'],\n ]\n for (const [pattern, key, field] of mappings) {\n const match = message.match(pattern)\n if (match !== null) return t(key, { [field]: match[1] })\n }\n return message\n}\n", "/** Locale namespace and complete bilingual copy for the dashboard surfaces. */\nexport const NS = 'dsh-usage-dashboard'\n\nexport const zh = {\n 'meta.locale': 'zh',\n 'nav.quota': '\u989D\u5EA6',\n 'widget.title': 'DeepSeek \u989D\u5EA6',\n 'common.unknownModel': '\u672A\u77E5\u6A21\u578B',\n 'common.calls': '{count} \u6B21\u8C03\u7528',\n 'common.callsShort': '{count} \u6B21',\n 'common.sessions': '{count} \u4E2A\u4F1A\u8BDD',\n 'common.records': '{count} \u6761',\n 'common.rows': '{count} \u884C',\n 'common.input': '\u8F93\u5165',\n 'common.output': '\u8F93\u51FA',\n 'common.cacheHit': '\u7F13\u5B58\u547D\u4E2D',\n 'common.status': '\u72B6\u6001',\n 'common.available': '\u53EF\u7528',\n 'common.unavailable': '\u4E0D\u53EF\u7528',\n 'common.justNow': '\u521A\u521A',\n 'common.minutesAgo': '{count} \u5206\u949F\u524D',\n 'common.hoursAgo': '{count} \u5C0F\u65F6\u524D',\n 'common.daysAgo': '{count} \u5929\u524D',\n 'common.listSeparator': '\u3001',\n 'common.period': '\u3002',\n 'delta.noBaselineTitle': '{label}\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u4ECE\u5BF9\u6BD4',\n 'delta.noBaseline': '\u65E0\u5BF9\u6BD4',\n 'delta.flat': '\u6301\u5E73',\n 'period.volume': '{tokens} tokens \u00B7 {calls}',\n 'budget.unset': '\u8FD8\u6CA1\u8BBE\u7F6E\u6708\u5EA6\u9884\u7B97\uFF0C\u65E0\u6CD5\u63D0\u524D\u5224\u65AD\u662F\u5426\u4F1A\u8D85\u652F\u3002',\n 'budget.configure': '\u8BBE\u7F6E\u9884\u7B97',\n 'budget.over': '\u5DF2\u8D85\u9884\u7B97 \u00A5{amount}',\n 'budget.forecastOver': '\u7167\u5F53\u524D\u901F\u5EA6\uFF0C\u9884\u8BA1\u8D85\u51FA \u00A5{amount}',\n 'budget.forecast': '\u7167\u5F53\u524D\u901F\u5EA6\uFF0C\u9884\u8BA1\u6708\u5E95 \u00A5{amount}',\n 'budget.title': '\u672C\u6708\u9884\u7B97',\n 'budget.progressLabel': '\u672C\u6708\u9884\u7B97\u4F7F\u7528\u8FDB\u5EA6',\n 'budget.used': '\u5DF2\u4F7F\u7528 {percent}%\uFF0C{forecast}',\n 'budget.remaining': '\u5269\u4F59 \u00A5{amount}',\n 'budget.exceeded': '\u8D85\u51FA \u00A5{amount}',\n 'pricing.title': '\u8BA1\u4EF7\u8BF4\u660E',\n 'pricing.currentPeak': '\u5F53\u524D \u9AD8\u5CF0\u65F6\u6BB5',\n 'pricing.currentOffPeak': '\u5F53\u524D \u95F2\u65F6',\n 'pricing.model': '\u6A21\u578B',\n 'pricing.period': '\u65F6\u6BB5',\n 'pricing.inputCacheHit': '\u8F93\u5165\u00B7\u7F13\u5B58\u547D\u4E2D',\n 'pricing.inputCacheMiss': '\u8F93\u5165\u00B7\u672A\u547D\u4E2D',\n 'pricing.output': '\u8F93\u51FA',\n 'pricing.peak': '\u9AD8\u5CF0',\n 'pricing.offPeak': '\u95F2\u65F6',\n 'pricing.unit': '\u5355\u4F4D\uFF1A{currency} / \u767E\u4E07 tokens\u3002',\n 'pricing.splitNote': '\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF08\u5468\u4E00\u81F3\u5468\u4E94\uFF09\uFF0C\u5176\u4F59\u4E3A\u95F2\u65F6\uFF08\u4EF7\u683C\u51CF\u534A\uFF09\uFF1B\u6BCF\u6761\u7528\u91CF\u6309\u53D1\u751F\u65F6\u523B\u751F\u6548\u7684\u5B98\u65B9\u4EF7\u683C\u4F30\u7B97\u3002',\n 'pricing.visionNote': '\u56FE\u7247\u6309\u5C3A\u5BF8\u6298\u7B97 token\uFF0C\u4E0E\u6587\u672C\u4E00\u5E76\u8BA1\u8D39\u3002',\n 'pricing.modelNote': 'deepseek-v4-flash\u3001deepseek-v4-flash-vision-exp \u662F\u65E7\u6A21\u578B\u540D\uFF0C\u73B0\u7531 deepseek-flash \u627F\u63A5\u5E76\u6309 Flash \u4EF7\u8BA1\u8D39\uFF1B2026-09-14 12:00 \u8D77 deepseek-v4-pro \u7684\u8BF7\u6C42\u4E5F\u4F1A\u8DEF\u7531\u5230 V4.1-Flash\uFF0C\u6309 Flash \u4EF7\u8BA1\u8D39\u3002',\n 'pricing.unknown': '\u672A\u77E5\u6A21\u578B\u6309 deepseek-v4-pro \u8BA1\u4EF7\u3002',\n 'coverage.noRange': '\u6682\u65E0\u6709\u6548\u7528\u91CF\u65F6\u95F4',\n 'coverage.title': '\u7EDF\u8BA1\u8303\u56F4',\n 'coverage.gaps': '\u5B58\u5728\u7F3A\u53E3',\n 'coverage.complete': '\u672C\u673A\u8BFB\u53D6\u5B8C\u6574',\n 'coverage.brief': '\u626B\u63CF {scanned} / {listed} \u4E2A\u4F1A\u8BDD \u00B7 {records} \u6761\u7528\u91CF\u8BB0\u5F55',\n 'coverage.scanned': '\u6210\u529F\u626B\u63CF',\n 'coverage.failed': '\u8BFB\u53D6\u5931\u8D25',\n 'coverage.skipped': '\u8DF3\u8FC7\u8BB0\u5F55',\n 'coverage.time': '\u8BB0\u5F55\u65F6\u95F4\uFF1A{range}',\n 'coverage.scope': '\u8FD9\u91CC\u53EA\u7EDF\u8BA1\u5F53\u524D\u8BBE\u5907\u4E0A\u7684 DSH \u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u4E0D\u5305\u542B\u5176\u4ED6\u8BBE\u5907\u3001DeepSeek \u5E73\u53F0\u76F4\u63A5\u8C03\u7528\u6216\u5DF2\u5220\u9664\u7684\u672C\u5730\u65E5\u5FD7\u3002',\n 'coverage.warning': '\u672C\u6B21\u56DE\u653E\u6709\u65E5\u5FD7\u65E0\u6CD5\u8BFB\u53D6\u6216\u7528\u91CF\u8BB0\u5F55\u683C\u5F0F\u5F02\u5E38\uFF0C\u9875\u9762\u6C47\u603B\u53EF\u80FD\u4F4E\u4E8E\u5B9E\u9645\u6D88\u8017\uFF1B\u53EF\u5237\u65B0\u91CD\u8BD5\uFF0C\u5E76\u4EE5 DeepSeek \u5B98\u65B9\u5E73\u53F0\u8D26\u5355\u4E3A\u51C6\u3002',\n 'export.title': '\u5BFC\u51FA\u5168\u90E8\u672C\u673A DSH \u4F1A\u8BDD\u65E5\u5FD7\u6570\u636E\uFF08\u4E0D\u53D7\u7EDF\u8BA1\u5468\u671F\u5F71\u54CD\uFF09',\n 'export.done': '\u5DF2\u5BFC\u51FA',\n 'export.action': '\u5BFC\u51FA',\n 'export.menuLabel': '\u9009\u62E9\u5BFC\u51FA\u683C\u5F0F',\n 'export.dailyCsv': '\u9010\u5929 CSV',\n 'export.modelCsv': '\u9010\u6A21\u578B CSV',\n 'export.fullJson': '\u5B8C\u6574 JSON',\n 'export.jsonHint': '\u542B\u56FE\u8868\u4E0E\u6392\u884C',\n 'cache.empty': '\u8FD8\u6CA1\u6709 prompt token \u53EF\u7EDF\u8BA1\u7F13\u5B58\u547D\u4E2D\u3002',\n 'cache.rate': '\u7F13\u5B58\u547D\u4E2D\u7387',\n 'cache.saved': '\u5DF2\u8282\u7701\u8D39\u7528\uFF08\u4F30\u7B97\uFF09',\n 'cache.allMiss': '\u82E5\u5168\u90E8\u672A\u547D\u4E2D',\n 'cache.aria': '\u7F13\u5B58\u547D\u4E2D {hit}%\uFF0C\u672A\u547D\u4E2D {miss}%',\n 'cache.hitLegend': '\u7F13\u5B58\u547D\u4E2D {tokens}',\n 'cache.missLegend': '\u672A\u547D\u4E2D {tokens}\uFF08\u6309\u672A\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF09',\n 'cache.lowHint': '\u547D\u4E2D\u7387\u504F\u4F4E\uFF1A\u9891\u7E41\u6539\u52A8 system prompt / \u5DE5\u5177\u5B9A\u4E49\u4F1A\u8BA9\u524D\u7F00\u7F13\u5B58\u5931\u6548\uFF0C\u628A\u7A33\u5B9A\u5185\u5BB9\u653E\u5728\u5BF9\u8BDD\u6700\u524D\u9762\u80FD\u63D0\u9AD8\u547D\u4E2D\u7387\u3002',\n 'cache.goodHint': '\u524D\u7F00\u7F13\u5B58\u628A\u91CD\u590D\u7684 prompt \u524D\u7F00\u6309\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF0C\u662F\u8FD9\u4EFD\u8D26\u5355\u4E0A\u6700\u5927\u7684\u7701\u94B1\u6760\u6746\u3002',\n 'peak.empty': '\u8FD8\u6CA1\u6709\u53EF\u6309\u65F6\u6BB5\u5F52\u7C7B\u7684\u7528\u91CF\u3002',\n 'peak.aria': '\u9AD8\u5CF0\u65F6\u6BB5 {peak}%\uFF0C\u95F2\u65F6 {offPeak}%',\n 'peak.peakLegend': '\u9AD8\u5CF0 {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.offPeakLegend': '\u95F2\u65F6 {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.shift': '\u82E5\u9AD8\u5CF0\u7528\u91CF\u90FD\u632A\u5230\u95F2\u65F6',\n 'peak.saving': '\u53EF\u7701 \u00A5{amount}',\n 'peak.schedule': '\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF08\u5468\u4E00\u81F3\u5468\u4E94\uFF09\uFF0C\u95F2\u65F6\u4EF7\u683C\u51CF\u534A\u3002',\n 'peak.activeHint': '\u628A\u6279\u91CF\u3001\u53EF\u5EF6\u540E\u7684\u4EFB\u52A1\u653E\u5230\u95F2\u65F6\u8DD1\uFF0C\u540C\u6837\u7684 token \u53EA\u8981\u4E00\u534A\u7684\u94B1\u3002',\n 'vision.empty': '{window}\u6CA1\u6709\u56FE\u7247\u8F93\u5165\u3002',\n 'vision.title': '\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF',\n 'vision.images': '\u56FE\u7247\u5F20\u6570',\n 'vision.tokens': '\u4F30\u7B97\u56FE\u7247 tokens',\n 'vision.cost': '\u4F30\u7B97\u8D39\u7528',\n 'vision.tokensShare': '\u5360\u8F93\u5165 {percent}%',\n 'vision.costShare': '\u5360\u8D39\u7528 {percent}%',\n 'vision.note': '\u56FE\u7247\u6309\u5B98\u65B9\u6362\u7B97\u89C4\u5219\u4F30\u7B97\uFF1A\u5148\u6309\u6BD4\u4F8B\u7F29\u653E\uFF08\u4E0A\u9650 ~800\u00D7800\u3001\u4E0B\u9650 384\u00D7384\uFF09\uFF0Ctoken \u4E0E\u7F29\u653E\u540E\u9762\u79EF\u6210\u6B63\u6BD4\u3001\u6BCF\u5F20\u4E0A\u9650 384\uFF1B\u56FE\u7247\u5F52\u5C5E\u5230\u968F\u540E\u4E00\u6B21\u6A21\u578B\u8C03\u7528\uFF0C\u6309\u8BE5\u8C03\u7528\u7684\u65F6\u6BB5\u4E0E\u6A21\u578B\u5355\u4EF7\u4F30\u7B97\uFF0C\u5B9E\u9645\u4EE5\u63A5\u53E3\u8FD4\u56DE\u7684\u7528\u91CF\u4E3A\u51C6\u3002',\n 'vision.sessionsTitle': '\u56FE\u7247\u6700\u591A\u7684\u4F1A\u8BDD',\n 'vision.sessionRow': '{images} \u5F20 \u00B7 \u00A5{cost}',\n 'vision.dailyTitle': '{window} \u00B7 \u6BCF\u5929\u56FE\u7247\u6570',\n 'vision.barAria': '{date} \u00B7 {images} \u5F20\u56FE\u7247 \u00B7 \u4F30\u7B97 {tokens} tokens',\n 'vision.barTitle': '{date} \u00B7 {images} \u5F20\u56FE\u7247 \u00B7 \u4F30\u7B97 {tokens} tokens \u00B7 \u00A5{cost}',\n 'sessions.empty': '\u8FD8\u6CA1\u6709\u4EA7\u751F\u8D39\u7528\u7684\u4F1A\u8BDD\u3002',\n 'sessions.detail': '{tokens} tokens \u00B7 {calls} \u00B7 {ago}',\n 'sessions.share': '\u5360 {percent}%',\n 'sessions.more': '\u5171 {count} \u4E2A\u4F1A\u8BDD\u6709\u7528\u91CF\uFF0C\u4E0A\u9762\u662F\u6700\u8D35\u7684 {shown} \u4E2A\u3002',\n 'sessions.fallbackTitle': '\u4F1A\u8BDD {id}',\n 'models.empty': '\u8FD8\u6CA1\u6709\u53EF\u5F52\u7C7B\u7684\u6A21\u578B\u7528\u91CF\u3002',\n 'models.shareAria': '{name} \u5360\u603B\u8D39\u7528 {percent}%',\n 'models.detail': '{tokens} tokens \u00B7 {calls} \u00B7 \u8F93\u5165 {input} / \u8F93\u51FA {output} / \u7F13\u5B58 {cache}',\n 'models.all': '\u5168\u90E8\u6A21\u578B',\n 'models.selected': '\u6A21\u578B \u00D7{count}',\n 'models.filterTitle': '\u7B5B\u9009\u56FE\u8868\u4E2D\u663E\u793A\u7684\u6A21\u578B',\n 'models.filterLabel': '\u7B5B\u9009\u56FE\u8868\u6A21\u578B',\n 'window.year': '\u8FD1 1 \u5E74',\n 'window.days': '\u8FD1 {days} \u5929',\n 'window.groupLabel': '\u7EDF\u8BA1\u5468\u671F',\n 'window.viewTitle': '\u67E5\u770B{window}\u7684\u7528\u91CF\u4E0E\u6392\u884C',\n 'window.yearButton': '1 \u5E74',\n 'window.daysButton': '{days} \u5929',\n 'metric.tokens': 'tokens',\n 'metric.usage': '\u7528\u91CF',\n 'metric.cost': '\u8D39\u7528',\n 'metric.calls': '\u8C03\u7528',\n 'metric.groupLabel': '\u56FE\u8868\u6307\u6807',\n 'metric.compareTitle': '\u6309{metric}\u6BD4\u8F83',\n 'chart.dailyTitle': '{window} \u00B7 \u9010\u5929{metric}',\n 'chart.hourlyTitle': '{window} \u00B7 {metric}\u6309\u5C0F\u65F6\u5206\u5E03\uFF080\u201323 \u70B9\uFF09',\n 'chart.peak': '\u5CF0\u503C {value}',\n 'chart.dimensionLabel': '\u5207\u6362\u56FE\u8868\u7EF4\u5EA6',\n 'chart.daily': '\u9010\u5929',\n 'chart.hourly': '\u9010\u5C0F\u65F6',\n 'chart.noWindowData': '{window}\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\uFF0C\u6362\u4E2A\u66F4\u957F\u5468\u671F\u770B\u770B\u3002',\n 'chart.heatmapTitle': '\u8FD1\u4E00\u5E74 \u00B7 \u6BCF\u65E5\u7528\u91CF\u70ED\u529B\u56FE',\n 'chart.empty': '\u8FD8\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002\u5728 DSH \u91CC\u8DD1\u4E00\u8F6E\u5BF9\u8BDD\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u9010\u5929 / \u9010\u5C0F\u65F6\u7EDF\u8BA1\u4E0E\u8D39\u7528\u4F30\u7B97\u3002',\n 'chart.hourPoint': '{hour} \u70B9',\n 'chart.tooltip': '{head} \u00B7 {tokens} tokens \u00B7 \u00A5{cost} \u00B7 {calls}',\n 'chart.less': '\u5C11',\n 'chart.more': '\u591A',\n 'chart.month1': '1\u6708',\n 'chart.month2': '2\u6708',\n 'chart.month3': '3\u6708',\n 'chart.month4': '4\u6708',\n 'chart.month5': '5\u6708',\n 'chart.month6': '6\u6708',\n 'chart.month7': '7\u6708',\n 'chart.month8': '8\u6708',\n 'chart.month9': '9\u6708',\n 'chart.month10': '10\u6708',\n 'chart.month11': '11\u6708',\n 'chart.month12': '12\u6708',\n 'number.tenThousand': '{value}\u4E07',\n 'number.hundredMillion': '{value}\u4EBF',\n 'fresh.none': '\u5C1A\u65E0\u6210\u529F\u8BB0\u5F55',\n 'fresh.justNow': '\u521A\u521A\u66F4\u65B0',\n 'fresh.minutes': '\u66F4\u65B0\u4E8E {count} \u5206\u949F\u524D',\n 'fresh.hours': '\u66F4\u65B0\u4E8E {count} \u5C0F\u65F6\u524D',\n 'fresh.days': '\u66F4\u65B0\u4E8E {count} \u5929\u524D',\n 'sync.first': '\u9996\u6B21\u540C\u6B65\u4E2D',\n 'sync.syncing': '\u540C\u6B65\u4E2D \u00B7 \u5F53\u524D\u6570\u636E{age}',\n 'sync.fresh': '\u5DF2\u540C\u6B65 \u00B7 {age}',\n 'sync.cached': '\u7F13\u5B58\u6570\u636E \u00B7 {age}',\n 'sync.fallback': '\u7F13\u5B58\u56DE\u9000 \u00B7 {age}',\n 'sync.error': '\u540C\u6B65\u5931\u8D25 \u00B7 \u6682\u65E0\u53EF\u663E\u793A\u7684\u7528\u91CF',\n 'status.neverSynced': '\u8FD8\u6CA1\u6709\u6210\u529F\u540C\u6B65\u8FC7\u7528\u91CF\u6570\u636E',\n 'status.usageTime': '\u7528\u91CF\u6570\u636E\u65F6\u95F4\uFF1A{time}',\n 'status.forceRefresh': '\u5F3A\u5236\u5237\u65B0\uFF08\u7ED5\u8FC7\u7F13\u5B58\uFF09',\n 'status.refreshing': '\u5237\u65B0\u4E2D',\n 'status.refresh': '\u5237\u65B0',\n 'error.balanceLoad': '\u4F59\u989D\u52A0\u8F7D\u5931\u8D25',\n 'error.usageLoad': '\u7528\u91CF\u52A0\u8F7D\u5931\u8D25',\n 'error.refreshCached': '\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C55\u793A\u7F13\u5B58\u6570\u636E\uFF08{error}\uFF09',\n 'error.balanceRefreshCached': '\u4F59\u989D\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C55\u793A\u7F13\u5B58\u4F59\u989D\uFF08{error}\uFF09',\n 'error.usageRefreshCached': '\u7528\u91CF\u5237\u65B0\u5931\u8D25\uFF0C\u6B63\u5728\u5C55\u793A\u7F13\u5B58\u7528\u91CF\uFF08{error}\uFF09',\n 'error.query': '\u67E5\u8BE2\u5931\u8D25',\n 'error.credentialsUnavailable': '\u51ED\u8BC1\u670D\u52A1\u4E0D\u53EF\u7528',\n 'error.credentialRead': '\u8BFB\u53D6 API Key \u5931\u8D25\uFF1A{detail}',\n 'error.noApiKey': '\u672A\u914D\u7F6E DEEPSEEK_API_KEY\uFF08\u53EF\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u4E2D\u586B\u5199\uFF09',\n 'error.accountBalanceUnavailable': '\u8D26\u53F7\u4F59\u989D\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u4E14\u672A\u914D\u7F6E DEEPSEEK_API_KEY',\n 'error.balanceRequest': '\u8BF7\u6C42\u4F59\u989D\u63A5\u53E3\u5931\u8D25\uFF1A{detail}',\n 'error.balanceHttp': '\u4F59\u989D\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF08HTTP {status}\uFF09',\n 'error.balanceParse': '\u89E3\u6790\u4F59\u989D\u54CD\u5E94\u5931\u8D25',\n 'error.persistenceUnavailable': '\u4F1A\u8BDD\u6301\u4E45\u5316\u670D\u52A1\u4E0D\u53EF\u7528',\n 'error.sessionList': '\u8BFB\u53D6\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF1A{detail}',\n 'error.sessionLogRead': '\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A{detail}',\n 'error.missingSessionId': '\u7F3A\u5C11\u4F1A\u8BDD id',\n 'alert.lowTitle': '\u4F59\u989D {balance} {currency} \u5DF2\u4F4E\u4E8E\u9884\u8B66\u7EBF {threshold}',\n 'alert.runway': '\uFF0C\u6309\u8FD1\u671F\u7528\u91CF\u4F30\u8BA1\u8FD8\u80FD\u6491 {days}',\n 'alert.topUp': '\u53BB\u5145\u503C',\n 'balance.title': '\u8D26\u6237\u4F59\u989D',\n 'balance.remaining': '\u5269\u4F59\u4F59\u989D',\n 'balance.breakdownTitle': '\u67E5\u770B\u5145\u503C\u4E0E\u8D60\u9001\u989D\u5EA6\u660E\u7EC6',\n 'balance.toppedUp': '\u5145\u503C\u989D\u5EA6 {amount}',\n 'balance.granted': '\u8D60\u9001\u989D\u5EA6 {amount}',\n 'balance.runway': '\u9884\u8BA1\u53EF\u7528',\n 'balance.runwayBreakdownTitle': '\u67E5\u770B\u9884\u8BA1\u53EF\u7528\u5929\u6570\u8BF4\u660E',\n 'balance.days': '{count} \u5929',\n 'balance.overYear': '> 365 \u5929',\n 'balance.noRunway': '\u8FD1 {days} \u5929\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u6CD5\u4F30\u7B97',\n 'balance.runwayTitle': '\u6309\u8FD1 {days} \u5929\u65E5\u5747 \u00A5{cost} \u4F30\u7B97\uFF08\u542B\u65E0\u7528\u91CF\u7684\u65E5\u5B50\uFF1B\u4ECA\u5929\u5C1A\u672A\u8FC7\u5B8C\uFF09',\n 'balance.empty': '\u8BFB\u4E0D\u5230\u4F59\u989D\u3002\u8BF7\u786E\u8BA4\u8D26\u53F7\u5DF2\u767B\u5F55\uFF0C\u6216\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u4E2D\u586B\u5199 DEEPSEEK_API_KEY\uFF0C\u7136\u540E\u70B9\u53F3\u4E0A\u89D2\u5237\u65B0\u3002',\n 'sessionUsage.title': '\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\uFF08\u8D39\u7528\u4E3A\u4F30\u7B97\uFF09',\n 'sessionUsage.cost': '\u672C\u4F1A\u8BDD\u8D39\u7528',\n 'sessionUsage.tokens': '\u672C\u4F1A\u8BDD tokens',\n 'sessionUsage.calls': '\u672C\u4F1A\u8BDD\u8C03\u7528',\n 'sessionUsage.range': '\u6D3B\u8DC3\u65F6\u95F4 {range}',\n 'sessionUsage.empty': '\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u4EA7\u751F\u7528\u91CF\u3002\u53D1\u4E00\u6761\u6D88\u606F\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u8FD9\u4E2A\u4F1A\u8BDD\u4E13\u5C5E\u7684\u8D39\u7528\u4E0E\u6A21\u578B\u660E\u7EC6\u3002',\n 'overview.title': '\u6D88\u8017\u6982\u89C8\uFF08\u8D39\u7528\u4E3A\u4F30\u7B97\uFF09',\n 'overview.today': '\u4ECA\u65E5',\n 'overview.month': '\u672C\u6708',\n 'overview.total': '\u7D2F\u8BA1',\n 'overview.vsYesterday': '\u8F83\u6628\u65E5',\n 'overview.vsLastMonth': '\u8F83\u4E0A\u6708\u540C\u671F',\n 'overview.empty': '\u8FD8\u6CA1\u6709\u53EF\u7EDF\u8BA1\u7684\u6D88\u8017\u3002',\n 'usage.trendTitle': '\u7528\u91CF\u8D8B\u52BF\uFF08tokens\uFF09',\n 'usage.windowInput': '{window}\u8F93\u5165',\n 'usage.modelCalls': '\u6A21\u578B\u8C03\u7528',\n 'heatmap.title': '\u7528\u91CF\u70ED\u529B\u56FE',\n 'peak.title': '\u9AD8\u5CF0 / \u95F2\u65F6\u5206\u5E03',\n 'cache.title': '\u7F13\u5B58\u547D\u4E2D\u4E0E\u8282\u7701',\n 'models.title': '\u6A21\u578B\u6210\u672C\u6392\u884C \u00B7 {window}',\n 'sessions.title': '\u4F1A\u8BDD\u6210\u672C\u6392\u884C \u00B7 {window}',\n 'links.title': '\u5FEB\u6377\u5165\u53E3',\n 'links.usage': '\u67E5\u770B\u989D\u5EA6 / \u7528\u91CF',\n 'links.apiKey': '\u751F\u6210 API Key',\n 'links.status': '\u670D\u52A1\u72B6\u6001',\n 'settings.title': '\u8BBE\u7F6E',\n 'settings.widget': '\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3',\n 'settings.widgetHint': '\u989D\u5EA6\u9875\u4F1A\u81EA\u52A8\u9690\u85CF\u6D6E\u7A97\uFF1B\u5176\u4ED6\u9875\u9762\u662F\u5426\u5C55\u793A\u7531\u4E0B\u65B9\u9009\u62E9\u51B3\u5B9A\u3002',\n 'settings.widgetTabs': '\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762',\n 'settings.widgetTabsHint': '\u9875\u9762\u5217\u8868\u6765\u81EA DSH \u7684\u5B9E\u65F6 Tab \u6CE8\u518C\u8868\uFF1B\u5176\u4ED6\u63D2\u4EF6\u65B0\u589E\u7684 Tab \u4E5F\u4F1A\u81EA\u52A8\u51FA\u73B0\u5728\u8FD9\u91CC\u3002',\n 'settings.widgetTabsEmpty': '\u5F53\u524D\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u4F1A\u8BDD\u9875\u9762\u3002',\n 'settings.lowBalance': '\u4F59\u989D\u9884\u8B66\u7EBF',\n 'settings.lowBalanceOn': '\u4F59\u989D\u4F4E\u4E8E {amount} {currency} \u65F6\uFF0C\u8FD9\u91CC\u548C\u60AC\u6D6E\u7A97\u90FD\u4F1A\u8F6C\u4E3A\u8B66\u793A\u8272\u3002\u586B 0 \u5173\u95ED\u3002',\n 'settings.lowBalanceOff': '\u5DF2\u5173\u95ED\u4F59\u989D\u9884\u8B66\u3002\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u6570\u5F00\u542F\u3002',\n 'settings.monthlyBudget': '\u6708\u5EA6\u9884\u7B97',\n 'settings.monthlyBudgetOn': '\u6309\u5317\u4EAC\u65F6\u95F4\u81EA\u7136\u6708\u8DDF\u8E2A {amount} CNY \u9884\u7B97\uFF0C\u5E76\u5728\u6D88\u8017\u6982\u89C8\u9884\u6D4B\u6708\u5E95\u82B1\u8D39\u3002\u586B 0 \u5173\u95ED\u3002',\n 'settings.monthlyBudgetOff': '\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u91D1\u989D\uFF0C\u6D88\u8017\u6982\u89C8\u4F1A\u663E\u793A\u8FDB\u5EA6\u4E0E\u6708\u5E95\u9884\u6D4B\u3002',\n 'widget.updatedNow': '\u521A\u521A\u66F4\u65B0',\n 'widget.stale': '{minutes} \u5206\u949F\u524D\u7684\u6570\u636E\uFF0C\u70B9 \u21BB \u66F4\u65B0',\n 'widget.loading': '\u67E5\u8BE2\u4E2D\u2026',\n 'widget.lowTitle': '\u4F59\u989D\u4F4E\u4E8E\u9884\u8B66\u7EBF {amount}',\n 'widget.todayCostTitle': '\u4ECA\u65E5\u6D88\u8017\uFF08\u4F30\u7B97\uFF09\u00B7 {age}',\n 'widget.todayCost': '\u4ECA\u65E5\u6D88\u8017',\n 'widget.sessionCostTitle': '\u5F53\u524D\u4F1A\u8BDD\u7D2F\u8BA1\u6D88\u8017\uFF08\u4F30\u7B97\uFF09',\n 'widget.sessionCost': '\u5F53\u524D\u4F1A\u8BDD',\n 'widget.noBalance': '\u6682\u65E0\u4F59\u989D\u6570\u636E',\n 'widget.dragCard': '\u62D6\u52A8\u5230\u56DB\u4E2A\u89D2\u843D',\n 'widget.dragGrip': '\u62D6\u52A8\u4EE5\u79FB\u52A8',\n 'widget.refresh': '\u5237\u65B0\u4F59\u989D\u4E0E\u4ECA\u65E5\u7528\u91CF',\n 'widget.expand': '\u5C55\u5F00',\n 'widget.collapse': '\u6536\u8D77',\n 'widget.expandAria': '\u5C55\u5F00\u989D\u5EA6\u7A97\u53E3',\n 'widget.collapseAria': '\u6536\u8D77\u989D\u5EA6\u7A97\u53E3',\n 'messageCost.label': '\u672C\u8F6E\u8D39\u7528',\n 'messageCost.tooltip': '\u7531 dsh-usage-dashboard \u63D0\u4F9B \u00B7 \u672C\u8F6E\u5BF9\u8BDD\u8D39\u7528\u4E3A\u4F30\u7B97\u503C',\n 'messageCost.aria': '{amount}\uFF0C\u672C\u8F6E\u5BF9\u8BDD\u4F30\u7B97\u8D39\u7528\uFF0C\u7531 dsh-usage-dashboard \u63D0\u4F9B',\n 'boundary.title': '\u989D\u5EA6\u9762\u677F\u51FA\u9519\u4E86',\n 'boundary.body': '\u6E32\u67D3\u65F6\u629B\u4E86\u5F02\u5E38\uFF0C\u901A\u5E38\u662F\u6D4F\u89C8\u5668\u91CC\u5B58\u7740\u65E7\u7248\u672C\u7684\u7F13\u5B58\u6570\u636E\u3002\u6E05\u6389\u672C\u63D2\u4EF6\u7684\u672C\u5730\u6570\u636E\u518D\u91CD\u65B0\u52A0\u8F7D\u5C31\u80FD\u6062\u590D\u3002',\n 'boundary.reset': '\u6E05\u9664\u672C\u5730\u6570\u636E\u5E76\u91CD\u65B0\u52A0\u8F7D',\n} as const\n\nexport type LocaleKey = keyof typeof zh\n\nexport const en: Record<LocaleKey, string> = {\n 'meta.locale': 'en',\n 'nav.quota': 'Usage',\n 'widget.title': 'DeepSeek Usage',\n 'common.unknownModel': 'Unknown model',\n 'common.calls': '{count} calls',\n 'common.callsShort': '{count} calls',\n 'common.sessions': '{count} sessions',\n 'common.records': '{count} records',\n 'common.rows': '{count} rows',\n 'common.input': 'Input',\n 'common.output': 'Output',\n 'common.cacheHit': 'Cache hit',\n 'common.status': 'Status',\n 'common.available': 'Available',\n 'common.unavailable': 'Unavailable',\n 'common.justNow': 'Just now',\n 'common.minutesAgo': '{count}m ago',\n 'common.hoursAgo': '{count}h ago',\n 'common.daysAgo': '{count}d ago',\n 'common.listSeparator': ', ',\n 'common.period': '.',\n 'delta.noBaselineTitle': 'No usage in {label}, so there is no baseline',\n 'delta.noBaseline': 'No baseline',\n 'delta.flat': 'No change',\n 'period.volume': '{tokens} tokens \u00B7 {calls}',\n 'budget.unset': 'Set a monthly budget to see whether current spending is on track.',\n 'budget.configure': 'Set budget',\n 'budget.over': 'Over budget by \u00A5{amount}',\n 'budget.forecastOver': 'At this pace, \u00A5{amount} over by month-end',\n 'budget.forecast': 'At this pace, \u00A5{amount} by month-end',\n 'budget.title': 'Monthly budget',\n 'budget.progressLabel': 'Monthly budget usage',\n 'budget.used': '{percent}% used. {forecast}',\n 'budget.remaining': '\u00A5{amount} remaining',\n 'budget.exceeded': '\u00A5{amount} over',\n 'pricing.title': 'Pricing details',\n 'pricing.currentPeak': 'Peak now',\n 'pricing.currentOffPeak': 'Off-peak now',\n 'pricing.model': 'Model',\n 'pricing.period': 'Period',\n 'pricing.inputCacheHit': 'Input \u00B7 cache hit',\n 'pricing.inputCacheMiss': 'Input \u00B7 cache miss',\n 'pricing.output': 'Output',\n 'pricing.peak': 'Peak',\n 'pricing.offPeak': 'Off-peak',\n 'pricing.unit': 'Unit: {currency} / million tokens. ',\n 'pricing.splitNote': 'Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. Each record is estimated at the official rates in force when it was written. ',\n 'pricing.visionNote': 'Images are converted to tokens by size and charged together with text. ',\n 'pricing.modelNote': 'deepseek-v4-flash and deepseek-v4-flash-vision-exp are legacy names, now served by deepseek-flash at Flash rates; from 2026-09-14 12:00 deepseek-v4-pro requests are routed to V4.1-Flash and billed at Flash rates. ',\n 'pricing.unknown': 'Unknown models use deepseek-v4-pro rates.',\n 'coverage.noRange': 'No valid usage timestamps',\n 'coverage.title': 'Coverage',\n 'coverage.gaps': 'Gaps found',\n 'coverage.complete': 'Local logs complete',\n 'coverage.brief': 'Scanned {scanned} / {listed} sessions \u00B7 {records} usage records',\n 'coverage.scanned': 'Scanned',\n 'coverage.failed': 'Read failures',\n 'coverage.skipped': 'Skipped records',\n 'coverage.time': 'Record range: {range}',\n 'coverage.scope': 'Only DSH session logs on this device are included. Other devices, direct DeepSeek Platform calls, and deleted local logs are excluded.',\n 'coverage.warning': 'Some logs could not be read or contained invalid usage records, so totals may be lower than actual spend. Refresh to retry and use the DeepSeek Platform bill as the source of truth.',\n 'export.title': 'Export all local DSH session-log data (independent of the selected range)',\n 'export.done': 'Exported',\n 'export.action': 'Export',\n 'export.menuLabel': 'Choose export format',\n 'export.dailyCsv': 'Daily CSV',\n 'export.modelCsv': 'Model CSV',\n 'export.fullJson': 'Full JSON',\n 'export.jsonHint': 'Charts and rankings included',\n 'cache.empty': 'No prompt tokens are available for cache analysis yet.',\n 'cache.rate': 'Cache hit rate',\n 'cache.saved': 'Estimated savings',\n 'cache.allMiss': 'Cost with no hits',\n 'cache.aria': '{hit}% cache hits, {miss}% misses',\n 'cache.hitLegend': 'Cache hits {tokens}',\n 'cache.missLegend': 'Misses {tokens} (charged at miss rate)',\n 'cache.lowHint': 'Low hit rate: frequent changes to the system prompt or tool definitions invalidate prefix caching. Put stable content first to improve reuse.',\n 'cache.goodHint': 'Prefix caching charges repeated prompt prefixes at the hit rate, making it the largest saving lever on this bill.',\n 'peak.empty': 'No usage can be classified by pricing period yet.',\n 'peak.aria': '{peak}% peak, {offPeak}% off-peak',\n 'peak.peakLegend': 'Peak {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.offPeakLegend': 'Off-peak {share}% \u00B7 {tokens} tokens \u00B7 {calls}',\n 'peak.shift': 'If all peak usage moved off-peak',\n 'peak.saving': 'Save \u00A5{amount}',\n 'peak.schedule': 'Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. ',\n 'peak.activeHint': 'Run batch or delay-tolerant work off-peak to pay half as much for the same tokens.',\n 'vision.empty': 'No image input in {window}.',\n 'vision.title': 'Image / multimodal usage',\n 'vision.images': 'Images',\n 'vision.tokens': 'Estimated image tokens',\n 'vision.cost': 'Estimated cost',\n 'vision.tokensShare': '{percent}% of input',\n 'vision.costShare': '{percent}% of cost',\n 'vision.note': 'Images are estimated with the official rule: resized keeping aspect ratio (cap ~800\u00D7800, floor 384\u00D7384), tokens proportional to the resized area with a 384-token cap per image. Each image is attributed to the next model call and priced at that call\\'s period and model rate; the provider usage numbers remain the source of truth.',\n 'vision.sessionsTitle': 'Sessions with the most images',\n 'vision.sessionRow': '{images} images \u00B7 \u00A5{cost}',\n 'vision.dailyTitle': '{window} \u00B7 images per day',\n 'vision.barAria': '{date} \u00B7 {images} images \u00B7 est. {tokens} tokens',\n 'vision.barTitle': '{date} \u00B7 {images} images \u00B7 est. {tokens} tokens \u00B7 \u00A5{cost}',\n 'sessions.empty': 'No sessions have incurred cost yet.',\n 'sessions.detail': '{tokens} tokens \u00B7 {calls} \u00B7 {ago}',\n 'sessions.share': '{percent}% of total',\n 'sessions.more': '{count} sessions have usage; the {shown} most expensive are shown above.',\n 'sessions.fallbackTitle': 'Session {id}',\n 'models.empty': 'No model usage can be classified yet.',\n 'models.shareAria': '{name} accounts for {percent}% of total cost',\n 'models.detail': '{tokens} tokens \u00B7 {calls} \u00B7 input {input} / output {output} / cache {cache}',\n 'models.all': 'All models',\n 'models.selected': '{count} models',\n 'models.filterTitle': 'Filter models shown in the chart',\n 'models.filterLabel': 'Filter chart models',\n 'window.year': 'Last year',\n 'window.days': 'Last {days} days',\n 'window.groupLabel': 'Date range',\n 'window.viewTitle': 'View usage and rankings for {window}',\n 'window.yearButton': '1 year',\n 'window.daysButton': '{days} days',\n 'metric.tokens': 'tokens',\n 'metric.usage': 'usage',\n 'metric.cost': 'cost',\n 'metric.calls': 'calls',\n 'metric.groupLabel': 'Chart metric',\n 'metric.compareTitle': 'Compare by {metric}',\n 'chart.dailyTitle': '{window} \u00B7 daily {metric}',\n 'chart.hourlyTitle': '{window} \u00B7 {metric} by hour (00\u201323)',\n 'chart.peak': 'Peak {value}',\n 'chart.dimensionLabel': 'Chart interval',\n 'chart.daily': 'Daily',\n 'chart.hourly': 'Hourly',\n 'chart.noWindowData': 'No usage in {window}. Try a longer range.',\n 'chart.heatmapTitle': 'Last year \u00B7 daily usage heatmap',\n 'chart.empty': 'No usage yet. Run a conversation in DSH to see daily and hourly totals with cost estimates.',\n 'chart.hourPoint': '{hour}:00',\n 'chart.tooltip': '{head} \u00B7 {tokens} tokens \u00B7 \u00A5{cost} \u00B7 {calls}',\n 'chart.less': 'Less',\n 'chart.more': 'More',\n 'chart.month1': 'Jan',\n 'chart.month2': 'Feb',\n 'chart.month3': 'Mar',\n 'chart.month4': 'Apr',\n 'chart.month5': 'May',\n 'chart.month6': 'Jun',\n 'chart.month7': 'Jul',\n 'chart.month8': 'Aug',\n 'chart.month9': 'Sep',\n 'chart.month10': 'Oct',\n 'chart.month11': 'Nov',\n 'chart.month12': 'Dec',\n 'number.tenThousand': '{value}0K',\n 'number.hundredMillion': '{value}00M',\n 'fresh.none': 'No successful sync yet',\n 'fresh.justNow': 'Updated just now',\n 'fresh.minutes': 'Updated {count}m ago',\n 'fresh.hours': 'Updated {count}h ago',\n 'fresh.days': 'Updated {count}d ago',\n 'sync.first': 'Syncing for the first time',\n 'sync.syncing': 'Syncing \u00B7 current data {age}',\n 'sync.fresh': 'Synced \u00B7 {age}',\n 'sync.cached': 'Cached data \u00B7 {age}',\n 'sync.fallback': 'Using cache \u00B7 {age}',\n 'sync.error': 'Sync failed \u00B7 no usage to display',\n 'status.neverSynced': 'Usage has not synced successfully yet',\n 'status.usageTime': 'Usage data timestamp: {time}',\n 'status.forceRefresh': 'Force refresh (bypass cache)',\n 'status.refreshing': 'Refreshing',\n 'status.refresh': 'Refresh',\n 'error.balanceLoad': 'Balance failed to load',\n 'error.usageLoad': 'Usage failed to load',\n 'error.refreshCached': 'Refresh failed; showing cached data ({error})',\n 'error.balanceRefreshCached': 'Balance refresh failed; showing cached balance ({error})',\n 'error.usageRefreshCached': 'Usage refresh failed; showing cached usage ({error})',\n 'error.query': 'Request failed',\n 'error.credentialsUnavailable': 'Credential service is unavailable',\n 'error.credentialRead': 'Could not read the API key: {detail}',\n 'error.noApiKey': 'DEEPSEEK_API_KEY is not configured (add it under Settings \u2192 Models)',\n 'error.accountBalanceUnavailable': 'The account balance endpoint is unavailable and DEEPSEEK_API_KEY is not configured',\n 'error.balanceRequest': 'Balance request failed: {detail}',\n 'error.balanceHttp': 'Balance endpoint returned HTTP {status}',\n 'error.balanceParse': 'Could not parse the balance response',\n 'error.persistenceUnavailable': 'Session persistence is unavailable',\n 'error.sessionList': 'Could not read the session list: {detail}',\n 'error.sessionLogRead': 'Could not read the session log: {detail}',\n 'error.missingSessionId': 'Missing session id',\n 'alert.lowTitle': 'Balance {balance} {currency} is below the {threshold} warning threshold',\n 'alert.runway': '; about {days} left at recent usage',\n 'alert.topUp': 'Top up',\n 'balance.title': 'Account balance',\n 'balance.remaining': 'Remaining balance',\n 'balance.breakdownTitle': 'View topped-up and granted balance details',\n 'balance.toppedUp': 'Topped up {amount}',\n 'balance.granted': 'Granted {amount}',\n 'balance.runway': 'Estimated runway',\n 'balance.runwayBreakdownTitle': 'View estimated runway details',\n 'balance.days': '{count} days',\n 'balance.overYear': '> 365 days',\n 'balance.noRunway': 'No usage in the last {days} days, so runway cannot be estimated',\n 'balance.runwayTitle': 'Based on \u00A5{cost} average daily spend over the last {days} days (including idle days; today is incomplete)',\n 'balance.empty': 'Balance is unavailable. Sign in to your account or add DEEPSEEK_API_KEY under Settings \u2192 Models, then refresh.',\n 'sessionUsage.title': 'Current session spend (estimated)',\n 'sessionUsage.cost': 'This session',\n 'sessionUsage.tokens': 'Session tokens',\n 'sessionUsage.calls': 'Session calls',\n 'sessionUsage.range': 'Active {range}',\n 'sessionUsage.empty': 'No usage in this session yet. Send a message and this session\u2019s own cost and model breakdown will show up here.',\n 'overview.title': 'Spend overview (estimated)',\n 'overview.today': 'Today',\n 'overview.month': 'This month',\n 'overview.total': 'All time',\n 'overview.vsYesterday': 'vs yesterday',\n 'overview.vsLastMonth': 'vs same period last month',\n 'overview.empty': 'No spend to summarize yet.',\n 'usage.trendTitle': 'Usage trend (tokens)',\n 'usage.windowInput': '{window} input',\n 'usage.modelCalls': 'Model calls',\n 'heatmap.title': 'Usage heatmap',\n 'peak.title': 'Peak / off-peak distribution',\n 'cache.title': 'Cache hits and savings',\n 'models.title': 'Model cost ranking \u00B7 {window}',\n 'sessions.title': 'Session cost ranking \u00B7 {window}',\n 'links.title': 'Quick Links',\n 'links.usage': 'View balance / usage',\n 'links.apiKey': 'Create API key',\n 'links.status': 'Service status',\n 'settings.title': 'Settings',\n 'settings.widget': 'Show the floating balance widget on other views',\n 'settings.widgetHint': 'The widget always hides on Usage; choose its other views below.',\n 'settings.widgetTabs': 'Show widget on',\n 'settings.widgetTabsHint': 'This live list comes from DSH\u2019s tab registry, including tabs added by other plugins.',\n 'settings.widgetTabsEmpty': 'There are no configurable conversation views right now.',\n 'settings.lowBalance': 'Balance warning',\n 'settings.lowBalanceOn': 'This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.',\n 'settings.lowBalanceOff': 'Balance warnings are off. Enter an amount above 0 to enable them.',\n 'settings.monthlyBudget': 'Monthly budget',\n 'settings.monthlyBudgetOn': 'Track a {amount} CNY budget by Beijing calendar month and forecast month-end spend. Enter 0 to disable.',\n 'settings.monthlyBudgetOff': 'Enter an amount above 0 to show progress and a month-end forecast.',\n 'widget.updatedNow': 'Updated just now',\n 'widget.stale': 'Data from {minutes}m ago; press \u21BB to update',\n 'widget.loading': 'Loading\u2026',\n 'widget.lowTitle': 'Balance below warning threshold {amount}',\n 'widget.todayCostTitle': 'Today\u2019s spend (estimated) \u00B7 {age}',\n 'widget.todayCost': 'Today\u2019s spend',\n 'widget.sessionCostTitle': 'Estimated spend for the current session',\n 'widget.sessionCost': 'Current session',\n 'widget.noBalance': 'No balance data',\n 'widget.dragCard': 'Drag to any corner',\n 'widget.dragGrip': 'Drag to move',\n 'widget.refresh': 'Refresh balance and today\u2019s usage',\n 'widget.expand': 'Expand',\n 'widget.collapse': 'Collapse',\n 'widget.expandAria': 'Expand balance widget',\n 'widget.collapseAria': 'Collapse balance widget',\n 'messageCost.label': 'Turn cost',\n 'messageCost.tooltip': 'Provided by dsh-usage-dashboard \u00B7 estimated cost for this turn',\n 'messageCost.aria': '{amount}, estimated cost for this turn, provided by dsh-usage-dashboard',\n 'boundary.title': 'The usage dashboard crashed',\n 'boundary.body': 'Rendering failed, usually because this browser still has cached data from an older version. Clear this plugin\u2019s local data and reload to recover.',\n 'boundary.reset': 'Clear local data and reload',\n}\n", "/** Shared lookups into the DSH host shell DOM. Both the floating widget\n * (keeping its drag bounds clear of the header/composer) and the dashboard\n * (keeping its scroll content clear of the fixed composer on narrow\n * viewports) need to find the same host elements \u2014 sharing the lookup here\n * keeps the two readings of \"where is the composer\" from drifting apart.\n */\n\n/** The shell's main column: the region between the sidebar and the details\n * panel. Found through the stable `conversation` slot anchor rather than the\n * hashed layout class names. */\nexport function getMainColumn(frame: HTMLElement): HTMLElement | null {\n const anchor = frame.querySelector('[data-slot=\"conversation\"]')\n const column = anchor?.parentElement ?? null\n return column === frame ? null : column\n}\n\n/**\n * The laid-out element behind a slot. Slot wrappers are 0\u00D70, and which\n * neighbour carries the real box differs per slot: the session header\n * renders inside its wrapper (`'child'`), while the composer's wrapper sits\n * inside the seat that reserves the space (`'ancestor'`). Picking the wrong\n * side silently yields a box in the wrong place, so the caller says which one\n * it means.\n */\nfunction slotElement(frame: HTMLElement, slot: string, side: 'child' | 'ancestor'): HTMLElement | null {\n const anchor = frame.querySelector(`[data-slot=\"${slot}\"]`)\n if (anchor === null) return null\n if (side === 'child') {\n const el = anchor.firstElementChild as HTMLElement | null\n return el !== null && el.getBoundingClientRect().height > 0 ? el : null\n }\n let el: HTMLElement | null = anchor.parentElement\n for (let depth = 0; depth < 4 && el !== null && el !== frame; depth++) {\n if (el.getBoundingClientRect().height > 0) return el\n el = el.parentElement\n }\n return null\n}\n\n/** The laid-out box behind a slot (see `slotElement`), or null if the slot is\n * not currently present or not yet laid out. */\nexport function slotBox(frame: HTMLElement, slot: string, side: 'child' | 'ancestor'): DOMRect | null {\n return slotElement(frame, slot, side)?.getBoundingClientRect() ?? null\n}\n\n/** The DSH shell frame \u2014 the element that hosts the sidebar, conversation\n * column and fixed composer \u2014 found by walking up from any node inside our\n * own plugin root via the stable `[data-shell-overlay]` anchor. */\nexport function getShellFrame(node: HTMLElement | null): HTMLElement | null {\n const overlay = node !== null && typeof node.closest === 'function' ? node.closest('[data-shell-overlay]') : null\n return (overlay?.parentElement as HTMLElement | null) ?? null\n}\n\n/** The composer's laid-out element (the seat that reserves its space, not the\n * 0\u00D70 slot wrapper), or null if the slot is not present in the current DOM\n * (host markup changed, or mid-navigation). Callers that only need the box\n * can call `.getBoundingClientRect()`; callers that want to observe size\n * changes (e.g. `ResizeObserver`) need the element itself.\n *\n * `frame` may be null: the floating widget lives inside the shell overlay\n * subtree, but the dashboard's plugin root is mounted in the conversation\n * pane and cannot reach the shell frame that way. A frame-less call falls\n * back to a document-wide lookup \u2014 the composer is unique in the active\n * shell, so this cannot hit an ambiguous element. */\nexport function getComposerElement(frame: HTMLElement | null): HTMLElement | null {\n if (frame === null) {\n return slotElement(document.documentElement, 'conversation.composer.dock', 'ancestor')\n }\n return slotElement(frame, 'conversation.composer.dock', 'ancestor')\n}\n\n/**\n * The composer's *input card* \u2014 the rounded panel that visually IS the input\n * box (textarea + send row, ~780px max in DSH). The `.dock` seat found by\n * {@link getComposerElement} spans the whole pane, so its width is the wrong\n * answer for any width-parity goal: the visible input card is narrower, and\n * it is the column the dashboard cards must align with. The card is located\n * from the textarea's ancestor chain \u2014 the first ancestor with a visible\n * background and a non-zero border radius, the shape DSH gives the input\n * panel. Returns null when the host markup stops matching; callers then fall\n * back to {@link getComposerElement}. */\nexport function getComposerInputElement(frame: HTMLElement | null): HTMLElement | null {\n const root = frame ?? document.documentElement\n const textarea = root.querySelector(\n '[data-slot=\"conversation.composer\"] textarea, [data-slot=\"conversation.composer.bar\"] textarea, textarea',\n )\n if (textarea === null) return null\n let el = textarea.parentElement\n for (let depth = 0; depth < 6 && el !== null; depth++) {\n const cs = getComputedStyle(el)\n if (cs.borderRadius !== '0px' && cs.backgroundColor !== 'rgba(0, 0, 0, 0)') return el\n el = el.parentElement\n }\n return null\n}\n\n/** Resolve the active `conversation.view` id from the host's semantic tablist.\n * The DOM intentionally carries no view id, but it renders buttons in the\n * exact order of the slot ledger. Indexing into that same live ledger avoids\n * translated-label matching and remains compatible with third-party tabs. */\nexport function getActiveConversationViewId(\n frame: HTMLElement | null,\n viewIds: string[],\n): string | null {\n if (viewIds.length === 0) return null\n const header = frame === null ? null : slotElement(frame, 'conversation.session.header', 'child')\n const tablist = header?.querySelector('[role=\"tablist\"]') ?? null\n if (tablist === null) return viewIds[0] ?? null\n const tabs = [...tablist.querySelectorAll('[role=\"tab\"]')]\n const activeIndex = tabs.findIndex(tab => tab.getAttribute('aria-selected') === 'true')\n return activeIndex < 0 ? viewIds[0] ?? null : viewIds[activeIndex] ?? null\n}\n", "import type { UsageData } from '../contract.ts'\n\nconst BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000\n\n/** YYYYMMDD in DeepSeek's billing timezone. */\nexport function exportDateStamp(nowMs = Date.now()): string {\n const date = new Date(nowMs + BEIJING_OFFSET_MS)\n return [\n date.getUTCFullYear(),\n String(date.getUTCMonth() + 1).padStart(2, '0'),\n String(date.getUTCDate()).padStart(2, '0'),\n ].join('')\n}\n\n/** RFC 4180 cell escaping plus spreadsheet-formula neutralisation for labels. */\nexport function csvCell(value: string | number): string {\n let text = String(value)\n if (typeof value === 'string' && /^[=+\\-@]/.test(text)) text = `'${text}`\n return /[\",\\r\\n]/.test(text) ? `\"${text.replaceAll('\"', '\"\"')}\"` : text\n}\n\nconst csv = (rows: Array<Array<string | number>>): string =>\n `\\uFEFF${rows.map(row => row.map(csvCell).join(',')).join('\\r\\n')}\\r\\n`\n\nexport function dailyUsageCsv(usage: UsageData): string {\n return csv([\n ['date', 'input_tokens', 'output_tokens', 'cache_hit_tokens', 'total_tokens', 'calls', 'estimated_cost_cny'],\n ...usage.daily.map(day => [day.date, day.input, day.output, day.cache, day.total, day.calls, day.cost]),\n ])\n}\n\nexport function modelUsageCsv(usage: UsageData): string {\n return csv([\n ['provider', 'model', 'input_tokens', 'output_tokens', 'cache_hit_tokens', 'total_tokens', 'calls', 'estimated_cost_cny'],\n ...usage.models.map(model => [\n model.provider, model.model, model.input, model.output, model.cache, model.total, model.calls, model.cost,\n ]),\n ])\n}\n\nexport function fullUsageJson(usage: UsageData, nowMs = Date.now()): string {\n return `${JSON.stringify({\n schemaVersion: 1,\n exportedAt: new Date(nowMs).toISOString(),\n scope: 'local-dsh-session-logs',\n data: usage,\n }, null, 2)}\\n`\n}\n\nexport function downloadText(filename: string, text: string, mediaType: string): void {\n const url = URL.createObjectURL(new Blob([text], { type: mediaType }))\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = filename\n anchor.hidden = true\n document.body.appendChild(anchor)\n anchor.click()\n anchor.remove()\n window.setTimeout(() => URL.revokeObjectURL(url), 0)\n}\n", "import { fallbackT, type Translate } from './i18n.tsx'\n\nexport type SyncState = 'syncing' | 'fresh' | 'cached' | 'fallback' | 'error'\n\n/** Short, stable freshness copy for the dashboard status row. */\nexport function updatedText(updatedAt: number | null, nowMs = Date.now(), t: Translate = fallbackT): string {\n if (updatedAt === null || !Number.isFinite(updatedAt)) return t('fresh.none')\n const elapsed = Math.max(0, nowMs - updatedAt)\n if (elapsed < 60_000) return t('fresh.justNow')\n const minutes = Math.floor(elapsed / 60_000)\n if (minutes < 60) return t('fresh.minutes', { count: minutes })\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return t('fresh.hours', { count: hours })\n return t('fresh.days', { count: Math.floor(hours / 24) })\n}\n\nexport function syncStatusText(state: SyncState, updatedAt: number | null, nowMs = Date.now(), t: Translate = fallbackT): string {\n const age = updatedText(updatedAt, nowMs, t)\n if (state === 'syncing') return updatedAt === null ? t('sync.first') : t('sync.syncing', { age })\n if (state === 'fresh') return t('sync.fresh', { age })\n if (state === 'cached') return t('sync.cached', { age })\n if (state === 'fallback') return t('sync.fallback', { age })\n return t('sync.error')\n}\n", "import { fallbackT, type Translate } from './i18n.tsx'\n\n/** Value axis shared by daily, hourly, and per-model usage charts. */\nexport const CHART_METRICS = ['tokens', 'cost', 'calls'] as const\nexport type ChartMetric = (typeof CHART_METRICS)[number]\n\nexport interface ChartMetricPoint {\n total: number\n cost: number\n calls: number\n}\n\nexport const isChartMetric = (value: unknown): value is ChartMetric =>\n typeof value === 'string' && CHART_METRICS.some(metric => metric === value)\n\nexport function chartMetricValue(metric: ChartMetric, point: ChartMetricPoint): number {\n if (metric === 'cost') return point.cost\n if (metric === 'calls') return point.calls\n return point.total\n}\n\nexport function chartMetricName(metric: ChartMetric, t: Translate = fallbackT): string {\n if (metric === 'cost') return t('metric.cost')\n if (metric === 'calls') return t('metric.calls')\n return t('metric.usage')\n}\n", "/**\n * localStorage-backed user preferences for the plugin.\n *\n * Everything the user deliberately sets (widget visibility, the corner it was\n * dragged to, whether it is collapsed) belongs here so a page refresh does not\n * throw the choice away. Keys share one prefix and carry a version suffix so\n * the shape can evolve without reading back stale values.\n *\n * Every read is validated by the caller's guard: a corrupted or hand-edited\n * entry falls back instead of poisoning the UI.\n */\n\nconst PREFIX = 'dsh-usage-dashboard:pref:'\nconst VERSION = 1\n\nconst keyOf = (key: string): string => `${PREFIX}${key}:v${VERSION}`\n\nexport function loadPref<T>(key: string, isValid: (value: unknown) => boolean, fallback: T): T {\n if (typeof localStorage === 'undefined') return fallback\n try {\n const raw = localStorage.getItem(keyOf(key))\n if (raw === null) return fallback\n const parsed = JSON.parse(raw) as unknown\n return isValid(parsed) ? (parsed as T) : fallback\n } catch {\n return fallback\n }\n}\n\nexport function savePref(key: string, value: unknown): void {\n if (typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(keyOf(key), JSON.stringify(value))\n } catch {\n // Quota / private-mode failures must never break the UI.\n }\n}\n\nexport const isBoolean = (value: unknown): boolean => typeof value === 'boolean'\n", "/**\n * Package-local stores for settings both surfaces read: the dashboard writes\n * them, the floating widget reacts. Every value is persisted (see ./prefs.ts),\n * so a choice survives a page refresh.\n */\nimport { USAGE_WINDOW_DAYS, type UsageWindowDays } from '../contract.ts'\nimport { isChartMetric, type ChartMetric } from './metric.ts'\nimport { isBoolean, loadPref, savePref } from './prefs.ts'\n\nexport interface Store<T> {\n get(): T\n set(value: T): void\n subscribe(fn: () => void): () => void\n}\n\nfunction createStore<T>(key: string, isValid: (value: unknown) => boolean, fallback: T): Store<T> {\n let current = loadPref<T>(key, isValid, fallback)\n const listeners = new Set<() => void>()\n return {\n get: () => current,\n set: (value: T) => {\n current = value\n savePref(key, value)\n for (const listener of [...listeners]) listener()\n },\n subscribe: (fn: () => void) => {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n }\n}\n\nconst isThreshold = (value: unknown): boolean =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0\n\n/** Whether the floating balance widget is shown. */\nexport const widgetVisibleStore = createStore<boolean>('widget.visible', isBoolean, true)\n\nconst isWidgetTabIds = (value: unknown): boolean => value === null\n || (Array.isArray(value) && value.every(id => typeof id === 'string' && id !== '')\n && new Set(value).size === value.length)\n\n/** Conversation view ids on which the widget is allowed to appear. `null`\n * means \"all available views\" and preserves the pre-setting default; once a\n * user customizes the list, newly installed plugin tabs arrive unchecked. */\nexport const widgetTabIdsStore = createStore<string[] | null>('widget.tabIds', isWidgetTabIds, null)\n\n/** Balance (in the account's own currency) below which both surfaces warn.\n * 0 turns the warning off. */\nexport const lowBalanceStore = createStore<number>('alert.lowBalance', isThreshold, 10)\n\n/** Monthly estimated-cost budget in CNY. 0 leaves budget tracking disabled. */\nexport const monthlyBudgetStore = createStore<number>('budget.monthly', isThreshold, 0)\n\nconst isUsageWindowDays = (value: unknown): value is UsageWindowDays =>\n typeof value === 'number' && USAGE_WINDOW_DAYS.some(days => days === value)\n\n/** Shared time range for the usage chart and both cost rankings. */\nexport const usageWindowStore = createStore<UsageWindowDays>('usage.windowDays', isUsageWindowDays, 30)\n\n/** Whether charts compare token volume, estimated CNY cost, or call count. */\nexport const chartMetricStore = createStore<ChartMetric>('usage.chartMetric', isChartMetric, 'tokens')\n\n/** Ephemeral presence of the full dashboard. The widget uses it to step out of\n * the way without changing the user's persisted visibility preference. */\nlet quotaViewActive = false\nconst quotaViewListeners = new Set<() => void>()\nexport const quotaViewActiveStore: Store<boolean> = {\n get: () => quotaViewActive,\n set: (value) => {\n if (value === quotaViewActive) return\n quotaViewActive = value\n for (const listener of [...quotaViewListeners]) listener()\n },\n subscribe: (fn) => {\n quotaViewListeners.add(fn)\n return () => {\n quotaViewListeners.delete(fn)\n }\n },\n}\n", "/** Estimated spend contributed beside each completed chat turn. */\nimport { useEffect, useState, type ReactElement } from 'react'\nimport type { SessionUsageData, TurnUsage } from '../contract.ts'\nimport { fetchSessionUsage, getCachedSessionUsage, subscribeSessionUsage } from './api.ts'\nimport { useI18n } from './i18n.tsx'\n\nexport interface MessageCostProps {\n sessionId: string\n messageId: string\n}\n\n/** Small costs need more than the dashboard's two-decimal money formatter or\n * almost every individual turn would misleadingly read as \u00A50.00. */\nexport const fmtTurnCost = (cost: number): string => cost.toLocaleString(undefined, {\n minimumFractionDigits: cost > 0 && cost < 0.01 ? 4 : 2,\n maximumFractionDigits: cost > 0 && cost < 0.01 ? 6 : 4,\n})\n\nconst cachedData = (sessionId: string): SessionUsageData | null => {\n const response = getCachedSessionUsage(sessionId)\n return response?.ok === true ? response.data ?? null : null\n}\n\nexport function MessageCost({ sessionId, messageId }: MessageCostProps): ReactElement | null {\n const { t } = useI18n()\n const [data, setData] = useState<SessionUsageData | null>(() => cachedData(sessionId))\n\n useEffect(() => {\n setData(cachedData(sessionId))\n const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData(sessionId)))\n const hit = cachedData(sessionId)?.turns.some(turn => turn.messageId === messageId) ?? false\n if (!hit) void fetchSessionUsage(sessionId)\n return unsubscribe\n }, [messageId, sessionId])\n\n const turn: TurnUsage | undefined = data?.turns.find(candidate => candidate.messageId === messageId)\n if (turn === undefined) return null\n const amount = `\u00A5 ${fmtTurnCost(turn.cost)}`\n const tooltip = t('messageCost.tooltip')\n return (\n <span\n className=\"dq-message-cost\"\n tabIndex={0}\n data-dq-tooltip={tooltip}\n aria-label={t('messageCost.aria', { amount })}\n >\n <span className=\"dq-message-cost-separator\" aria-hidden>\u00B7</span>\n <span className=\"dq-message-cost-amount\">{amount}</span>\n </span>\n )\n}\n", "/** All plugin styles in one sheet, injected once at client load. */\n\nexport const css = `\n.dsh-quota-root{position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.45;color:var(--dsw-alias-label-primary,#1f2328);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}\n.dsh-quota-root.dsh-quota-root--hidden{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .14s ease-out,visibility 0s linear .14s}\n.dsh-quota-root.dsh-quota-dragging{transition:none}\n.dsh-quota-card,.dsh-quota-card *{box-sizing:border-box}\n.dsh-quota-card{background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);padding:12px 14px;min-width:208px;max-width:270px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}\n.dsh-quota-root.dsh-quota-dragging .dsh-quota-card{cursor:grabbing;box-shadow:0 12px 32px rgba(0,0,0,.28)}\n.dsh-quota-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;min-width:0}\n.dsh-quota-title{font-weight:600;font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden}\n.dsh-quota-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-quota-grip{color:var(--dsw-alias-label-secondary,#59636e);opacity:.7;font-size:11px;letter-spacing:0;cursor:grab;flex:none}\n.dsh-quota-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#2da44e);flex:none}\n.dsh-quota-dot--error{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dsh-quota-dot--warn{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dsh-quota-total--low{color:var(--dsw-alias-state-warning-primary,#9a6700)!important}\n.dsh-quota-dot--idle{background:var(--dsw-alias-label-secondary,#59636e);opacity:.6}\n.dsh-quota-collapsed-total-row{margin-top:6px}\n.dsh-quota-collapsed-total{font-weight:700;font-size:15px;color:var(--dsw-alias-state-business-primary,#0969da);font-variant-numeric:tabular-nums}\n.dsh-quota-actions{display:flex;gap:2px;flex:none}\n.dsh-quota-btn{border:none;background:transparent;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);padding:3px 7px;border-radius:6px;font-size:13px;line-height:1}\n.dsh-quota-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#1f2328)}\n.dsh-quota-btn:active:not(:disabled){transform:scale(.92)}\n.dsh-quota-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dsh-quota-btn:disabled{opacity:.5;cursor:default}\n.dsh-quota-body{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-remaining-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dsh-quota-total{font-size:24px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328);letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n.dsh-quota-currency{font-size:14px;font-weight:600;margin-left:3px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-row{display:flex;justify-content:space-between;gap:12px;margin-top:6px;font-size:12px}\n.dsh-quota-label{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-value{color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dsh-quota-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-message-cost{position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}\n.dq-message-cost-separator{cursor:default}\n.dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}\n.dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}\n.dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}\n.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.45;text-align:left;white-space:normal;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(-3px);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear .12s}\n.dq-message-cost:hover::after,.dq-message-cost:focus-visible::after{opacity:1;visibility:visible;transform:translateY(0);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear}\n@media (hover:hover){\n [data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}\n [data-time-hover-root]:hover .dq-message-cost,[data-time-hover-root]:focus-within .dq-message-cost{opacity:1}\n}\n/* \u5361\u7247\u95F4\u8DDD\uFF08\u6B64\u5904 gap\uFF09\u523B\u610F\u5927\u4E8E\u5361\u5185\u5C0F\u8282\u95F4\u8DDD\uFF08margin-top:12/14/16px \u90A3\u4E00\u6863\uFF09\uFF0C\u5236\u9020\"\u5361\u7247\u5916\u758F\u3001\u5361\u7247\u5185\u5BC6\"\u7684\n \u758F\u5BC6\u5BF9\u6BD4\uFF1A24px \u76F8\u5BF9\u5361\u5185\u6700\u5927\u6863 16px \u4ECD\u6709 1.5 \u500D\u7EA7\u5DEE\uFF0C\u76F8\u5BF9\u5E38\u89C1\u6863 12px \u6709 2 \u500D\u7EA7\u5DEE\uFF0C\u4E0D\u4E0E .dq-card \u7684\n padding\uFF0816px/20px\uFF0C\u5361\u7247\u5185\u90E8\u547C\u5438\u611F\uFF0C\u8F6E\u6B21 36 \u5DF2\u5B9A\uFF09\u6DF7\u540C\u3002 */\n/* Width parity with the host composer: the dashboard column reserves the\n same max-width as the conversation reading column (860px, \u8F6E\u6B21 42 kept it\n deliberately), but on narrower viewports the fixed 860px would overhang\n the input box \u2014 measured in dashboard.tsx as --dq-composer-w, capped at\n the reading-column width so wide viewports are unchanged. */\n.dq-balance{display:flex;flex-direction:column;gap:24px;padding:20px 24px 48px;max-width:min(860px,var(--dq-composer-w,860px));margin:0 auto;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n@media (max-width:620px){.dq-balance{box-sizing:border-box;max-width:calc(100vw - 56px);padding:16px 16px calc(var(--dq-composer-h,126px) + 16px)}}\n.dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px;min-width:0;max-width:100%}\n.dq-card-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 12px;letter-spacing:.02em}\n.dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}\n.dq-card-head .dq-card-title{margin:0}\n.dq-card-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0}\n/* \u4E3B\u5361\u7247\uFF08\u4F59\u989D/\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017/\u6D88\u8017\u6982\u89C8/\u7528\u91CF\u8D8B\u52BF\uFF0C\u8F6E\u6B21 45 \u65B0\u589E\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\uFF09\u89C6\u89C9\u6743\u91CD\u9AD8\u4E8E\u6B21\u7EA7\u5206\u6790\n \u5361\u7247\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF1A\u53EA\u8C03\u6392\u7248\u5C5E\u6027\u2014\u2014\u5185\u8FB9\u8DDD\u3001\u6807\u9898\u5B57\u53F7\u5B57\u91CD\u4E0E\u53D6\u8272\u3001\u8FB9\u6846\u6DF1\u6D45\u2014\u2014\u4E0D\u5F15\u5165\u65B0\u914D\u8272\u4F53\u7CFB\u3002\u4E24\u6761\u89C4\u5219\n \u4E0E\u4E0A\u9762\u7684\u57FA\u7840\u89C4\u5219\u540C\u4E3A\u5355\u7C7B\u9009\u62E9\u5668\u4E14\u6E90\u7801\u987A\u5E8F\u66F4\u9760\u540E\uFF0C\u5929\u7136\u8986\u76D6\uFF0C\u4E0D\u9700\u8981 !important\u3002\u6B21\u7EA7\u5206\u6790\u5361\u7247\uFF08\u7528\u91CF\n \u70ED\u529B\u56FE/\u9AD8\u5CF0\u95F2\u65F6/\u7F13\u5B58\u547D\u4E2D/\u6A21\u578B\u6392\u884C/\u4F1A\u8BDD\u6392\u884C\uFF09\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF08\u8BBE\u7F6E\uFF0C\u542B\u8F6E\u6B21 39 \u5E76\u5165\u7684\u5B98\u65B9\u5E73\u53F0\u94FE\u63A5\uFF09\u90FD\u4E0D\u52A0\n \u8FD9\u4E2A\u4FEE\u9970\u7C7B\uFF0C\u7EF4\u6301\u539F\u6709 .dq-card \u57FA\u7EBF\uFF0C\u5F62\u6210\u4E24\u6863\u6743\u91CD\u3002\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\u5347\u7EA7\u4E3A\u4E3B\u5361\u7247\u7684\u7406\u7531\uFF1A\u5B83\u548C\u4F59\u989D\u4E00\u6837\u662F\n \u6253\u5F00\u8FD9\u4E2A\u300C\u989D\u5EA6\u300Dtab \u65F6\u4E0A\u4E0B\u6587\u6700\u76F4\u63A5\u76F8\u5173\u7684\u6570\u5B57\uFF08\u8FD9\u4E2A tab \u6302\u5728\u5177\u4F53\u4F1A\u8BDD\u5185\u90E8\uFF0C\u89C1 DESIGN_NOTES\u300C\u4FE1\u606F\u5C42\u7EA7\u300D\uFF09\uFF0C\n \u4E0D\u5C5E\u4E8E\u539F\u6709\u8D26\u6237\u5168\u5C40\u5C42\u7EA7 1-3 \u7684\u4EFB\u4F55\u4E00\u7EA7\uFF0C\u800C\u662F\u4E0E\u4E4B\u6B63\u4EA4\u7684\u53E6\u4E00\u4E2A\u7EF4\u5EA6\uFF0C\u4F46\u540C\u6837\u662F\u9996\u5C4F\u5FC5\u8BFB\uFF0C\u56E0\u6B64\u540C\u6837\u7ED9\n primary \u6743\u91CD\uFF0C\u4E0D\u65B0\u5F00\u7B2C\u4E09\u6863\u3002 */\n.dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dq-card--primary .dq-card-title{font-size:13px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.08))}\n.dq-window-btn{border:0;border-radius:6px;padding:4px 8px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;white-space:nowrap;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}\n.dq-window-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.12)}\n.dq-window-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n@media (max-width:620px){.dq-card-head{align-items:flex-start;flex-wrap:wrap}.dq-card-actions{width:100%;justify-content:space-between}}\n.dq-export{position:relative;flex:none}\n.dq-export-btn{min-width:58px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease,background .15s ease}\n.dq-export-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-export-btn--done{border-color:rgba(45,164,78,.35);color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.08)}\n.dq-export-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:180px;padding:6px;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);display:flex;flex-direction:column;gap:2px}\n.dq-export-menu button{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;border:0;border-radius:6px;padding:7px 8px;background:transparent;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;text-align:left;cursor:pointer}\n.dq-export-menu button:hover,.dq-export-menu button:focus-visible{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-export-menu button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-1px}\n.dq-export-menu small{font-size:10.5px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap}\n/* Small-text tier system (\u8F6E\u6B21 41 \u6536\u655B): two whole-pixel tiers replace what used to be four values\n (11/11.5/12/12.5px) spaced only 0.5px apart \u2014 a gap browsers subpixel-round away, so it read as one\n size with extra maintenance cost, not an actual visual hierarchy. 11px = \"micro\" chrome the user\n scans rather than reads (dt-style stat/period labels, delta/status/coverage pills, share %, tags,\n the widget grip) \u2014 kept as-is. 12px = \"secondary text\" (section titles, hint/footnote/legend/sub\n prose, table cells, tooltip copy, alert/empty-state sentences, and filter/segmented-control button\n labels like .dq-window-btn/.dq-chart-switch-btn, which are the same control family and should share\n one size) \u2014 absorbed the old 11.5px and 12.5px selectors, all by rounding toward the nearer tier\n (11.5\u219212 grows slightly for legibility, safe because none of the affected containers are width-\n capped; 12.5\u219212 shrinks by 0.5px on two full-sentence blocks, imperceptible). Weight/color still do\n the job of separating titles from body text within the 12px tier, same as before this merge. Do not\n reintroduce a fractional-px value in this range; if a selector needs to stand apart from these two,\n it belongs in an already-established different tier (10px icon glyphs, 13px+ hero-adjacent text). */\n.dq-balance-grid{display:flex;flex-wrap:wrap;gap:12px 24px}\n.dq-stat{min-width:110px}\n.dq-stat-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dq-stat-value{font-size:18px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-stat-value--ok{color:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-stat-value--bad{color:var(--dsw-alias-state-error-primary,#cf222e)}\n/* \u8F6E\u6B21 43: .dq-runway is a controlled <details> (see dashboard.tsx) \u2014 hover/toggle state\n drives the real open attribute now, so .dq-runway-detail only needs to react to [open];\n an unopened <details>'s non-summary children don't actually render in current Chromium no\n matter what display value author CSS gives them (:hover{display:block} alone is a dead end). */\n.dq-runway{position:relative}\n.dq-runway>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-runway>summary::-webkit-details-marker{display:none}\n.dq-runway>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-runway-detail{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;max-width:240px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:normal;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-runway[open] .dq-runway-detail{display:block}\n/* hero number for the balance, matching .dq-period-cost; relies on cascade order (declared after\n .dq-stat-value, same specificity) to win over the .dq-stat-value it's paired with in JSX \u2014 keep it below */\n/* \u8F6E\u6B21 47: .dq-remaining is a controlled <details> too now (see dashboard.tsx), same reason as\n .dq-runway above \u2014 an unopened <details>'s non-summary children don't actually render in current\n Chromium regardless of author display value, so the old :hover{display:flex} branch was dead code. */\n.dq-remaining{position:relative;font-size:22px;font-weight:680;letter-spacing:-.01em;line-height:1.25}\n.dq-remaining>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-remaining>summary::-webkit-details-marker{display:none}\n.dq-remaining>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-remaining[open] .dq-remaining-breakdown{display:flex}\n.dq-remaining-granted{color:var(--dsw-alias-label-tertiary,#59636e);font-weight:400;font-size:11px}\n/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned\n .dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the\n next row (e.g. \"\u4F59\u989D\u660E\u7EC6\" open covering \"\u9884\u8BA1\u53EF\u7528\"'s clickable summary) \u2014 force each stat onto its own\n full-width row and switch both popovers to normal in-flow layout so an open one pushes its own row\n taller instead of overlaying a sibling; desktop keeps the original hover-overlay behavior unchanged */\n@media (max-width:620px){\n .dq-balance-grid .dq-stat{flex:1 1 100%}\n .dq-remaining-breakdown,.dq-runway-detail{position:static;margin-top:6px;box-shadow:none;max-width:none;white-space:normal}\n}\n.dq-period-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0}\n.dq-period{min-width:0;padding:4px 16px}\n.dq-period:first-child{padding-left:0}\n.dq-period:last-child{padding-right:0}\n.dq-period+.dq-period{border-left:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n@media (max-width:620px){.dq-period-grid{grid-template-columns:1fr}.dq-period,.dq-period:first-child,.dq-period:last-child{padding:12px 0}.dq-period:first-child{padding-top:2px}.dq-period+.dq-period{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-left:0}}\n.dq-period-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px}\n.dq-period-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-period-cost{font-size:22px;font-weight:680;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;line-height:1.25}\n.dq-period-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-top:2px;font-variant-numeric:tabular-nums;line-height:1.45}\n.dq-budget{margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-budget--unset{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-budget-action{border:0;background:transparent;padding:3px 4px;border-radius:5px;color:var(--dsw-alias-state-business-primary,#0969da);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer}\n.dq-budget-action:hover{text-decoration:underline;text-underline-offset:3px}\n.dq-budget-action:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dq-budget-head,.dq-budget-meta{display:flex;align-items:center;justify-content:space-between;gap:12px}\n.dq-budget-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-budget-amount{font-size:12px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-budget-track{height:8px;margin-top:8px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-budget-fill{width:100%;height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transform-origin:left center;transition:transform .35s cubic-bezier(.22,1,.36,1)}\n.dq-budget--risk .dq-budget-fill{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-budget--over .dq-budget-fill{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-budget-meta{margin-top:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-budget--risk .dq-budget-meta span:last-child{color:var(--dsw-alias-state-warning-primary,#9a6700);font-weight:600}\n.dq-budget--over .dq-budget-meta{color:var(--dsw-alias-state-error-primary,#cf222e);font-weight:600}\n@media (max-width:620px){.dq-budget-head,.dq-budget-meta,.dq-budget--unset{align-items:flex-start;flex-direction:column;gap:4px}}\n.dq-delta{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:600;padding:1px 6px;border-radius:999px;white-space:nowrap;cursor:help;font-variant-numeric:tabular-nums}\n.dq-delta--up{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-delta--down{color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.14)}\n.dq-delta--flat,.dq-delta--new{color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14);font-weight:500}\n.dq-delta-label{font-weight:400;opacity:.8}\n.dq-pricing{margin-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-top:10px}\n.dq-pricing-summary{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);cursor:pointer;list-style:none;width:fit-content;border-radius:6px;padding:2px 4px;margin-left:-4px}\n.dq-pricing-summary::-webkit-details-marker{display:none}\n.dq-pricing-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-pricing[open] .dq-pricing-summary::before{transform:rotate(90deg)}\n.dq-pricing-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-pricing-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-pricing-now{font-size:11px;font-weight:600;padding:1px 7px;border-radius:999px;color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14)}\n.dq-pricing-now--peak{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-pricing-body{margin-top:10px;overflow-x:auto}\n.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}\n.dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}\n.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}\n.dq-pricing-foot{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage{margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-coverage-summary{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;cursor:pointer;list-style:none;width:fit-content;max-width:100%;margin-left:-4px;padding:3px 4px;border-radius:6px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-summary::-webkit-details-marker{display:none}\n.dq-coverage-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-coverage[open] .dq-coverage-summary::before{transform:rotate(90deg)}\n.dq-coverage-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-coverage-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-status{padding:1px 7px;border-radius:999px;background:rgba(45,164,78,.14);color:var(--dsw-alias-state-success-primary,#1a7f37);font-size:11px;font-weight:600}\n.dq-coverage--warn .dq-coverage-status{background:rgba(191,135,0,.14);color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-coverage-brief{font-size:12px;font-variant-numeric:tabular-nums}\n.dq-coverage-body{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-coverage-metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 20px;margin:0}\n.dq-coverage-metrics div{min-width:0}\n.dq-coverage-metrics dt{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-coverage-metrics dd{margin:3px 0 0;font-size:13px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-coverage-range,.dq-coverage-note,.dq-coverage-warning{margin:9px 0 0;font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-range{font-variant-numeric:tabular-nums}\n.dq-coverage-warning{padding:8px 10px;border-radius:8px;background:rgba(191,135,0,.10);color:var(--dsw-alias-state-warning-primary,#7a5200)}\n@media (max-width:620px){.dq-coverage-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.dq-coverage-brief{flex-basis:100%;margin-left:19px}}\n.dq-peak-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85;overflow:hidden}\n.dq-peak-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-peak-legend{display:flex;flex-wrap:wrap;gap:6px 20px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-peak-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}\n.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:14px}\n.dq-peak-delta{font-size:11px;font-weight:500;margin-left:8px;color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-peak-delta--save{color:var(--dsw-alias-state-success-primary,#1a7f37)}\n.dq-peak-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}\n.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:550}\n.dq-stat-value--warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-cache-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);overflow:hidden}\n.dq-cache-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-cache-legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-cache-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--hit{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-legend-swatch--miss{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-cache-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-sessions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}\n.dq-session{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-session-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-session-title{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-session-cost{font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-session-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-session-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-business-primary,#4176e6);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-session-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n/* The \u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300Dcard uses this class on a bare <p> (no flex-gap parent\n to lean on, unlike the .dq-session usage above), so it needs its own\n explicit spacing rather than falling back to the browser's implicit <p>\n margin \u2014 this project always declares spacing explicitly. */\np.dq-session-sub{margin:8px 0}\n.dq-session-foot{margin:12px 0 0;font-size:12px;color:var(--dsw-alias-label-tertiary,#59636e)}\n/* \u8F6E\u6B21 46: each ranking row is now a <details> whose <summary> wraps the existing\n head/track/sub trio unchanged (in a flex-column body div) plus a leading chevron \u2014\n same disclosure grammar as .dq-pricing/.dq-coverage above (hidden native marker,\n rotating glyph, :focus-visible ring), just applied to a multi-line summary instead\n of a single line. .dq-session-details needs its own min-width:0 because it is now\n the flex item inside .dq-session that the ellipsis chain must shrink through. */\n.dq-session-details{min-width:0}\n.dq-session-details>summary{list-style:none;cursor:pointer}\n.dq-session-details>summary::-webkit-details-marker{display:none}\n.dq-session-summary{display:flex;align-items:flex-start;gap:8px;border-radius:8px;padding:4px;margin:-4px}\n.dq-session-summary:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}\n.dq-session-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-session-chevron{flex:none;width:12px;margin-top:4px;font-size:10px;line-height:1;color:var(--dsw-alias-label-tertiary,#59636e);transition:transform .15s ease}\n.dq-session-details[open] .dq-session-chevron{transform:rotate(90deg)}\n.dq-session-summary-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}\n/* Expanded body reuses the same shaded-inset language as .dq-coverage-body (10px\n gap from the summary, 11px/12px padding, 10px radius, bg-layer-2) rather than\n inventing a new \"nested panel\" look. SessionUsageCard renders inside unchanged. */\n.dq-session-expand{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-rank{display:flex;flex-direction:column;gap:14px}\n.dq-rank-row{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-rank-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-rank-name{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-rank-cost{display:inline-flex;align-items:baseline;gap:8px;font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-rank-share{font-size:11px;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);min-width:30px;text-align:right}\n.dq-rank-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-rank-fill{height:100%;border-radius:999px;transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-rank-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n.dq-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;font-size:12px;line-height:1.6;color:var(--dsw-alias-state-warning-primary,#7a5200);background:rgba(191,135,0,.12);border:1px solid rgba(191,135,0,.35)}\n.dq-alert-icon{flex:none;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#bf8700);color:#fff;font-weight:700;font-size:12px;line-height:18px;text-align:center}\n.dq-alert strong{font-weight:650}\n.dq-alert-link{margin-left:8px;color:inherit;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}\n.dq-setting{display:flex;align-items:flex-start;gap:12px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-setting-label{font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}\n.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}\n.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}\n/* Page footer: the package name + version, injected at build time from\n package.json (see build.mjs) so it can never drift from the release. */\n.dq-footer{margin:18px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}\n.dq-links-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}\n.dq-links{display:flex;flex-wrap:wrap;gap:8px}\n.dq-link{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-state-business-primary,#0969da);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}\n.dq-link:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}\n/* .dq-toggle is the first settings control after the links row (\u8F6E\u6B21 39 merge) \u2014 its own\n border-top separates the \"\u5FEB\u6377\u5165\u53E3\" links (originally the standalone \u5B98\u65B9\u5E73\u53F0 card, renamed\n in \u8F6E\u6B21 39 fix-round-1 to read as part of \u8BBE\u7F6E's own navigation area) from \u8BBE\u7F6E controls\n inside the merged card, same 14px/14px/border-l1 recipe as .dq-setting so the two\n sub-sections read at equal weight. */\n.dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-toggle input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}\n.dq-toggle-hint{margin:5px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:7px}\n.dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:6px 14px}\n.dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;cursor:pointer}\n.dq-widget-tab input{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-toggle input:focus-visible,.dq-model-item input:focus-visible,.dq-widget-tab input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-link:focus-visible,.dq-model-btn:focus-visible,.dq-chart-switch-btn:focus-visible,.dq-export-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-usage-totals{display:flex;flex-wrap:wrap;gap:12px 24px;margin-bottom:16px}\n/* value itself (number+unit, e.g. \"9999.9\u4E07\") must never break across lines inside its own column \u2014\n nowrap+ellipsis is a defensive floor for all widths; the actual fix for the realistic 320px 2-col\n case is the font-size drop below, which keeps every measured stress value comfortably under the\n 83px column width without ever hitting the ellipsis fallback (see round 38 PROGRESS entry) */\n.dq-usage-totals .dq-stat-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.dq-vision-share{display:inline-block;margin-left:6px;font-size:11px;font-weight:550;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-vision-sessions{margin-top:12px;display:flex;flex-direction:column;gap:8px;min-width:0}\n/* The vision card's session rows reuse .dq-session-head, but \u2014 unlike the\n ranking card's rows \u2014 they sit directly under .dq-session with no\n .dq-session-summary-body wrapper to carry min-width:0. Without the chain\n below, a long session title (nowrap) sets the row's min-content width and\n can stretch the card past its siblings / the container. */\n.dq-vision-sessions .dq-session{min-width:0}\n.dq-vision-sessions .dq-session-head{min-width:0}\n.dq-vision-sessions .dq-session-title{flex:1 1 auto;min-width:0}\n@media (max-width:620px){.dq-usage-totals{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}.dq-usage-totals .dq-stat{min-width:0}.dq-usage-totals .dq-stat-value{font-size:13px}}\n.dq-chart-peak{margin-left:10px;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}\n.dq-chart-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:16px 0 8px}\n.dq-chart-title:first-of-type{margin-top:0}\n.dq-chart-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:16px 0 8px}\n.dq-chart-block-head .dq-chart-title{margin:0}\n.dq-chart-controls{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:none;flex-wrap:wrap}\n@media (max-width:620px){.dq-chart-block-head{align-items:flex-start;flex-direction:column}.dq-chart-controls{width:100%}}\n.dq-model-picker{position:relative}\n.dq-model-btn{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}\n.dq-model-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-model-btn::after{content:\"\u25BE\";font-size:10px;opacity:.7}\n.dq-model-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:190px;max-height:280px;overflow-y:auto;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);padding:6px;display:flex;flex-direction:column;gap:2px}\n.dq-model-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;font-size:12px;cursor:pointer;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-model-item:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-model-item input{width:14px;height:14px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-model-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-model-item--all{border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-bottom:8px;margin-bottom:2px;border-radius:6px}\n.dq-model-tag{color:var(--dsw-alias-label-tertiary,#59636e);font-size:11px;flex:none}\n.dq-bar-group{display:flex;flex-direction:column-reverse;width:100%}\n.dq-bar-group .dq-bar{flex:0 0 auto;width:100%;border-radius:0}\n.dq-bar-group .dq-bar:first-child{border-radius:0 0 2px 2px}\n.dq-bar-group .dq-bar:last-child{border-radius:2px 2px 0 0}\n.dq-bar-group .dq-bar:not(:last-child){box-shadow:inset 0 1px 0 rgba(0,0,0,.18)}\n.dq-legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-legend-item{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch{width:10px;height:10px;border-radius:3px;flex:none}\n.dq-chart-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));border-radius:999px;flex:none}\n.dq-chart-switch-btn{border:none;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1;padding:5px 12px;border-radius:999px;cursor:pointer;transition:background .15s ease,color .15s ease}\n.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}\n.dq-chart-wrap{position:relative}\n.dq-bars-scroll{overflow-x:auto;overflow-y:hidden;padding-bottom:2px}\n.dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.55;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}\n.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}\n.dq-tip-head{font-weight:650;margin-bottom:2px}\n.dq-tip-row{opacity:.85}\n.dq-bars{display:flex;align-items:flex-end;gap:2px;width:100%}\n.dq-bar-col{flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch}\n.dq-bar{background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:2px 2px 0 0;min-height:1px;transition:filter .12s ease}\n.dq-bar-col{cursor:default}\n.dq-bar-col,.dq-bar,.dq-heat-cell{scroll-margin-block-start:24px;scroll-margin-block-end:160px}\n.dq-bar-col:hover .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar-col:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-bar-col--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar-col--pinned .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-heat-cell:not(.dq-heat-cell--pad):hover{outline:1.5px solid var(--dsw-alias-label-primary,#1f2328);outline-offset:1px}\n.dq-heat-cell:not(.dq-heat-cell--pad):focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-bar-label{font-size:9px;color:var(--dsw-alias-label-tertiary,#59636e);text-align:center;margin-top:3px;overflow:visible;white-space:nowrap}\n.dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}\n.dq-heat-scroll{overflow-x:auto;padding-bottom:2px}\n.dq-heatmap{display:flex;gap:3px}\n.dq-heat-week{display:flex;flex:1 1 0;min-width:6px;flex-direction:column;gap:3px}\n.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:3px;flex:none}\n.dq-heat-cell--pad{background:transparent}\n.dq-heat-months{display:flex;gap:3px;margin-top:4px}\n.dq-heat-month{flex:1 1 0;min-width:6px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap;overflow:visible}\n.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}\n.dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);align-self:flex-end}\n.dq-heat-scale>span:first-child{margin-right:2px}\n.dq-heat-scale>span:last-child{margin-left:2px}\n.dq-muted{color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-crash{margin:10px 0 14px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));border-radius:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary,#cf222e);white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\n.dq-empty{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.6;padding:6px 0}\n/* Error-state modifier for a card body that has nothing else to show (mirrors the .dq-stat-value--ok/\n --bad/--warn pattern: same layout as .dq-empty, declared after it so this color wins at equal\n specificity). Used by the current-session card when its own fetch failed and there is no prior\n data to fall back to \u2014 the status-row .dq-error/.dq-warn spans are for the account-wide balance/\n usage fetch, this is the per-card equivalent. */\n.dq-empty--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-skel{background:linear-gradient(90deg,rgba(125,135,155,.10) 25%,rgba(125,135,155,.20) 37%,rgba(125,135,155,.10) 63%);background-size:400% 100%;border-radius:5px;animation:dq-shimmer 1.5s ease-in-out infinite}\n.dq-stat .dq-skel+.dq-skel{margin-top:6px}\n.dq-skel-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:120px;margin-top:10px}\n.dq-skel-bar{flex:1 1 0;min-width:0;border-radius:2px 2px 0 0}\n@keyframes dq-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}\n.dq-status-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px}\n.dq-status-messages{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0}\n.dq-status-row .dq-warn,.dq-status-row .dq-error{font-size:12px;line-height:1.45}\n.dq-sync{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-sync-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--dsw-alias-label-tertiary,#8c959f)}\n.dq-sync--fresh .dq-sync-dot{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-sync--syncing .dq-sync-dot{background:var(--dsw-alias-state-business-primary,#4176e6);animation:dq-sync-pulse 1.2s ease-in-out infinite}\n.dq-sync--fallback{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-sync--fallback .dq-sync-dot{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-sync--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}\n@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}\n.dq-refresh-btn{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}\n.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-refresh-btn:disabled{cursor:default;opacity:.6}\n.dq-refresh-icon{display:inline-block;line-height:1}\n.dq-refresh-icon--spin{animation:dq-spin .9s linear infinite}\n@keyframes dq-spin{to{transform:rotate(360deg)}}\n@media (prefers-reduced-motion:reduce){\n .dsh-quota-root,.dsh-quota-root *,\n .dsh-quota-root *::before,.dsh-quota-root *::after,\n .dq-message-cost,.dq-message-cost::after,\n .dq-balance,.dq-balance *,\n .dq-balance *::before,.dq-balance *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}\n}\n`\n", "/** The draggable bottom-right floating balance widget.\n *\n * Where it sits (corner) and whether it is collapsed are user choices, so both\n * are persisted (see ./prefs.ts) and restored before the first paint; the\n * widget also re-snaps to its corner when the frame resizes (window resize,\n * sidebar collapse) instead of stranding itself at stale pixel coordinates.\n */\nimport { useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react'\nimport {\n fetchBalance,\n fetchSessionUsage,\n fetchUsage,\n getCachedBalance,\n getCachedSessionUsage,\n getCachedUsage,\n getCachedUsageAt,\n subscribeSessionUsage,\n subscribeUsage,\n} from './api.ts'\nimport { fmt } from './charts.tsx'\nimport type { BalanceData, SessionUsageData, UsageData } from '../contract.ts'\nimport { getActiveConversationViewId, getMainColumn, getShellFrame, slotBox } from './dom.ts'\nimport { localizeApiError, useI18n } from './i18n.tsx'\nimport { fmtTurnCost } from './message-cost.tsx'\nimport { isBoolean, loadPref, savePref } from './prefs.ts'\nimport { currentSessionIdOf, type SessionListStateFace } from './sessions.ts'\nimport { lowBalanceStore, quotaViewActiveStore, widgetTabIdsStore, widgetVisibleStore } from './store.ts'\nimport type { ConversationViewTab, ConversationViewsSource } from './views.ts'\n\nconst MARGIN = 16\n\ntype Corner = 'tl' | 'tr' | 'bl' | 'br'\n\nconst CORNER_KEY = 'widget.corner'\nconst COLLAPSED_KEY = 'widget.collapsed'\n\nconst isCorner = (value: unknown): boolean =>\n value === 'tl' || value === 'tr' || value === 'bl' || value === 'br'\n\ninterface Bounds {\n left: number\n top: number\n right: number\n bottom: number\n}\n\ninterface DragSession {\n pointerId: number\n startX: number\n startY: number\n originX: number\n originY: number\n width: number\n height: number\n bounds: Bounds\n}\n\n/** Below this the region is too short to park a widget in, so an exclusion is\n * skipped rather than squeezing the widget out of the frame. */\nconst MIN_REGION_HEIGHT = 140\n\n/**\n * Draggable area = the main column minus the session header and the composer.\n *\n * Using the main column (rather than the whole frame minus the sidebar) also\n * keeps the widget clear of the right-hand details panel when it is open.\n * The header is excluded so the widget can never park on the Chat / Trajectory\n * / \u989D\u5EA6 tab row and swallow its clicks, and the composer is excluded because a\n * bottom-right widget otherwise covers the model picker and the send button \u2014\n * the two controls the user reaches for most.\n */\nfunction getBounds(node: HTMLElement | null): Bounds {\n const fallback: Bounds = { left: 0, top: 0, right: 100000, bottom: 100000 }\n if (node === null) return fallback\n const frame = getShellFrame(node)\n if (frame === null) return fallback\n const frameRect = frame.getBoundingClientRect()\n let left = frameRect.left\n let right = frameRect.right\n let top = frameRect.top\n const mainRect = getMainColumn(frame)?.getBoundingClientRect() ?? null\n if (mainRect !== null && mainRect.width > 0) {\n left = mainRect.left\n right = mainRect.right\n top = mainRect.top\n } else {\n const sidebarRect = (frame.children[0] as HTMLElement | undefined)?.getBoundingClientRect() ?? null\n if (sidebarRect !== null) left = sidebarRect.right\n }\n let bottom = frameRect.bottom\n const headerRect = slotBox(frame, 'conversation.session.header', 'child')\n if (headerRect !== null && headerRect.bottom > top && headerRect.bottom < bottom - MIN_REGION_HEIGHT) {\n top = headerRect.bottom\n }\n const composerRect = slotBox(frame, 'conversation.composer.dock', 'ancestor')\n if (composerRect !== null && composerRect.top < bottom && composerRect.top > top + MIN_REGION_HEIGHT) {\n bottom = composerRect.top\n }\n return { left, top, right, bottom }\n}\n\nfunction cornerPos(corner: Corner, node: HTMLElement | null, bounds: Bounds): { x: number; y: number } {\n const rect = node?.getBoundingClientRect()\n const w = rect?.width ?? 0\n const h = rect?.height ?? 0\n const xLeft = bounds.left + MARGIN\n const yTop = bounds.top + MARGIN\n const xRight = bounds.right - w - MARGIN\n const yBottom = bounds.bottom - h - MARGIN\n if (corner === 'tl') return { x: xLeft, y: yTop }\n if (corner === 'tr') return { x: xRight, y: yTop }\n if (corner === 'bl') return { x: xLeft, y: yBottom }\n return { x: xRight, y: yBottom }\n}\n\nexport type UseSessionsHook = <T>(selector: (state: SessionListStateFace) => T) => T\n\nexport function QuotaWidget(props: {\n useSessions: UseSessionsHook\n views: ConversationViewsSource\n}): ReactElement | null {\n const { t } = useI18n()\n const sessionId = props.useSessions(currentSessionIdOf)\n const [visible, setVisible] = useState(widgetVisibleStore.get())\n useEffect(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), [])\n const [quotaViewActive, setQuotaViewActive] = useState(quotaViewActiveStore.get())\n useEffect(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), [])\n const [lowBalance, setLowBalance] = useState(lowBalanceStore.get())\n useEffect(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), [])\n const [selectedTabIds, setSelectedTabIds] = useState(widgetTabIdsStore.get())\n useEffect(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), [])\n const [viewTabs, setViewTabs] = useState<ConversationViewTab[]>(() => props.views.list())\n useEffect(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views])\n const [activeViewId, setActiveViewId] = useState<string | null>(null)\n\n const cachedBalance = getCachedBalance()\n const [data, setData] = useState<BalanceData | null>(cachedBalance?.data ?? null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(cachedBalance === null)\n const [collapsed, setCollapsed] = useState(() => loadPref<boolean>(COLLAPSED_KEY, isBoolean, false))\n const [pos, setPos] = useState<{ x: number; y: number } | null>(null)\n const [corner, setCorner] = useState<Corner>(() => loadPref<Corner>(CORNER_KEY, isCorner, 'br'))\n const [dragging, setDragging] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const dragRef = useRef<DragSession | null>(null)\n\n // Today's spend comes from the shared usage cache \u2014 never fetched on the\n // widget's own poll, since aggregating the session logs takes seconds. A\n // manual refresh (below) is the one place the widget pays for it.\n const readUsage = (): { data: UsageData | null; at: number | null } => {\n const cached = getCachedUsage()\n return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() }\n }\n const [usage, setUsage] = useState(readUsage)\n useEffect(() => subscribeUsage(() => setUsage(readUsage())), [])\n\n const readSessionUsage = (id: string | undefined): SessionUsageData | null => {\n if (id === undefined) return null\n const response = getCachedSessionUsage(id)\n return response?.ok === true ? response.data ?? null : null\n }\n const [sessionUsage, setSessionUsage] = useState<SessionUsageData | null>(() => readSessionUsage(sessionId))\n useEffect(() => {\n setSessionUsage(readSessionUsage(sessionId))\n if (sessionId === undefined) return\n const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)))\n void fetchSessionUsage(sessionId)\n return unsubscribe\n }, [sessionId])\n\n // The root-scoped overlay does not receive the session's active view store.\n // Read the host's semantic tablist instead and map its selected button by\n // index to the same live slot ledger used to build that tablist.\n useLayoutEffect(() => {\n const viewIds = viewTabs.map(tab => tab.id)\n const read = (): void => {\n const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds)\n setActiveViewId(previous => previous === next ? previous : next)\n }\n read()\n const frame = getShellFrame(rootRef.current)\n let observer: MutationObserver | null = null\n if (frame !== null && typeof MutationObserver === 'function') {\n observer = new MutationObserver(read)\n observer.observe(frame, { subtree: true, childList: true, attributes: true, attributeFilter: ['aria-selected'] })\n }\n const poll = setInterval(read, 1000)\n return () => {\n observer?.disconnect()\n clearInterval(poll)\n }\n }, [sessionId, viewTabs])\n\n const load = async (showLoading: boolean, force = false): Promise<void> => {\n if (showLoading) setLoading(true)\n try {\n const res = await fetchBalance(force)\n if (res.ok && res.data !== undefined) {\n setData(res.data)\n setError(null)\n } else {\n setData(null)\n setError(res.error ?? '')\n }\n } catch (err) {\n setData(null)\n setError((err as { message?: string } | null)?.message ?? String(err))\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n void load(true)\n const id = setInterval(() => { void load(false) }, 60_000)\n return () => clearInterval(id)\n }, [])\n\n /**\n * Keep the widget parked in its corner: on mount (restoring the persisted\n * corner before the first paint), when collapse/expand changes its size, and\n * whenever the layout moves under it \u2014 window resize, sidebar collapse or\n * the details panel opening (observed), plus a slow poll that catches the\n * one change no observer reports: the session header mounting *after* the\n * overlay, which would otherwise leave the widget parked over the tab row.\n *\n * `place` keeps the previous object when the corner has not actually moved,\n * so repeated triggers cost one rect read and no re-render.\n */\n useLayoutEffect(() => {\n if (!visible || quotaViewActive) return\n const node = rootRef.current\n if (node === null) return\n const place = (): void => {\n if (dragRef.current !== null) return\n const next = cornerPos(corner, node, getBounds(node))\n setPos(prev => (prev !== null && prev.x === next.x && prev.y === next.y ? prev : next))\n }\n place()\n const frame = getShellFrame(node)\n let observer: ResizeObserver | null = null\n if (frame !== null && typeof ResizeObserver === 'function') {\n observer = new ResizeObserver(place)\n observer.observe(frame)\n const main = getMainColumn(frame)\n if (main !== null) observer.observe(main)\n }\n window.addEventListener('resize', place)\n const poll = setInterval(place, 1000)\n return () => {\n observer?.disconnect()\n window.removeEventListener('resize', place)\n clearInterval(poll)\n }\n }, [visible, quotaViewActive, corner, collapsed])\n\n const snapToNearest = (session: DragSession): void => {\n const node = rootRef.current\n if (node === null) return\n const { width: w, height: h, bounds: b } = session\n const corners: Array<{ c: Corner; x: number; y: number }> = [\n { c: 'tl', x: b.left + MARGIN, y: b.top + MARGIN },\n { c: 'tr', x: b.right - w - MARGIN, y: b.top + MARGIN },\n { c: 'bl', x: b.left + MARGIN, y: b.bottom - h - MARGIN },\n { c: 'br', x: b.right - w - MARGIN, y: b.bottom - h - MARGIN },\n ]\n const rect = node.getBoundingClientRect()\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n let best = corners[0]\n let bestDistance = Number.POSITIVE_INFINITY\n for (const candidate of corners) {\n const ccx = candidate.x + w / 2\n const ccy = candidate.y + h / 2\n const distance = (ccx - cx) ** 2 + (ccy - cy) ** 2\n if (distance < bestDistance) {\n bestDistance = distance\n best = candidate\n }\n }\n setCorner(best.c)\n savePref(CORNER_KEY, best.c)\n setPos({ x: best.x, y: best.y })\n }\n\n /** Idempotent end-of-drag; also the missed-pointerup backstop. */\n const endDrag = (snap: boolean): void => {\n const session = dragRef.current\n if (session === null) return\n dragRef.current = null\n setDragging(false)\n const node = rootRef.current\n if (node !== null && typeof node.releasePointerCapture === 'function') {\n try {\n node.releasePointerCapture(session.pointerId)\n } catch {\n // capture already released\n }\n }\n if (snap) {\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(() => snapToNearest(session))\n } else {\n snapToNearest(session)\n }\n }\n }\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.button !== 0) return\n const target = e.target as Element | null\n if (target !== null && typeof target.closest === 'function' && target.closest('button') !== null) return\n const node = rootRef.current\n if (node === null) return\n const bounds = getBounds(node)\n const rect = node.getBoundingClientRect()\n dragRef.current = {\n pointerId: e.pointerId,\n startX: e.clientX,\n startY: e.clientY,\n originX: rect.left,\n originY: rect.top,\n width: rect.width,\n height: rect.height,\n bounds,\n }\n node.setPointerCapture(e.pointerId)\n setDragging(true)\n e.preventDefault()\n }\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const session = dragRef.current\n if (session === null || session.pointerId !== e.pointerId) return\n if ((e.buttons & 1) === 0) {\n endDrag(true)\n return\n }\n const dx = e.clientX - session.startX\n const dy = e.clientY - session.startY\n const maxX = session.bounds.right - session.width - MARGIN\n const maxY = session.bounds.bottom - session.height - MARGIN\n const minX = session.bounds.left + MARGIN\n const minY = session.bounds.top + MARGIN\n setPos({\n x: Math.max(minX, Math.min(session.originX + dx, maxX)),\n y: Math.max(minY, Math.min(session.originY + dy, maxY)),\n })\n }\n\n const onPointerUp = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const session = dragRef.current\n if (session === null || session.pointerId !== e.pointerId) return\n endDrag(true)\n }\n\n if (!visible) return null\n\n const allowedOnActiveView = activeViewId === null\n ? sessionId === undefined\n : activeViewId !== 'balance' && (selectedTabIds === null || selectedTabIds.includes(activeViewId))\n const hidden = quotaViewActive || !allowedOnActiveView\n\n const primary = data !== null && data.balances.length > 0 ? data.balances[0] : null\n const balanceValue = primary !== null ? Number(primary.total) : Number.NaN\n const low = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance\n const dotClass = primary === null\n ? 'dsh-quota-dot dsh-quota-dot--idle'\n : data?.isAvailable === false\n ? 'dsh-quota-dot dsh-quota-dot--error'\n : low ? 'dsh-quota-dot dsh-quota-dot--warn' : 'dsh-quota-dot'\n\n // The usage figure can be minutes old (it is only refreshed on demand), so\n // say how old rather than presenting it as live.\n const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 60_000)\n const usageAgeText = ageMinutes === null || ageMinutes < 1\n ? t('widget.updatedNow')\n : t('widget.stale', { minutes: ageMinutes })\n const sessionCostRow = sessionUsage === null ? null : (\n <div className=\"dsh-quota-row\" title={t('widget.sessionCostTitle')}>\n <span className=\"dsh-quota-label\">{t('widget.sessionCost')}</span>\n <span className=\"dsh-quota-value\">\u00A5 {fmtTurnCost(sessionUsage.cost)}</span>\n </div>\n )\n\n let body: ReactElement | null = null\n if (!collapsed) {\n if (loading && data === null && error === null) {\n body = <div className=\"dsh-quota-body\"><div>{t('widget.loading')}</div>{sessionCostRow}</div>\n } else if (error !== null && data === null) {\n body = (\n <div className=\"dsh-quota-body\">\n <div className=\"dsh-quota-error\">{localizeApiError(error, t, 'error.query')}</div>\n {sessionCostRow}\n </div>\n )\n } else if (primary !== null) {\n body = (\n <div className=\"dsh-quota-body\">\n <div className=\"dsh-quota-remaining-label\">{t('balance.remaining')}</div>\n <div title={low ? t('widget.lowTitle', { amount: fmt(lowBalance) }) : undefined}>\n <span className={`dsh-quota-total${low ? ' dsh-quota-total--low' : ''}`}>{fmt(primary.total)}</span>\n <span className=\"dsh-quota-currency\">{primary.currency}</span>\n </div>\n {usage.data !== null && (\n <div className=\"dsh-quota-row\" title={t('widget.todayCostTitle', { age: usageAgeText })}>\n <span className=\"dsh-quota-label\">{t('widget.todayCost')}</span>\n <span className=\"dsh-quota-value\">\u00A5 {fmt(usage.data.summary.today.cost)}</span>\n </div>\n )}\n {sessionCostRow}\n {data?.isAvailable === false && (\n <div className=\"dsh-quota-row\">\n <span className=\"dsh-quota-label\">{t('common.status')}</span>\n <span className=\"dsh-quota-value dsh-quota-error\">{t('common.unavailable')}</span>\n </div>\n )}\n </div>\n )\n } else {\n body = (\n <div className=\"dsh-quota-body\">\n <div className=\"dsh-quota-error\">{t('widget.noBalance')}</div>\n {sessionCostRow}\n </div>\n )\n }\n }\n\n const style: CSSProperties | undefined = pos !== null\n ? { left: `${pos.x}px`, top: `${pos.y}px`, right: 'auto', bottom: 'auto' }\n : undefined\n\n return (\n <div\n className={`dsh-quota-root${dragging ? ' dsh-quota-dragging' : ''}${hidden ? ' dsh-quota-root--hidden' : ''}`}\n style={style}\n aria-hidden={hidden}\n >\n <div\n ref={rootRef}\n className=\"dsh-quota-card\"\n title={t('widget.dragCard')}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={() => endDrag(false)}\n onLostPointerCapture={() => endDrag(false)}\n >\n <div className=\"dsh-quota-header\">\n <div className=\"dsh-quota-title\">\n <span className=\"dsh-quota-grip\" title={t('widget.dragGrip')}>\u2807</span>\n <span className={dotClass} />\n <span className=\"dsh-quota-name\">{t('widget.title')}</span>\n </div>\n <div className=\"dsh-quota-actions\">\n <button\n className=\"dsh-quota-btn\"\n type=\"button\"\n title={t('widget.refresh')}\n aria-label={t('widget.refresh')}\n disabled={loading}\n onClick={() => {\n void load(true, true)\n void fetchUsage(true)\n if (sessionId !== undefined) void fetchSessionUsage(sessionId, true)\n }}\n >\n \u21BB\n </button>\n <button\n className=\"dsh-quota-btn\"\n type=\"button\"\n title={collapsed ? t('widget.expand') : t('widget.collapse')}\n aria-label={collapsed ? t('widget.expandAria') : t('widget.collapseAria')}\n aria-expanded={!collapsed}\n onClick={() => {\n const next = !collapsed\n setCollapsed(next)\n savePref(COLLAPSED_KEY, next)\n }}\n >\n {collapsed ? '+' : '\u2212'}\n </button>\n </div>\n </div>\n {collapsed && primary !== null && (\n <div className=\"dsh-quota-collapsed-total-row\">\n <span className={`dsh-quota-collapsed-total${low ? ' dsh-quota-total--low' : ''}`}>{fmt(primary.total)} {primary.currency}</span>\n </div>\n )}\n {body}\n </div>\n </div>\n )\n}\n", "/**\n * Structural face of the host's session-list state, and the one selector this\n * plugin needs from it.\n *\n * The hosts this plugin runs on speak two dialects. 0.1.5-era hosts kept a\n * flat `current` field; 0.1.6+ removed it and mark the open session through\n * `byId[*].retainedBy.mainView` \u2014 the same signal the host's own document\n * title and sidebar selection read. Both fields stay optional so neither\n * generation fails, and consumers keep working across a host upgrade without\n * a plugin re-release.\n */\n\n/** One session row; only the fields the selector reads. */\nexport interface SessionSummaryFace {\n id?: string\n retainedBy?: { mainView?: number }\n}\n\nexport interface SessionListStateFace {\n byId?: Readonly<Record<string, SessionSummaryFace | undefined>>\n /** 0.1.5-era hosts: the flat current-session field. */\n current?: string\n}\n\n/** The session currently shown in the main panel, if any. */\nexport function currentSessionIdOf(state: SessionListStateFace): string | undefined {\n const retained = Object.values(state.byId ?? {})\n .find(row => (row?.retainedBy?.mainView ?? 0) > 0)\n return retained?.id ?? state.current\n}\n", "/** Live projection of the host's `conversation.view` slot ledger. */\n\nexport interface ConversationViewTab {\n id: string\n label: string\n}\n\nexport interface ConversationViewsSource {\n list(): ConversationViewTab[]\n subscribe(fn: () => void): () => void\n}\n\nexport interface ConversationViewSlotEntry {\n options: {\n id?: string\n label?: string | (() => string)\n }\n}\n\nexport interface ConversationViewSlots {\n entries(name: string): ConversationViewSlotEntry[]\n subscribe(name: string, fn: () => void): () => void\n}\n\nconst labelOf = (entry: ConversationViewSlotEntry): string => {\n const { id = '', label } = entry.options\n try {\n const resolved = typeof label === 'function' ? label() : label\n return typeof resolved === 'string' && resolved !== '' ? resolved : id\n } catch {\n return id\n }\n}\n\n/** Build a source once at plugin apply-time. The ledger owns ordering, so the\n * same index also maps to the host's rendered tab buttons without relying on\n * hashed class names or translated labels. */\nexport function conversationViewsSource(slots: ConversationViewSlots): ConversationViewsSource {\n return {\n list: () => slots.entries('conversation.view')\n .flatMap(entry => entry.options.id === undefined\n ? []\n : [{ id: entry.options.id, label: labelOf(entry) }]),\n subscribe: fn => slots.subscribe('conversation.view', fn),\n }\n}\n"],
5
+ "mappings": "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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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",
6
6
  "names": ["window", "import_react", "import_react", "import_react", "import_jsx_runtime", "name", "import_jsx_runtime", "el", "BEIJING_OFFSET_MS", "PREFIX", "import_jsx_runtime", "name", "rate", "frame", "composer", "input", "window", "import_react", "import_jsx_runtime", "turn", "import_react", "import_jsx_runtime", "quotaViewActive", "import_jsx_runtime"]
7
7
  }