dsh-provider-usage 0.3.11 → 0.3.12
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 +3 -0
- package/lib/client.js.map +1 -1
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1040,6 +1040,9 @@ window.__ModuleLoader__.load({
|
|
|
1040
1040
|
tone halo stacks on top. */
|
|
1041
1041
|
.dsh-usage-ball {
|
|
1042
1042
|
position: fixed; z-index: 999; width: 32px; height: 32px; border-radius: 50%;
|
|
1043
|
+
/* The shell's theme opts every element into squircle corners
|
|
1044
|
+
(corner-shape: superellipse) under @supports; a ball must stay round. */
|
|
1045
|
+
corner-shape: round;
|
|
1043
1046
|
cursor: pointer; touch-action: none;
|
|
1044
1047
|
display: inline-flex; align-items: center; justify-content: center;
|
|
1045
1048
|
border: 1px solid var(--dsw-alias-border-l2);
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","useMemo","useCallback","useRef","createPortal"],"sources":["../src/client/locales.ts","../src/client/Panel.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'action.label': '用量',\n 'action.aria': 'Provider 用量',\n 'panel.title': 'Provider 用量',\n 'panel.resize': '拖拽调整面板高度',\n 'panel.refresh': '立即刷新',\n 'panel.refreshing': '刷新中…',\n 'lang.switch': 'Switch to English',\n 'panel.resetPos': '悬浮球归位',\n 'panel.interval': '刷新周期',\n 'panel.thresholds': '余额阈值',\n 'panel.thresholdRed': '红',\n 'panel.thresholdYellow': '黄',\n 'panel.updated': '更新于 {time}',\n 'panel.never': '尚未获取',\n 'panel.empty': '未检测到已配置的 Provider',\n 'interval.15s': '15 秒',\n 'interval.30s': '30 秒',\n 'interval.60s': '1 分钟',\n 'interval.300s': '5 分钟',\n 'interval.900s': '15 分钟',\n 'interval.1800s': '30 分钟',\n 'balance.total': '总额',\n 'balance.granted': '赠送 {amount}',\n 'balance.toppedUp': '充值 {amount}',\n 'usage.weekly': '每周配额',\n 'usage.used': '已用 {used}',\n 'usage.remaining': '剩余 {remaining}',\n 'usage.unlimited': '不限量',\n 'usage.resetAt': '重置于 {time}',\n 'usage.resetIn': '{countdown} 后重置',\n 'status.unsupported': '该 Provider 暂不支持额度查询',\n 'status.missingCredential': '未配置密钥({ref})',\n 'status.missingAuthorization': '未完成 OAuth 授权({key})',\n 'status.error': '查询失败',\n 'status.inUse': '使用中',\n 'status.tone.ok': '当前 Provider 正常',\n 'status.tone.warn': '当前 Provider 用量接近上限或余额偏低',\n 'status.tone.danger': '当前 Provider 异常、用量将尽或余额过低',\n 'countdown.days': '{days}天{hours}小时',\n 'countdown.hours': '{hours}小时{minutes}分',\n 'countdown.minutes': '{minutes}分钟',\n}\n\n/** English dictionary, key-identical to the Chinese source of truth. */\nexport const en = {\n 'action.label': 'Usage',\n 'action.aria': 'Provider usage',\n 'panel.title': 'Provider Usage',\n 'panel.resize': 'Drag to resize panel',\n 'panel.refresh': 'Refresh now',\n 'panel.refreshing': 'Refreshing…',\n 'lang.switch': '切换到中文',\n 'panel.resetPos': 'Reset ball position',\n 'panel.interval': 'Refresh interval',\n 'panel.thresholds': 'Balance thresholds',\n 'panel.thresholdRed': 'Red',\n 'panel.thresholdYellow': 'Yellow',\n 'panel.updated': 'Updated {time}',\n 'panel.never': 'Not fetched yet',\n 'panel.empty': 'No configured provider detected',\n 'interval.15s': '15s',\n 'interval.30s': '30s',\n 'interval.60s': '1 min',\n 'interval.300s': '5 min',\n 'interval.900s': '15 min',\n 'interval.1800s': '30 min',\n 'balance.total': 'Total',\n 'balance.granted': 'granted {amount}',\n 'balance.toppedUp': 'topped up {amount}',\n 'usage.weekly': 'Weekly quota',\n 'usage.used': 'used {used}',\n 'usage.remaining': '{remaining} left',\n 'usage.unlimited': 'Unlimited',\n 'usage.resetAt': 'Resets at {time}',\n 'usage.resetIn': 'Resets in {countdown}',\n 'status.unsupported': 'Usage query is not supported for this provider',\n 'status.missingCredential': 'No API key configured ({ref})',\n 'status.missingAuthorization': 'OAuth authorization missing ({key})',\n 'status.error': 'Query failed',\n 'status.inUse': 'In use',\n 'status.tone.ok': 'Current provider healthy',\n 'status.tone.warn': 'Current provider usage nearing its limit or balance running low',\n 'status.tone.danger': 'Current provider error, usage almost exhausted, or balance too low',\n 'countdown.days': '{days}d {hours}h',\n 'countdown.hours': '{hours}h {minutes}m',\n 'countdown.minutes': '{minutes}m',\n}\n","/**\n * Floating-ball quota widget: a draggable ball portaled to document.body\n * (the sidebar slot only mounts the component; the button itself no longer\n * lives in the sidebar, avoiding slot layout contention with other plugins).\n * The popover panel lists every configured provider's live balance/quota.\n * Polling interval is user-selectable (persisted in localStorage) and falls\n * back to the deployment-suggested value from the host plugin config.\n */\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { en, zh } from './locales'\n\n/* ------------------------------------------------------------------ *\n * Wire views (mirror of the host half)\n * ------------------------------------------------------------------ */\n\ninterface BalanceRow {\n currency: string\n total: string\n granted: string\n toppedUp: string\n}\n\ninterface UsageRow {\n label: string\n used: number | null\n limit: number | null\n remaining: number | null\n percent: number | null\n resetAt: string | null\n}\n\ninterface ProviderUsageView {\n id: string\n displayName: string\n kind: 'balance' | 'usage' | null\n status: 'ok' | 'error' | 'missing-credential' | 'missing-authorization' | 'unsupported'\n message: string | null\n balances: BalanceRow[] | null\n usages: UsageRow[] | null\n /** The provider in use (absent on older hosts); it alone drives the ball tone. */\n active?: boolean\n}\n\ninterface UsageListResult {\n fetchedAt: string\n refreshSeconds: number\n /** Deployment-suggested balance thresholds (absent on older hosts);\n the panel may override them locally. */\n balanceRedThreshold?: number\n balanceYellowThreshold?: number\n /** Plugin package version (absent on older hosts). */\n version?: string\n providers: ProviderUsageView[]\n}\n\nexport interface UsageActionProps {\n /** Sidebar expansion state, owned by the sidebar footer slot. Not read\n directly — a change re-renders us, which re-anchors the default dock. */\n wide?: boolean\n /** Namespace translator injected by the locale seat. */\n t: (key: string, params?: Record<string, unknown>) => string\n /** Business face: call the host `usage/list` remote. */\n fetchUsage: () => Promise<UsageListResult>\n /** Provider the FOCUSED session's composer would use (live client state);\n undefined when unavailable — the host's own flag then stands. */\n getActiveProvider?: () => string | undefined\n /** Subscribe to focused-session / model-selection changes; the panel\n re-derives the \"in use\" flag on every change, without a poll tick. */\n subscribeActiveChange?: (onChange: () => void) => (() => void)\n}\n\n/* ------------------------------------------------------------------ *\n * Refresh interval\n * ------------------------------------------------------------------ */\n\nconst INTERVAL_OPTIONS = [15, 30, 60, 300, 900, 1800] as const\nconst STORAGE_KEY = 'dsh.provider-usage.refreshSeconds'\nconst LANG_KEY = 'dsh.provider-usage.lang'\nconst POS_KEY = 'dsh.provider-usage.floatPos'\nconst THRESHOLD_RED_KEY = 'dsh.provider-usage.balanceRedThreshold'\nconst THRESHOLD_YELLOW_KEY = 'dsh.provider-usage.balanceYellowThreshold'\nconst PANEL_HEIGHT_KEY = 'dsh.provider-usage.panelHeight'\nconst DEFAULT_INTERVAL = 60\n/** Balance thresholds fall back to these hardcoded defaults when neither the\n host config nor a stored override provides them. */\nconst DEFAULT_BALANCE_RED = 10\nconst DEFAULT_BALANCE_YELLOW = 30\n/** Smallest panel height the top-edge drag allows (px). */\nconst PANEL_MIN_H = 120\n\n/** Largest panel height the top-edge drag allows: the viewport minus a\n breathing margin, so the panel can never outgrow the screen. */\nfunction panelMaxHeight(): number {\n return Math.max(PANEL_MIN_H, window.innerHeight - 24)\n}\n\n/** Bottom padding of the open panel (px), read live so the layout math stays\n correct if the stylesheet ever changes it. */\nfunction panelPaddingBottom(panel: HTMLElement): number {\n const pad = Number.parseFloat(getComputedStyle(panel).paddingBottom)\n return Number.isFinite(pad) ? pad : 0\n}\n\n/**\n * Natural content height of the open panel — children plus padding — even\n * while the panel itself is height-constrained or scrolled (children rects\n * are compensated by scrollTop). This is the grow limit: at this height the\n * panel needs no scrolling and leaves no blank strip at the bottom.\n */\nfunction panelContentHeight(panel: HTMLElement): number {\n const panelTop = panel.getBoundingClientRect().top\n let bottom = 0\n for (const child of Array.from(panel.children)) {\n const rect = (child as HTMLElement).getBoundingClientRect()\n bottom = Math.max(bottom, rect.bottom - panelTop + panel.scrollTop)\n }\n return Math.ceil(bottom) + panelPaddingBottom(panel)\n}\n\n/**\n * Shrink limit: the height that keeps the panel head plus the FIRST provider\n * card fully visible (bottom padding included). Falls back to PANEL_MIN_H\n * when no provider card is present (e.g. empty list).\n */\nfunction firstCardMinHeight(panel: HTMLElement): number {\n const card = panel.querySelector<HTMLElement>('.dsh-usage-card')\n if (card === null) return PANEL_MIN_H\n const panelTop = panel.getBoundingClientRect().top\n const cardRect = card.getBoundingClientRect()\n return Math.ceil(cardRect.bottom - panelTop + panel.scrollTop) + panelPaddingBottom(panel)\n}\n\n/** Floating ball diameter in px; drag math derives from it. */\nconst BALL_SIZE = 32\n\ntype Lang = 'zh' | 'en'\n/** Docked ball position: the ball CENTER as fractions of the viewport, so\n both the default spot and dragged spots follow window resizes. */\ninterface FloatPos {\n fx: number\n fy: number\n}\n\n/** Panel-level override stored in localStorage; null means follow the harness language. */\nfunction readStoredLang(): Lang | null {\n try {\n const raw = localStorage.getItem(LANG_KEY)\n return raw === 'zh' || raw === 'en' ? raw : null\n } catch {\n return null\n }\n}\n\nfunction storeLang(lang: Lang): void {\n try {\n localStorage.setItem(LANG_KEY, lang)\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** Local translator over the bundled dictionaries, mirroring the shell's `{param}` interpolation. */\nfunction makeT(dict: Record<string, string>): UsageActionProps['t'] {\n return (key, params) => {\n const template = dict[key] ?? key\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (raw, name: string) => (name in params ? String(params[name]) : raw))\n }\n}\n\nfunction readStoredInterval(): number | null {\n try {\n const raw = localStorage.getItem(STORAGE_KEY)\n if (raw === null) return null\n const value = Number(raw)\n return Number.isFinite(value) && value >= 5 ? value : null\n } catch {\n return null\n }\n}\n\nfunction storeInterval(value: number): void {\n try {\n localStorage.setItem(STORAGE_KEY, String(value))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Balance thresholds (panel overrides, persisted in localStorage)\n * ------------------------------------------------------------------ */\n\n/** Parse a threshold input: a finite number ≥ 0, or null while the field is\n empty/transiently invalid (the string is kept in state so typing never\n fights the controlled input). */\nfunction parseThreshold(raw: string): number | null {\n const trimmed = raw.trim()\n if (trimmed === '') return null\n const value = Number(trimmed)\n return Number.isFinite(value) && value >= 0 ? value : null\n}\n\nfunction readStoredThreshold(key: string): string | null {\n try {\n const raw = localStorage.getItem(key)\n return raw !== null && parseThreshold(raw) !== null ? raw : null\n } catch {\n return null\n }\n}\n\nfunction storeThreshold(key: string, value: string): void {\n try {\n localStorage.setItem(key, value)\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Panel height (user-resized via the top-edge drag handle)\n * ------------------------------------------------------------------ */\n\nfunction readStoredPanelHeight(): number | null {\n try {\n const raw = localStorage.getItem(PANEL_HEIGHT_KEY)\n if (raw === null) return null\n const value = Number(raw)\n return Number.isFinite(value) && value >= PANEL_MIN_H ? value : null\n } catch {\n return null\n }\n}\n\nfunction storePanelHeight(value: number): void {\n try {\n localStorage.setItem(PANEL_HEIGHT_KEY, String(value))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Floating ball position\n * ------------------------------------------------------------------ */\n\n/** Equal margin from the chat area's left edge and the window's bottom edge. */\nconst DOCK_MARGIN = 24\n\n/**\n * Default dock: bottom-left of the main chat area with equal margins on both\n * axes. Returned as viewport fractions of the ball CENTER, like every other\n * position. The chat area's left edge is the sidebar's right edge, derived\n * from the full-height ancestor of the settings slot (stable data-slot hook;\n * class names are build-hashed), so it adapts to sidebar collapse.\n */\nfunction defaultFloatPos(): FloatPos {\n let sidebarRight = 264\n let node = document.querySelector('[data-slot=\"sidebar.settings\"]')?.parentElement ?? null\n while (node) {\n const rect = node.getBoundingClientRect()\n if (rect.x <= 1 && rect.height >= window.innerHeight * 0.9) {\n sidebarRight = rect.right\n break\n }\n node = node.parentElement\n }\n return {\n fx: (sidebarRight + DOCK_MARGIN + BALL_SIZE / 2) / window.innerWidth,\n fy: (window.innerHeight - DOCK_MARGIN - BALL_SIZE / 2) / window.innerHeight,\n }\n}\n\nfunction readStoredFloatPos(): FloatPos | null {\n try {\n const raw = localStorage.getItem(POS_KEY)\n if (raw === null) return null\n const parsed = JSON.parse(raw) as Partial<FloatPos> | null\n if (parsed === null || typeof parsed.fx !== 'number' || !Number.isFinite(parsed.fx)) return null\n if (typeof parsed.fy !== 'number' || !Number.isFinite(parsed.fy)) return null\n return { fx: Math.min(1, Math.max(0, parsed.fx)), fy: Math.min(1, Math.max(0, parsed.fy)) }\n } catch {\n return null\n }\n}\n\nfunction storeFloatPos(pos: FloatPos): void {\n try {\n localStorage.setItem(POS_KEY, JSON.stringify(pos))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** 归位: forget the pinned spot; the ball follows the default dock again. */\nfunction clearStoredFloatPos(): void {\n try {\n localStorage.removeItem(POS_KEY)\n } catch {\n /* storage unavailable */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Formatting helpers\n * ------------------------------------------------------------------ */\n\nfunction formatCountdown(resetAt: string, now: number, t: UsageActionProps['t']): string | null {\n const diff = new Date(resetAt).getTime() - now\n if (!Number.isFinite(diff) || diff <= 0) return null\n const minutes = Math.floor(diff / 60000)\n if (minutes >= 60 * 24) return t('countdown.days', { days: Math.floor(minutes / (60 * 24)), hours: Math.floor((minutes % (60 * 24)) / 60) })\n if (minutes >= 60) return t('countdown.hours', { hours: Math.floor(minutes / 60), minutes: minutes % 60 })\n return t('countdown.minutes', { minutes: Math.max(1, minutes) })\n}\n\nfunction barTone(percent: number | null): 'ok' | 'warn' | 'danger' {\n if (percent === null) return 'ok'\n if (percent >= 90) return 'danger'\n if (percent >= 70) return 'warn'\n return 'ok'\n}\n\n/** Parse a balance amount string (\"12.34\", \"0.00\") into a number. */\nfunction toAmount(value: string): number | null {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n}\n\n/**\n * Tone for a monetary balance, compared in the balance's own currency:\n * below the red threshold → danger, below the yellow threshold → warn,\n * otherwise ok. The effective red threshold never exceeds the yellow one,\n * so the zones stay ordered no matter how the panel is configured.\n */\nfunction balanceTone(value: number | null, red: number, yellow: number): 'ok' | 'warn' | 'danger' {\n if (value === null) return 'ok'\n const effRed = Math.min(red, yellow)\n if (value < effRed) return 'danger'\n if (value < yellow) return 'warn'\n return 'ok'\n}\n\ntype Tone = 'ok' | 'warn' | 'danger'\n\nconst TONE_SEVERITY: Record<Tone, number> = { ok: 0, warn: 1, danger: 2 }\n\n/** More severe of two tones; used WITHIN one category. */\nfunction worseTone(a: Tone | null, b: Tone | null): Tone | null {\n if (a === null) return b\n if (b === null) return a\n return TONE_SEVERITY[a] >= TONE_SEVERITY[b] ? a : b\n}\n\n/**\n * Aggregate tone for one provider. The subscription/usage windows (\"plan\")\n * and the prepaid balance/credits are two INDEPENDENT categories joined with\n * OR semantics: the provider stays green while either category has enough\n * left (the plan is normally consumed before credits, so the surviving\n * resource sets the tone), and when both are running low the lighter warning\n * wins. Within one category the worst row still governs — an exhausted 5h\n * window is not \"enough plan\" just because the weekly window is healthy.\n */\nfunction providerTone(provider: ProviderUsageView, red: number, yellow: number): Tone {\n let planTone: Tone | null = null\n let creditTone: Tone | null = null\n if (provider.kind === 'balance') {\n for (const row of provider.balances ?? []) {\n creditTone = worseTone(creditTone, balanceTone(toAmount(row.total), red, yellow))\n }\n }\n for (const row of provider.usages ?? []) {\n if (row.label === 'credits') {\n creditTone = worseTone(creditTone, balanceTone(row.remaining, red, yellow))\n } else {\n planTone = worseTone(planTone, barTone(row.percent))\n }\n }\n if (planTone === null) return creditTone ?? 'ok'\n if (creditTone === null) return planTone\n return TONE_SEVERITY[planTone] <= TONE_SEVERITY[creditTone] ? planTone : creditTone\n}\n\n/** Worst health across the fetch + the provider IN USE: drives the trigger's\n status dot. Only the active provider (the composer's current selection)\n colors the ball, so an idle low-quota provider does not alarm; when the\n host reports no active provider, fall back to every provider. */\nfunction healthTone(data: UsageListResult | null, error: string | null, red: number, yellow: number): 'ok' | 'warn' | 'danger' {\n if (error !== null) return 'danger'\n const providers = data?.providers ?? []\n const active = providers.filter((provider) => provider.active)\n const relevant = active.length > 0 ? active : providers\n let tone: 'ok' | 'warn' | 'danger' = 'ok'\n for (const provider of relevant) {\n if (provider.status === 'error' || provider.status === 'missing-credential' || provider.status === 'missing-authorization') return 'danger'\n const providerToneValue = providerTone(provider, red, yellow)\n if (providerToneValue === 'danger') return 'danger'\n if (providerToneValue === 'warn') tone = 'warn'\n }\n return tone\n}\n\nfunction formatAmount(value: number | null): string {\n if (value === null) return '—'\n return Number.isInteger(value) ? String(value) : value.toFixed(2)\n}\n\n/**\n * Refine the host's `active` flags with the provider of the FOCUSED session\n * (the composer's own state), so a session switch re-highlights instantly\n * even though the host only tracks the last explicit selection. Returns the\n * same object when nothing changes; `undefined` provider leaves the host's\n * flags untouched.\n */\nfunction applyActiveProvider(data: UsageListResult, provider: string | undefined): UsageListResult {\n if (provider === undefined || !Array.isArray(data.providers)) return data\n let changed = false\n const providers = data.providers.map((entry) => {\n const active = entry.id === provider\n if (active === entry.active) return entry\n changed = true\n return { ...entry, active }\n })\n return changed ? { ...data, providers } : data\n}\n\n/* ------------------------------------------------------------------ *\n * Provider card\n * ------------------------------------------------------------------ */\n\nfunction UsageRows({ provider, now, red, yellow, t }: { provider: ProviderUsageView; now: number; red: number; yellow: number; t: UsageActionProps['t'] }) {\n const rows = provider.usages ?? []\n if (rows.length === 0) return null\n return (\n <>\n {rows.map((row, index) => {\n const percent = row.percent\n const isCredits = row.label === 'credits'\n // Credits rows carry a remaining balance and are judged by the amount;\n // quota windows keep the percent-based tone.\n const tone = isCredits ? balanceTone(row.remaining, red, yellow) : barTone(row.percent)\n const reset = row.resetAt !== null ? formatCountdown(row.resetAt, now, t) : null\n return (\n <div className=\"dsh-usage-row\" key={`${row.label}-${index}`}>\n <div className=\"dsh-usage-rowHead\">\n <span className=\"dsh-usage-rowLabel\">{row.label === 'weekly' ? t('usage.weekly') : row.label}</span>\n <span className={`dsh-usage-rowValue${tone === 'ok' ? '' : ` dsh-usage-rowValue--${tone}`}`}>\n {isCredits && row.remaining !== null\n ? formatAmount(row.remaining)\n : row.limit === null\n ? row.used !== null\n ? t('usage.used', { used: formatAmount(row.used) })\n : row.remaining !== null\n ? formatAmount(row.remaining)\n : t('usage.unlimited')\n : percent !== null\n ? `${Math.round(percent)}%`\n : t('usage.used', { used: formatAmount(row.used) })}\n </span>\n </div>\n {percent !== null ? (\n <div className=\"dsh-usage-barTrack\">\n <div\n className={`dsh-usage-barFill dsh-usage-barFill--${tone}`}\n style={{ width: `${Math.min(100, Math.max(0, percent))}%` }}\n />\n </div>\n ) : null}\n {reset !== null ? <span className=\"dsh-usage-rowReset\">{t('usage.resetIn', { countdown: reset })}</span> : null}\n </div>\n )\n })}\n </>\n )\n}\n\nfunction ProviderCard({ provider, now, red, yellow, t }: { provider: ProviderUsageView; now: number; red: number; yellow: number; t: UsageActionProps['t'] }) {\n return (\n <div className={`dsh-usage-card${provider.active ? ' dsh-usage-card--active' : ''}`}>\n <div className=\"dsh-usage-cardHead\">\n <span className=\"dsh-usage-providerName\">{provider.displayName}</span>\n {provider.active ? <span className=\"dsh-usage-inUse\">{t('status.inUse')}</span> : null}\n </div>\n {provider.status === 'unsupported' ? (\n <span className=\"dsh-usage-message\">{t('status.unsupported')}</span>\n ) : provider.status === 'missing-credential' ? (\n <span className=\"dsh-usage-message\">{t('status.missingCredential', { ref: provider.message })}</span>\n ) : provider.status === 'missing-authorization' ? (\n <span className=\"dsh-usage-message\">{t('status.missingAuthorization', { key: provider.message })}</span>\n ) : provider.status === 'error' ? (\n <span className=\"dsh-usage-message\">{t('status.error')}: {provider.message}</span>\n ) : provider.kind === 'balance' ? (\n (provider.balances ?? []).map((row, index) => {\n const tone = balanceTone(toAmount(row.total), red, yellow)\n return (\n <div key={`${row.currency}-${index}`}>\n <div className=\"dsh-usage-balanceRow\">\n <span className={`dsh-usage-balanceTotal${tone === 'ok' ? '' : ` dsh-usage-balanceTotal--${tone}`}`}>{row.total}</span>\n <span className=\"dsh-usage-balanceCurrency\">{row.currency}</span>\n </div>\n <span className=\"dsh-usage-balanceParts\">\n {[row.granted !== '0' && row.granted !== '0.00' ? t('balance.granted', { amount: row.granted }) : null,\n row.toppedUp !== '0' && row.toppedUp !== '0.00' ? t('balance.toppedUp', { amount: row.toppedUp }) : null]\n .filter(Boolean)\n .join(' · ')}\n </span>\n </div>\n )\n })\n ) : (\n <UsageRows provider={provider} now={now} red={red} yellow={yellow} t={t} />\n )}\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ *\n * Trigger + popover\n * ------------------------------------------------------------------ */\n\n/** Battery level: remaining charge reads as remaining quota. */\nfunction BatteryIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <rect width=\"16\" height=\"10\" x=\"2\" y=\"7\" rx=\"2\" ry=\"2\" />\n <path d=\"M22 11v2\" />\n <path d=\"M6 11v2\" />\n <path d=\"M10 11v2\" />\n </svg>\n )\n}\n\nfunction RefreshIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 12a8 8 0 1 1-2.34-5.66\" />\n <path d=\"M20 4v4h-4\" />\n </svg>\n )\n}\n\nfunction GlobeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M3 12h18\" />\n <path d=\"M12 3c2.5 2.6 3.9 5.7 3.9 9s-1.4 6.4-3.9 9c-2.5-2.6-3.9-5.7-3.9-9S9.5 5.6 12 3z\" />\n </svg>\n )\n}\n\n/** Crosshair for the \"reset ball to its default spot\" head button. */\nfunction HomeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"7\" />\n <path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n )\n}\n\nexport default function UsageAction({ t, fetchUsage, getActiveProvider, subscribeActiveChange }: UsageActionProps) {\n const [open, setOpen] = useState(false)\n const [data, setData] = useState<UsageListResult | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [intervalSec, setIntervalSec] = useState(() => readStoredInterval() ?? DEFAULT_INTERVAL)\n const [langOverride, setLangOverride] = useState<Lang | null>(() => readStoredLang())\n /** Balance thresholds as raw input strings (kept as text so typing never\n fights the controlled input); parsed on use, overridden locally only\n after the user edits them. */\n const [redThreshold, setRedThreshold] = useState<string>(() => readStoredThreshold(THRESHOLD_RED_KEY) ?? String(DEFAULT_BALANCE_RED))\n const [yellowThreshold, setYellowThreshold] = useState<string>(() => readStoredThreshold(THRESHOLD_YELLOW_KEY) ?? String(DEFAULT_BALANCE_YELLOW))\n const [now, setNow] = useState(() => Date.now())\n /** Pinned spot (user-dragged) as viewport fractions; null = follow the default dock. */\n const [pinnedPos, setPinnedPos] = useState<FloatPos | null>(() => readStoredFloatPos())\n /** Viewport size drives the fraction → px conversion and the default dock. */\n const [viewport, setViewport] = useState({ w: window.innerWidth, h: window.innerHeight })\n /** Transient ball position while dragging (cursor-centered); null when docked. */\n const [dragPos, setDragPos] = useState<{ x: number; y: number } | null>(null)\n /** User-resized panel height in px; null = auto-size to content (max-height\n + scroll). Persisted, like every other panel preference. */\n const [panelHeight, setPanelHeight] = useState<number | null>(() => readStoredPanelHeight())\n\n // Follow the harness language by default: probe the shell-injected\n // translator with a known key, then let a panel-level override win.\n const harnessLang: Lang | null =\n t('lang.switch') === zh['lang.switch'] ? 'zh' : t('lang.switch') === en['lang.switch'] ? 'en' : null\n const lang: Lang = langOverride ?? harnessLang ?? 'en'\n const tt = useMemo(() => makeT(lang === 'zh' ? (zh as Record<string, string>) : (en as Record<string, string>)), [lang])\n const toggleLang = useCallback(() => {\n setLangOverride((current) => {\n const next: Lang = (current ?? harnessLang ?? 'en') === 'zh' ? 'en' : 'zh'\n storeLang(next)\n return next\n })\n }, [harnessLang])\n /** Panel-head home button: unpin the ball so it follows the default dock. */\n const resetFloatPos = () => {\n clearStoredFloatPos()\n setPinnedPos(null)\n }\n const [panelPos, setPanelPos] = useState<{ left: number; top?: number; bottom?: number } | null>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n const ballRef = useRef<HTMLButtonElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const inFlight = useRef(false)\n /** Active ball-drag session; cleared on pointerup/pointercancel. */\n const dragRef = useRef<{ pointerId: number; startX: number; startY: number; dragging: boolean } | null>(null)\n /** Set when a drag ends so the trailing click does not toggle the panel. */\n const suppressClickRef = useRef(false)\n /** Active panel-height drag; cleared on pointerup/pointercancel. */\n const resizeRef = useRef<{ pointerId: number; startY: number; startHeight: number; current: number } | null>(null)\n /** Resize bounds snapshotted at drag start from the live layout: min = first\n provider card fully visible, max = content height (no scroll, no blank). */\n const resizeLimitsRef = useRef<{ min: number; max: number } | null>(null)\n\n const refresh = useCallback(async () => {\n if (inFlight.current) return\n inFlight.current = true\n setLoading(true)\n try {\n const result = await fetchUsage()\n // Refine \"in use\" with the focused session's provider (the host only\n // tracks the last explicit composer selection).\n setData(applyActiveProvider(result, getActiveProvider?.()))\n setError(null)\n } catch (reason) {\n setError(reason instanceof Error ? reason.message : String(reason))\n } finally {\n inFlight.current = false\n setLoading(false)\n }\n }, [fetchUsage, getActiveProvider])\n\n // Initial fetch + fixed-interval polling; skip ticks while the tab is hidden.\n useEffect(() => {\n refresh()\n }, [refresh])\n useEffect(() => {\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') refresh()\n }, intervalSec * 1000)\n return () => clearInterval(timer)\n }, [intervalSec, refresh])\n\n // Adopt the deployment-suggested interval until the user picks one locally.\n useEffect(() => {\n if (data !== null && readStoredInterval() === null && data.refreshSeconds !== intervalSec) {\n setIntervalSec(data.refreshSeconds)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data])\n\n // Adopt the deployment-suggested balance thresholds until the user edits\n // them locally (a stored override always wins, like the refresh interval).\n useEffect(() => {\n if (data === null) return\n if (readStoredThreshold(THRESHOLD_RED_KEY) === null && typeof data.balanceRedThreshold === 'number') {\n setRedThreshold(String(data.balanceRedThreshold))\n }\n if (readStoredThreshold(THRESHOLD_YELLOW_KEY) === null && typeof data.balanceYellowThreshold === 'number') {\n setYellowThreshold(String(data.balanceYellowThreshold))\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data])\n\n // Follow the FOCUSED session: when it switches (or its model selection\n // changes), re-derive which provider is \"in use\" immediately — the host's\n // global-default flag would otherwise stay stale until the next poll. Falls\n // back to the host flag when no live client state is available.\n useEffect(() => {\n const unsubscribe = subscribeActiveChange?.(() => {\n setData((prev) => (prev === null ? prev : applyActiveProvider(prev, getActiveProvider?.())))\n })\n return unsubscribe\n // The subscription callbacks are stable closures created once by the\n // client half; re-subscribing on prop identity churn is not needed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Slow tick so reset countdowns advance while the panel is open.\n useEffect(() => {\n if (!open) return\n setNow(Date.now())\n const timer = setInterval(() => setNow(Date.now()), 30000)\n return () => clearInterval(timer)\n }, [open])\n\n // Close on outside press / Escape, mirroring the shipped popover behavior.\n // The panel and the ball are portaled to document.body, so all trees count\n // as inside.\n useEffect(() => {\n if (!open) return\n const closeOutside = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) return\n if (rootRef.current?.contains(event.target)) return\n if (ballRef.current?.contains(event.target)) return\n if (panelRef.current?.contains(event.target)) return\n setOpen(false)\n }\n document.addEventListener('pointerdown', closeOutside)\n return () => document.removeEventListener('pointerdown', closeOutside)\n }, [open])\n\n // Anchor the fixed-position panel to the ball; recompute while open so a\n // position reset or window resize keeps it attached. A ball in the lower\n // half of the viewport opens the panel above it, upper half below.\n useEffect(() => {\n if (!open) return\n const update = () => {\n const rect = ballRef.current?.getBoundingClientRect()\n if (!rect) return\n const left = Math.min(Math.max(8, rect.left), Math.max(8, window.innerWidth - 336))\n setPanelPos(\n rect.top > window.innerHeight / 2\n ? { left, bottom: window.innerHeight - rect.top + 6 }\n : { left, top: rect.bottom + 6 },\n )\n }\n update()\n window.addEventListener('resize', update)\n return () => window.removeEventListener('resize', update)\n }, [open, pinnedPos, viewport])\n\n // Keep a persisted height honest while open: never render taller than the\n // current content (no blank strip at the bottom when providers vanish) or\n // the viewport. Runs before paint, so no flash; a no-op during a drag.\n useLayoutEffect(() => {\n const panel = panelRef.current\n if (panel === null || panelHeight === null) return\n const clamped = Math.min(panelHeight, panelContentHeight(panel), panelMaxHeight())\n if (clamped !== panelHeight) setPanelHeight(clamped)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [panelHeight, data, open])\n\n // Track the viewport: fraction-based positions and the default dock follow\n // window resizes automatically.\n useEffect(() => {\n const onResize = () => setViewport({ w: window.innerWidth, h: window.innerHeight })\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n // Effective thresholds: parse the inputs, fall back to defaults, and keep\n // the red zone at or below the yellow one so both stay meaningful.\n const effYellow = parseThreshold(yellowThreshold) ?? DEFAULT_BALANCE_YELLOW\n const effRed = Math.min(parseThreshold(redThreshold) ?? DEFAULT_BALANCE_RED, effYellow)\n\n const tone = healthTone(data, error, effRed, effYellow)\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n if (event.key !== 'Escape' || !open) return\n event.preventDefault()\n setOpen(false)\n }\n\n /* Ball dragging: pointer events only (touch included via touch-action:\n none). A press becomes a drag past a 5px dead zone; while dragging the\n ball follows the cursor centered on it, and on release it stays exactly\n where dropped (no edge snapping). A plain press falls through to the\n click handler. */\n const onBallPointerDown = (event: React.PointerEvent<HTMLButtonElement>) => {\n event.currentTarget.setPointerCapture(event.pointerId)\n dragRef.current = { pointerId: event.pointerId, startX: event.clientX, startY: event.clientY, dragging: false }\n }\n\n const onBallPointerMove = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n if (drag === null || drag.pointerId !== event.pointerId) return\n if (!drag.dragging) {\n if (Math.abs(event.clientX - drag.startX) < 5 && Math.abs(event.clientY - drag.startY) < 5) return\n drag.dragging = true\n // The panel would detach from a moving anchor: close it on drag start.\n if (open) setOpen(false)\n }\n const half = BALL_SIZE / 2\n setDragPos({\n x: Math.min(Math.max(half, event.clientX), window.innerWidth - half),\n y: Math.min(Math.max(half, event.clientY), window.innerHeight - half),\n })\n }\n\n const endBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n dragRef.current = null\n if (drag === null || drag.pointerId !== event.pointerId || !drag.dragging) return\n const half = BALL_SIZE / 2\n const x = Math.min(Math.max(half, event.clientX), window.innerWidth - half)\n const y = Math.min(Math.max(half, event.clientY), window.innerHeight - half)\n // Pin the drop point as viewport fractions so it follows window resizes.\n const next: FloatPos = { fx: x / window.innerWidth, fy: y / window.innerHeight }\n setPinnedPos(next)\n storeFloatPos(next)\n setDragPos(null)\n suppressClickRef.current = true\n }\n\n const cancelBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (dragRef.current?.pointerId !== event.pointerId) return\n dragRef.current = null\n setDragPos(null)\n }\n\n /* Panel height resize: grab the top-edge handle, drag up to grow / down to\n shrink. The panel itself never moves — the anchored edge stays put, only\n the height changes. The drag is bounded at drag start: growing stops at\n the content height (no scroll, no blank strip), shrinking stops once the\n first provider card is fully visible. */\n const onResizePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n event.currentTarget.setPointerCapture(event.pointerId)\n const panel = panelRef.current\n const rect = panel?.getBoundingClientRect()\n const startHeight = panelHeight ?? Math.round(rect?.height ?? 0)\n if (panel !== null) {\n const viewportMax = panelMaxHeight()\n const max = Math.min(panelContentHeight(panel), viewportMax)\n const min = Math.min(Math.max(firstCardMinHeight(panel), PANEL_MIN_H), max)\n resizeLimitsRef.current = { min, max }\n } else {\n resizeLimitsRef.current = { min: PANEL_MIN_H, max: panelMaxHeight() }\n }\n resizeRef.current = { pointerId: event.pointerId, startY: event.clientY, startHeight, current: startHeight }\n }\n\n const onResizePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const resize = resizeRef.current\n if (resize === null || resize.pointerId !== event.pointerId) return\n const limits = resizeLimitsRef.current\n const next = limits === null\n ? resize.startHeight + (resize.startY - event.clientY)\n : Math.min(Math.max(resize.startHeight + (resize.startY - event.clientY), limits.min), limits.max)\n resize.current = next\n setPanelHeight(next)\n }\n\n const endResize = (event: React.PointerEvent<HTMLDivElement>) => {\n const resize = resizeRef.current\n if (resize === null || resize.pointerId !== event.pointerId) return\n resizeRef.current = null\n resizeLimitsRef.current = null\n storePanelHeight(resize.current)\n }\n\n const onBallClick = () => {\n if (suppressClickRef.current) {\n suppressClickRef.current = false\n return\n }\n setNow(Date.now())\n setOpen((current) => !current)\n if (!open) refresh()\n }\n\n // Ball center in px: transient drag point > pinned fractions > default dock.\n // (The default reads the sidebar edge from the DOM at render time; cheap.)\n const docked = pinnedPos ?? defaultFloatPos()\n const centerX = dragPos !== null ? dragPos.x : docked.fx * viewport.w\n const centerY = dragPos !== null ? dragPos.y : docked.fy * viewport.h\n\n return (\n <div ref={rootRef} className=\"dsh-usage-root\" onKeyDown={onKeyDown}>\n {createPortal(\n <button\n ref={ballRef}\n type=\"button\"\n className={`dsh-usage-ball dsh-usage-tone-${tone}`}\n style={{ left: centerX - BALL_SIZE / 2, top: centerY - BALL_SIZE / 2 }}\n aria-expanded={open}\n aria-label={tt('action.aria')}\n title={`${tt('action.aria')} · ${tt(`status.tone.${tone}`)}`}\n onPointerDown={onBallPointerDown}\n onPointerMove={onBallPointerMove}\n onPointerUp={endBallDrag}\n onPointerCancel={cancelBallDrag}\n onClick={onBallClick}\n onKeyDown={onKeyDown}\n >\n <BatteryIcon />\n </button>,\n document.body,\n )}\n {open && panelPos !== null\n ? createPortal(\n <div\n ref={panelRef}\n className=\"dsh-usage-panel\"\n role=\"dialog\"\n aria-label={tt('panel.title')}\n style={{\n left: panelPos.left,\n top: panelPos.top,\n bottom: panelPos.bottom,\n ...(panelHeight !== null ? { height: Math.min(panelHeight, panelMaxHeight()), maxHeight: 'none' } : {}),\n }}\n onKeyDown={onKeyDown}\n >\n <div\n className=\"dsh-usage-resize\"\n title={tt('panel.resize')}\n aria-label={tt('panel.resize')}\n onPointerDown={onResizePointerDown}\n onPointerMove={onResizePointerMove}\n onPointerUp={endResize}\n onPointerCancel={endResize}\n />\n <div className=\"dsh-usage-head\">\n <span className=\"dsh-usage-title\">{tt('panel.title')}</span>\n {data?.version ? <span className=\"dsh-usage-version\">v{data.version}</span> : null}\n <button\n type=\"button\"\n className=\"dsh-usage-home\"\n onClick={resetFloatPos}\n title={tt('panel.resetPos')}\n aria-label={tt('panel.resetPos')}\n >\n <HomeIcon />\n </button>\n <button\n type=\"button\"\n className=\"dsh-usage-lang\"\n onClick={toggleLang}\n title={tt('lang.switch')}\n aria-label={tt('lang.switch')}\n >\n <GlobeIcon />\n <span>{lang === 'zh' ? '中' : 'EN'}</span>\n </button>\n <button\n type=\"button\"\n className={`dsh-usage-refresh${loading ? ' dsh-usage-refresh--loading' : ''}`}\n disabled={loading}\n onClick={refresh}\n title={tt('panel.refresh')}\n aria-label={tt('panel.refresh')}\n >\n <RefreshIcon />\n </button>\n </div>\n {data === null && error !== null ? (\n <span className=\"dsh-usage-message\">{tt('status.error')}: {error}</span>\n ) : null}\n {data !== null && data.providers.length === 0 ? (\n <span className=\"dsh-usage-message\">{tt('panel.empty')}</span>\n ) : null}\n {(data?.providers ?? []).map((provider) => (\n <ProviderCard key={provider.id} provider={provider} now={now} red={effRed} yellow={effYellow} t={tt} />\n ))}\n <div className=\"dsh-usage-foot\">\n <span className=\"dsh-usage-footLabel\">{tt('panel.interval')}</span>\n <select\n className=\"dsh-usage-select\"\n value={intervalSec}\n onChange={(event) => {\n const value = Number(event.target.value)\n setIntervalSec(value)\n storeInterval(value)\n }}\n >\n {INTERVAL_OPTIONS.map((seconds) => (\n <option key={seconds} value={seconds}>{tt(`interval.${seconds}s`)}</option>\n ))}\n </select>\n <span className=\"dsh-usage-updated\">\n {data === null\n ? tt('panel.never')\n : tt('panel.updated', { time: new Date(data.fetchedAt).toLocaleTimeString(lang === 'zh' ? 'zh-CN' : 'en-US') })}\n </span>\n </div>\n <div className=\"dsh-usage-thresholds\">\n <span className=\"dsh-usage-footLabel\">{tt('panel.thresholds')}</span>\n <label className=\"dsh-usage-thresholdField\" title={tt('panel.thresholdRed')}>\n <span className=\"dsh-usage-thresholdLabel dsh-usage-thresholdLabel--red\">{tt('panel.thresholdRed')}</span>\n <input\n className=\"dsh-usage-thresholdInput\"\n type=\"number\"\n min={0}\n step={0.01}\n inputMode=\"decimal\"\n value={redThreshold}\n onChange={(event) => {\n setRedThreshold(event.target.value)\n storeThreshold(THRESHOLD_RED_KEY, event.target.value)\n }}\n />\n </label>\n <label className=\"dsh-usage-thresholdField\" title={tt('panel.thresholdYellow')}>\n <span className=\"dsh-usage-thresholdLabel dsh-usage-thresholdLabel--yellow\">{tt('panel.thresholdYellow')}</span>\n <input\n className=\"dsh-usage-thresholdInput\"\n type=\"number\"\n min={0}\n step={0.01}\n inputMode=\"decimal\"\n value={yellowThreshold}\n onChange={(event) => {\n setYellowThreshold(event.target.value)\n storeThreshold(THRESHOLD_YELLOW_KEY, event.target.value)\n }}\n />\n </label>\n </div>\n </div>,\n document.body,\n )\n : null}\n </div>\n )\n}\n","/**\n * Panel stylesheet, injected once per page. Follows the shipped plugins'\n * convention of a `<style data-plugin-css>` tag and design-token colors.\n */\nconst CSS = `\n.dsh-usage-root { position: relative; display: inline-flex; }\n/* Tri-tone health halo: only the shadow carries the state of the provider IN\n USE (green ok / amber remaining<30% / red error or usage>=90%) — the button\n face itself stays neutral. A 1px tone ring plus a soft halo keeps the\n state readable on both light and dark skins. */\n.dsh-usage-tone-ok { --dsh-usage-tone: #22a06b; }\n.dsh-usage-tone-warn { --dsh-usage-tone: #e2b93b; }\n.dsh-usage-tone-danger { --dsh-usage-tone: #d94f4f; }\n/* Floating ball trigger: a draggable fixed circle portaled to document.body,\n one layer below the panel (1000) so the popover always covers it. Position\n comes from inline style: docked it sits at its stored coordinates, while\n dragging it follows the cursor. Keeps the shell's lv3 lift shadow; the\n tone halo stacks on top. */\n.dsh-usage-ball {\n position: fixed; z-index: 999; width: 32px; height: 32px; border-radius: 50%;\n cursor: pointer; touch-action: none;\n display: inline-flex; align-items: center; justify-content: center;\n border: 1px solid var(--dsw-alias-border-l2);\n background-color: var(--dsw-specific-menu);\n color: var(--dsw-alias-label-tertiary);\n transition: box-shadow 0.15s;\n}\n.dsh-usage-ball:hover, .dsh-usage-ball:focus-visible { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-ball svg { width: 15px; height: 15px; }\n.dsh-usage-ball.dsh-usage-tone-ok,\n.dsh-usage-ball.dsh-usage-tone-warn,\n.dsh-usage-ball.dsh-usage-tone-danger {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 40%, transparent),\n 0 0 14px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent);\n}\n.dsh-usage-ball.dsh-usage-tone-ok:hover,\n.dsh-usage-ball.dsh-usage-tone-warn:hover,\n.dsh-usage-ball.dsh-usage-tone-danger:hover,\n.dsh-usage-ball.dsh-usage-tone-ok:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-warn:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-danger:focus-visible {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent),\n 0 0 18px color-mix(in srgb, var(--dsh-usage-tone) 70%, transparent);\n}\n.dsh-usage-panel {\n z-index: 1000; box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-menu);\n box-shadow: var(--dsw-shadow-lv3);\n border-radius: 12px; margin: 0; padding: 8px;\n width: 320px; max-width: min(360px, 100vw - 16px);\n max-height: min(460px, 100vh - 60px); overflow: auto;\n display: flex; flex-direction: column; gap: 6px;\n /* position: fixed with viewport coordinates computed inline from the\n trigger rect — absolute positioning inside the sidebar gets clipped by\n the column's overflow. */\n position: fixed;\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n}\n/* Top-edge resize handle: sticks to the panel top even while the body\n scrolls, so it is always grabbable; drag to make the panel taller/shorter\n (height persisted in localStorage). */\n.dsh-usage-resize {\n position: sticky; top: 0; z-index: 2; flex: none;\n height: 6px; margin: -4px 0 0;\n cursor: ns-resize; touch-action: none;\n border-radius: 3px;\n}\n.dsh-usage-resize::after {\n content: ''; position: absolute; left: 50%; top: 50%;\n transform: translate(-50%, -50%);\n width: 32px; height: 3px; border-radius: 2px;\n background: var(--dsw-alias-border-l2);\n}\n.dsh-usage-resize:hover, .dsh-usage-resize:active {\n background: color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 25%, transparent);\n}\n.dsh-usage-resize:hover::after, .dsh-usage-resize:active::after {\n background: var(--dsw-alias-accent, #4f8cff);\n}\n.dsh-usage-head {\n display: flex; align-items: center; gap: 8px; padding: 2px 4px 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-title { color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600; }\n.dsh-usage-version {\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n padding: 0 5px; border-radius: 4px; background: var(--dsw-alias-fill-l2);\n font-variant-numeric: tabular-nums; user-select: none; margin-right: auto;\n}\n.dsh-usage-lang {\n cursor: pointer; display: inline-flex; align-items: center; gap: 4px;\n border: 0; border-radius: 6px; background: 0; padding: 3px 5px;\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n}\n.dsh-usage-lang:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-refresh:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh:disabled { opacity: 0.5; cursor: default; }\n/* Home button in the panel head: sends the ball back to its default spot. */\n.dsh-usage-home {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-home:hover { color: var(--dsw-alias-label-secondary); }\n@keyframes dsh-usage-spin { to { transform: rotate(360deg); } }\n.dsh-usage-refresh--loading svg { animation: dsh-usage-spin 0.9s linear infinite; }\n.dsh-usage-card {\n border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px;\n background: var(--dsw-alias-fill-l2);\n}\n/* The provider in use, whose quota alone drives the ball tone. */\n.dsh-usage-card--active {\n outline: 1px solid color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 45%, transparent);\n outline-offset: -1px;\n}\n.dsh-usage-cardHead { display: flex; align-items: center; gap: 8px; }\n.dsh-usage-providerName {\n color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600;\n min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;\n}\n.dsh-usage-inUse {\n flex: none; font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 999px;\n color: var(--dsw-alias-label-secondary);\n background: color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 18%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 35%, transparent);\n}\n.dsh-usage-message { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; word-break: break-all; }\n.dsh-usage-balanceRow { display: flex; align-items: baseline; gap: 8px; }\n.dsh-usage-balanceTotal {\n color: var(--dsw-alias-label-primary); font-size: 18px; font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n/* Balance below the user-configured thresholds: red < red threshold, yellow\n below the yellow threshold; a healthy balance keeps the default label color. */\n.dsh-usage-balanceTotal--warn { color: #e2b93b; }\n.dsh-usage-balanceTotal--danger { color: #d94f4f; }\n.dsh-usage-balanceCurrency { color: var(--dsw-alias-label-secondary); font-size: 12px; }\n.dsh-usage-balanceParts { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-row { display: flex; flex-direction: column; gap: 3px; }\n.dsh-usage-rowHead { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; }\n.dsh-usage-rowLabel { color: var(--dsw-alias-label-secondary); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dsh-usage-rowValue { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; flex: none; }\n/* Credits rows show a remaining balance: tinted by the balance thresholds. */\n.dsh-usage-rowValue--warn { color: #e2b93b; }\n.dsh-usage-rowValue--danger { color: #d94f4f; }\n.dsh-usage-barTrack {\n height: 4px; border-radius: 2px; overflow: hidden;\n background: var(--dsw-alias-border-l2);\n}\n.dsh-usage-barFill { height: 100%; border-radius: 2px; transition: width 0.3s; }\n.dsh-usage-barFill--ok { background: #22a06b; }\n.dsh-usage-barFill--warn { background: #e2b93b; }\n.dsh-usage-barFill--danger { background: #d94f4f; }\n.dsh-usage-rowReset { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-foot {\n display: flex; align-items: center; gap: 8px; padding: 6px 4px 2px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-footLabel { color: var(--dsw-alias-label-tertiary); font-size: 11px; }\n.dsh-usage-select {\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: 0;\n color: var(--dsw-alias-label-secondary); font-size: 11px; padding: 2px 4px; cursor: pointer;\n}\n.dsh-usage-updated { color: var(--dsw-alias-label-tertiary); font-size: 11px; margin-left: auto; }\n/* Balance threshold row under the footer: red/yellow inputs persisted locally. */\n.dsh-usage-thresholds {\n display: flex; align-items: center; gap: 10px; padding: 2px 4px 4px;\n}\n.dsh-usage-thresholdField { display: inline-flex; align-items: center; gap: 4px; }\n.dsh-usage-thresholdLabel { font-size: 11px; font-weight: 600; }\n.dsh-usage-thresholdLabel--red { color: #d94f4f; }\n.dsh-usage-thresholdLabel--yellow { color: #e2b93b; }\n.dsh-usage-thresholdInput {\n width: 58px; box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: 0;\n color: var(--dsw-alias-label-secondary); font-size: 11px; padding: 2px 4px;\n font-variant-numeric: tabular-nums;\n}\n.dsh-usage-thresholdInput:focus {\n outline: none;\n border-color: var(--dsw-alias-accent, #4f8cff);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 40%, transparent);\n}\n`\n\nconst TAG_ID = 'dsh-provider-usage/panel.css'\n\n/** Inject the panel stylesheet once; on HMR swap, refresh the existing tag's payload in place. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector('style[data-plugin-css=' + JSON.stringify(TAG_ID) + ']')\n if (existing !== null) {\n // A hot-swapped bundle re-runs this module while the old <style> tag is\n // still in the DOM — sync its content so the new CSS actually applies.\n if (existing.textContent !== CSS) existing.textContent = CSS\n return\n }\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-provider-usage'\n tag.dataset.pluginCss = TAG_ID\n tag.textContent = CSS\n document.head.appendChild(tag)\n}\n","/**\n * provider-usage — browser half.\n *\n * Registers the locale dictionaries and contributes the usage widget into the\n * sidebar footer's additive action slot. Data comes from the host `usage`\n * Typert Remote (SRC mode — no generated descriptors) through the raw RPC\n * caller on the connection service.\n *\n * The \"provider in use\" flag is refined CLIENT-side: the host marks the route\n * from the GLOBAL default model selection, which only moves on an explicit\n * composer pick — switching between sessions with different per-session\n * selections would leave it stale. The focused session's own effective\n * selection (pending → last used → host default, mirrored by the composer's\n * model seat) is read live from the client session services and overrides the\n * host flag, so the panel follows session switches immediately.\n */\nimport UsageAction, { type UsageActionProps } from './Panel'\nimport { en, zh } from './locales'\nimport { ensureStyles } from './styles'\n\n/**\n * Required client services: slot registry, locale seats, the connection RPC\n * caller, and the live session/model-selection state used to refine the\n * \"in use\" flag (`sessions` + `modelDirectories`, both mounted by the web\n * app — the latter is the composer model seat's own service).\n */\nconst inject = ['slots', 'locale', 'connection', 'sessions', 'modelDirectories']\n\ninterface ClientContext {\n effect: (fn: () => unknown, label: string) => void\n locale: { register: (ns: string, dicts: { zh: unknown; en: unknown }) => unknown }\n slots: {\n inject: (name: string, callback: () => unknown) => void\n register: (entry: Record<string, unknown>, component: unknown) => unknown\n }\n connection: {\n rpc: {\n call: (\n channel: string,\n endpoint: string,\n payload: { args: Record<string, unknown> },\n signal?: AbortSignal,\n ) => Promise<{ ok: true; value: unknown } | { ok: false; error: { code: string; message: string } }>\n }\n }\n /** Client session list: the focused session id rides the same snapshot as\n the sidebar highlight (duck-typed SnapshotStore face). */\n sessions: {\n list: {\n getSnapshot(): { current?: string }\n subscribe(fn: () => void): () => void\n }\n }\n /** Per-session model directories (the composer seat's shared state): the\n focused session's effective selection mirrors the agent's resolution —\n pending selection → last used → deployment default. */\n modelDirectories: {\n directoryFor(sessionId: string): {\n store: {\n getSnapshot(): { current?: { provider: string; model: string } | null }\n subscribe(fn: () => void): () => void\n }\n }\n }\n}\n\nfunction apply(ctx: ClientContext) {\n ensureStyles()\n ctx.effect(() => ctx.locale.register('provider-usage', { zh, en }), 'provider-usage: dictionaries')\n\n const fetchUsage: UsageActionProps['fetchUsage'] = async () => {\n const result = await ctx.connection.rpc.call('/api', 'usage/list', { args: {} })\n if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`)\n return result.value as Awaited<ReturnType<UsageActionProps['fetchUsage']>>\n }\n\n /**\n * The provider the FOCUSED session's composer shows (and its agent would\n * use): the session's durable model selection, else the deployment default\n * — exactly what the host resolves per session. Undefined while the client\n * session state is not ready; the host's own flag then stands.\n */\n const getActiveProvider: UsageActionProps['getActiveProvider'] = () => {\n const current = ctx.sessions?.list?.getSnapshot()?.current\n if (typeof current !== 'string' || current === '') return undefined\n try {\n const directory = ctx.modelDirectories?.directoryFor(current)\n const provider = directory?.store?.getSnapshot()?.current?.provider\n return typeof provider === 'string' && provider !== '' ? provider : undefined\n } catch {\n // Session scope not resolvable yet — the next list event retries.\n return undefined\n }\n }\n\n /**\n * Re-derive the panel's \"in use\" flag the moment the focused session or its\n * model selection changes — no need to wait for the next poll tick. The\n * directory subscription follows the CURRENT focused session and is\n * re-pointed on every session switch, so a late projection load still lands.\n */\n const subscribeActiveChange: UsageActionProps['subscribeActiveChange'] = (onChange) => {\n const sessions = ctx.sessions\n if (typeof sessions?.list?.subscribe !== 'function') return () => {}\n let directoryUnsub: (() => void) | null = null\n const followDirectory = () => {\n directoryUnsub?.()\n directoryUnsub = null\n const current = sessions.list.getSnapshot()?.current\n if (typeof current === 'string' && current !== '') {\n try {\n const store = ctx.modelDirectories?.directoryFor(current)?.store\n if (typeof store?.subscribe === 'function') directoryUnsub = store.subscribe(onChange)\n } catch {\n /* directory not resolvable yet — the next session-list event retries */\n }\n }\n }\n const offList = sessions.list.subscribe(() => {\n followDirectory()\n onChange()\n })\n followDirectory()\n return () => {\n offList?.()\n directoryUnsub?.()\n }\n }\n\n ctx.slots.inject('sidebar.footer.action', () =>\n ctx.slots.register(\n {\n name: 'sidebar.footer.action',\n id: 'provider-usage',\n order: 10,\n locale: 'provider-usage',\n inject: () => ({ fetchUsage, getActiveProvider, subscribeActiveChange }),\n },\n UsageAction,\n ),\n )\n}\n\nexport { apply, inject }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","useMemo","useCallback","useRef","createPortal"],"sources":["../src/client/locales.ts","../src/client/Panel.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'action.label': '用量',\n 'action.aria': 'Provider 用量',\n 'panel.title': 'Provider 用量',\n 'panel.resize': '拖拽调整面板高度',\n 'panel.refresh': '立即刷新',\n 'panel.refreshing': '刷新中…',\n 'lang.switch': 'Switch to English',\n 'panel.resetPos': '悬浮球归位',\n 'panel.interval': '刷新周期',\n 'panel.thresholds': '余额阈值',\n 'panel.thresholdRed': '红',\n 'panel.thresholdYellow': '黄',\n 'panel.updated': '更新于 {time}',\n 'panel.never': '尚未获取',\n 'panel.empty': '未检测到已配置的 Provider',\n 'interval.15s': '15 秒',\n 'interval.30s': '30 秒',\n 'interval.60s': '1 分钟',\n 'interval.300s': '5 分钟',\n 'interval.900s': '15 分钟',\n 'interval.1800s': '30 分钟',\n 'balance.total': '总额',\n 'balance.granted': '赠送 {amount}',\n 'balance.toppedUp': '充值 {amount}',\n 'usage.weekly': '每周配额',\n 'usage.used': '已用 {used}',\n 'usage.remaining': '剩余 {remaining}',\n 'usage.unlimited': '不限量',\n 'usage.resetAt': '重置于 {time}',\n 'usage.resetIn': '{countdown} 后重置',\n 'status.unsupported': '该 Provider 暂不支持额度查询',\n 'status.missingCredential': '未配置密钥({ref})',\n 'status.missingAuthorization': '未完成 OAuth 授权({key})',\n 'status.error': '查询失败',\n 'status.inUse': '使用中',\n 'status.tone.ok': '当前 Provider 正常',\n 'status.tone.warn': '当前 Provider 用量接近上限或余额偏低',\n 'status.tone.danger': '当前 Provider 异常、用量将尽或余额过低',\n 'countdown.days': '{days}天{hours}小时',\n 'countdown.hours': '{hours}小时{minutes}分',\n 'countdown.minutes': '{minutes}分钟',\n}\n\n/** English dictionary, key-identical to the Chinese source of truth. */\nexport const en = {\n 'action.label': 'Usage',\n 'action.aria': 'Provider usage',\n 'panel.title': 'Provider Usage',\n 'panel.resize': 'Drag to resize panel',\n 'panel.refresh': 'Refresh now',\n 'panel.refreshing': 'Refreshing…',\n 'lang.switch': '切换到中文',\n 'panel.resetPos': 'Reset ball position',\n 'panel.interval': 'Refresh interval',\n 'panel.thresholds': 'Balance thresholds',\n 'panel.thresholdRed': 'Red',\n 'panel.thresholdYellow': 'Yellow',\n 'panel.updated': 'Updated {time}',\n 'panel.never': 'Not fetched yet',\n 'panel.empty': 'No configured provider detected',\n 'interval.15s': '15s',\n 'interval.30s': '30s',\n 'interval.60s': '1 min',\n 'interval.300s': '5 min',\n 'interval.900s': '15 min',\n 'interval.1800s': '30 min',\n 'balance.total': 'Total',\n 'balance.granted': 'granted {amount}',\n 'balance.toppedUp': 'topped up {amount}',\n 'usage.weekly': 'Weekly quota',\n 'usage.used': 'used {used}',\n 'usage.remaining': '{remaining} left',\n 'usage.unlimited': 'Unlimited',\n 'usage.resetAt': 'Resets at {time}',\n 'usage.resetIn': 'Resets in {countdown}',\n 'status.unsupported': 'Usage query is not supported for this provider',\n 'status.missingCredential': 'No API key configured ({ref})',\n 'status.missingAuthorization': 'OAuth authorization missing ({key})',\n 'status.error': 'Query failed',\n 'status.inUse': 'In use',\n 'status.tone.ok': 'Current provider healthy',\n 'status.tone.warn': 'Current provider usage nearing its limit or balance running low',\n 'status.tone.danger': 'Current provider error, usage almost exhausted, or balance too low',\n 'countdown.days': '{days}d {hours}h',\n 'countdown.hours': '{hours}h {minutes}m',\n 'countdown.minutes': '{minutes}m',\n}\n","/**\n * Floating-ball quota widget: a draggable ball portaled to document.body\n * (the sidebar slot only mounts the component; the button itself no longer\n * lives in the sidebar, avoiding slot layout contention with other plugins).\n * The popover panel lists every configured provider's live balance/quota.\n * Polling interval is user-selectable (persisted in localStorage) and falls\n * back to the deployment-suggested value from the host plugin config.\n */\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { en, zh } from './locales'\n\n/* ------------------------------------------------------------------ *\n * Wire views (mirror of the host half)\n * ------------------------------------------------------------------ */\n\ninterface BalanceRow {\n currency: string\n total: string\n granted: string\n toppedUp: string\n}\n\ninterface UsageRow {\n label: string\n used: number | null\n limit: number | null\n remaining: number | null\n percent: number | null\n resetAt: string | null\n}\n\ninterface ProviderUsageView {\n id: string\n displayName: string\n kind: 'balance' | 'usage' | null\n status: 'ok' | 'error' | 'missing-credential' | 'missing-authorization' | 'unsupported'\n message: string | null\n balances: BalanceRow[] | null\n usages: UsageRow[] | null\n /** The provider in use (absent on older hosts); it alone drives the ball tone. */\n active?: boolean\n}\n\ninterface UsageListResult {\n fetchedAt: string\n refreshSeconds: number\n /** Deployment-suggested balance thresholds (absent on older hosts);\n the panel may override them locally. */\n balanceRedThreshold?: number\n balanceYellowThreshold?: number\n /** Plugin package version (absent on older hosts). */\n version?: string\n providers: ProviderUsageView[]\n}\n\nexport interface UsageActionProps {\n /** Sidebar expansion state, owned by the sidebar footer slot. Not read\n directly — a change re-renders us, which re-anchors the default dock. */\n wide?: boolean\n /** Namespace translator injected by the locale seat. */\n t: (key: string, params?: Record<string, unknown>) => string\n /** Business face: call the host `usage/list` remote. */\n fetchUsage: () => Promise<UsageListResult>\n /** Provider the FOCUSED session's composer would use (live client state);\n undefined when unavailable — the host's own flag then stands. */\n getActiveProvider?: () => string | undefined\n /** Subscribe to focused-session / model-selection changes; the panel\n re-derives the \"in use\" flag on every change, without a poll tick. */\n subscribeActiveChange?: (onChange: () => void) => (() => void)\n}\n\n/* ------------------------------------------------------------------ *\n * Refresh interval\n * ------------------------------------------------------------------ */\n\nconst INTERVAL_OPTIONS = [15, 30, 60, 300, 900, 1800] as const\nconst STORAGE_KEY = 'dsh.provider-usage.refreshSeconds'\nconst LANG_KEY = 'dsh.provider-usage.lang'\nconst POS_KEY = 'dsh.provider-usage.floatPos'\nconst THRESHOLD_RED_KEY = 'dsh.provider-usage.balanceRedThreshold'\nconst THRESHOLD_YELLOW_KEY = 'dsh.provider-usage.balanceYellowThreshold'\nconst PANEL_HEIGHT_KEY = 'dsh.provider-usage.panelHeight'\nconst DEFAULT_INTERVAL = 60\n/** Balance thresholds fall back to these hardcoded defaults when neither the\n host config nor a stored override provides them. */\nconst DEFAULT_BALANCE_RED = 10\nconst DEFAULT_BALANCE_YELLOW = 30\n/** Smallest panel height the top-edge drag allows (px). */\nconst PANEL_MIN_H = 120\n\n/** Largest panel height the top-edge drag allows: the viewport minus a\n breathing margin, so the panel can never outgrow the screen. */\nfunction panelMaxHeight(): number {\n return Math.max(PANEL_MIN_H, window.innerHeight - 24)\n}\n\n/** Bottom padding of the open panel (px), read live so the layout math stays\n correct if the stylesheet ever changes it. */\nfunction panelPaddingBottom(panel: HTMLElement): number {\n const pad = Number.parseFloat(getComputedStyle(panel).paddingBottom)\n return Number.isFinite(pad) ? pad : 0\n}\n\n/**\n * Natural content height of the open panel — children plus padding — even\n * while the panel itself is height-constrained or scrolled (children rects\n * are compensated by scrollTop). This is the grow limit: at this height the\n * panel needs no scrolling and leaves no blank strip at the bottom.\n */\nfunction panelContentHeight(panel: HTMLElement): number {\n const panelTop = panel.getBoundingClientRect().top\n let bottom = 0\n for (const child of Array.from(panel.children)) {\n const rect = (child as HTMLElement).getBoundingClientRect()\n bottom = Math.max(bottom, rect.bottom - panelTop + panel.scrollTop)\n }\n return Math.ceil(bottom) + panelPaddingBottom(panel)\n}\n\n/**\n * Shrink limit: the height that keeps the panel head plus the FIRST provider\n * card fully visible (bottom padding included). Falls back to PANEL_MIN_H\n * when no provider card is present (e.g. empty list).\n */\nfunction firstCardMinHeight(panel: HTMLElement): number {\n const card = panel.querySelector<HTMLElement>('.dsh-usage-card')\n if (card === null) return PANEL_MIN_H\n const panelTop = panel.getBoundingClientRect().top\n const cardRect = card.getBoundingClientRect()\n return Math.ceil(cardRect.bottom - panelTop + panel.scrollTop) + panelPaddingBottom(panel)\n}\n\n/** Floating ball diameter in px; drag math derives from it. */\nconst BALL_SIZE = 32\n\ntype Lang = 'zh' | 'en'\n/** Docked ball position: the ball CENTER as fractions of the viewport, so\n both the default spot and dragged spots follow window resizes. */\ninterface FloatPos {\n fx: number\n fy: number\n}\n\n/** Panel-level override stored in localStorage; null means follow the harness language. */\nfunction readStoredLang(): Lang | null {\n try {\n const raw = localStorage.getItem(LANG_KEY)\n return raw === 'zh' || raw === 'en' ? raw : null\n } catch {\n return null\n }\n}\n\nfunction storeLang(lang: Lang): void {\n try {\n localStorage.setItem(LANG_KEY, lang)\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** Local translator over the bundled dictionaries, mirroring the shell's `{param}` interpolation. */\nfunction makeT(dict: Record<string, string>): UsageActionProps['t'] {\n return (key, params) => {\n const template = dict[key] ?? key\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (raw, name: string) => (name in params ? String(params[name]) : raw))\n }\n}\n\nfunction readStoredInterval(): number | null {\n try {\n const raw = localStorage.getItem(STORAGE_KEY)\n if (raw === null) return null\n const value = Number(raw)\n return Number.isFinite(value) && value >= 5 ? value : null\n } catch {\n return null\n }\n}\n\nfunction storeInterval(value: number): void {\n try {\n localStorage.setItem(STORAGE_KEY, String(value))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Balance thresholds (panel overrides, persisted in localStorage)\n * ------------------------------------------------------------------ */\n\n/** Parse a threshold input: a finite number ≥ 0, or null while the field is\n empty/transiently invalid (the string is kept in state so typing never\n fights the controlled input). */\nfunction parseThreshold(raw: string): number | null {\n const trimmed = raw.trim()\n if (trimmed === '') return null\n const value = Number(trimmed)\n return Number.isFinite(value) && value >= 0 ? value : null\n}\n\nfunction readStoredThreshold(key: string): string | null {\n try {\n const raw = localStorage.getItem(key)\n return raw !== null && parseThreshold(raw) !== null ? raw : null\n } catch {\n return null\n }\n}\n\nfunction storeThreshold(key: string, value: string): void {\n try {\n localStorage.setItem(key, value)\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Panel height (user-resized via the top-edge drag handle)\n * ------------------------------------------------------------------ */\n\nfunction readStoredPanelHeight(): number | null {\n try {\n const raw = localStorage.getItem(PANEL_HEIGHT_KEY)\n if (raw === null) return null\n const value = Number(raw)\n return Number.isFinite(value) && value >= PANEL_MIN_H ? value : null\n } catch {\n return null\n }\n}\n\nfunction storePanelHeight(value: number): void {\n try {\n localStorage.setItem(PANEL_HEIGHT_KEY, String(value))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Floating ball position\n * ------------------------------------------------------------------ */\n\n/** Equal margin from the chat area's left edge and the window's bottom edge. */\nconst DOCK_MARGIN = 24\n\n/**\n * Default dock: bottom-left of the main chat area with equal margins on both\n * axes. Returned as viewport fractions of the ball CENTER, like every other\n * position. The chat area's left edge is the sidebar's right edge, derived\n * from the full-height ancestor of the settings slot (stable data-slot hook;\n * class names are build-hashed), so it adapts to sidebar collapse.\n */\nfunction defaultFloatPos(): FloatPos {\n let sidebarRight = 264\n let node = document.querySelector('[data-slot=\"sidebar.settings\"]')?.parentElement ?? null\n while (node) {\n const rect = node.getBoundingClientRect()\n if (rect.x <= 1 && rect.height >= window.innerHeight * 0.9) {\n sidebarRight = rect.right\n break\n }\n node = node.parentElement\n }\n return {\n fx: (sidebarRight + DOCK_MARGIN + BALL_SIZE / 2) / window.innerWidth,\n fy: (window.innerHeight - DOCK_MARGIN - BALL_SIZE / 2) / window.innerHeight,\n }\n}\n\nfunction readStoredFloatPos(): FloatPos | null {\n try {\n const raw = localStorage.getItem(POS_KEY)\n if (raw === null) return null\n const parsed = JSON.parse(raw) as Partial<FloatPos> | null\n if (parsed === null || typeof parsed.fx !== 'number' || !Number.isFinite(parsed.fx)) return null\n if (typeof parsed.fy !== 'number' || !Number.isFinite(parsed.fy)) return null\n return { fx: Math.min(1, Math.max(0, parsed.fx)), fy: Math.min(1, Math.max(0, parsed.fy)) }\n } catch {\n return null\n }\n}\n\nfunction storeFloatPos(pos: FloatPos): void {\n try {\n localStorage.setItem(POS_KEY, JSON.stringify(pos))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** 归位: forget the pinned spot; the ball follows the default dock again. */\nfunction clearStoredFloatPos(): void {\n try {\n localStorage.removeItem(POS_KEY)\n } catch {\n /* storage unavailable */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Formatting helpers\n * ------------------------------------------------------------------ */\n\nfunction formatCountdown(resetAt: string, now: number, t: UsageActionProps['t']): string | null {\n const diff = new Date(resetAt).getTime() - now\n if (!Number.isFinite(diff) || diff <= 0) return null\n const minutes = Math.floor(diff / 60000)\n if (minutes >= 60 * 24) return t('countdown.days', { days: Math.floor(minutes / (60 * 24)), hours: Math.floor((minutes % (60 * 24)) / 60) })\n if (minutes >= 60) return t('countdown.hours', { hours: Math.floor(minutes / 60), minutes: minutes % 60 })\n return t('countdown.minutes', { minutes: Math.max(1, minutes) })\n}\n\nfunction barTone(percent: number | null): 'ok' | 'warn' | 'danger' {\n if (percent === null) return 'ok'\n if (percent >= 90) return 'danger'\n if (percent >= 70) return 'warn'\n return 'ok'\n}\n\n/** Parse a balance amount string (\"12.34\", \"0.00\") into a number. */\nfunction toAmount(value: string): number | null {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n}\n\n/**\n * Tone for a monetary balance, compared in the balance's own currency:\n * below the red threshold → danger, below the yellow threshold → warn,\n * otherwise ok. The effective red threshold never exceeds the yellow one,\n * so the zones stay ordered no matter how the panel is configured.\n */\nfunction balanceTone(value: number | null, red: number, yellow: number): 'ok' | 'warn' | 'danger' {\n if (value === null) return 'ok'\n const effRed = Math.min(red, yellow)\n if (value < effRed) return 'danger'\n if (value < yellow) return 'warn'\n return 'ok'\n}\n\ntype Tone = 'ok' | 'warn' | 'danger'\n\nconst TONE_SEVERITY: Record<Tone, number> = { ok: 0, warn: 1, danger: 2 }\n\n/** More severe of two tones; used WITHIN one category. */\nfunction worseTone(a: Tone | null, b: Tone | null): Tone | null {\n if (a === null) return b\n if (b === null) return a\n return TONE_SEVERITY[a] >= TONE_SEVERITY[b] ? a : b\n}\n\n/**\n * Aggregate tone for one provider. The subscription/usage windows (\"plan\")\n * and the prepaid balance/credits are two INDEPENDENT categories joined with\n * OR semantics: the provider stays green while either category has enough\n * left (the plan is normally consumed before credits, so the surviving\n * resource sets the tone), and when both are running low the lighter warning\n * wins. Within one category the worst row still governs — an exhausted 5h\n * window is not \"enough plan\" just because the weekly window is healthy.\n */\nfunction providerTone(provider: ProviderUsageView, red: number, yellow: number): Tone {\n let planTone: Tone | null = null\n let creditTone: Tone | null = null\n if (provider.kind === 'balance') {\n for (const row of provider.balances ?? []) {\n creditTone = worseTone(creditTone, balanceTone(toAmount(row.total), red, yellow))\n }\n }\n for (const row of provider.usages ?? []) {\n if (row.label === 'credits') {\n creditTone = worseTone(creditTone, balanceTone(row.remaining, red, yellow))\n } else {\n planTone = worseTone(planTone, barTone(row.percent))\n }\n }\n if (planTone === null) return creditTone ?? 'ok'\n if (creditTone === null) return planTone\n return TONE_SEVERITY[planTone] <= TONE_SEVERITY[creditTone] ? planTone : creditTone\n}\n\n/** Worst health across the fetch + the provider IN USE: drives the trigger's\n status dot. Only the active provider (the composer's current selection)\n colors the ball, so an idle low-quota provider does not alarm; when the\n host reports no active provider, fall back to every provider. */\nfunction healthTone(data: UsageListResult | null, error: string | null, red: number, yellow: number): 'ok' | 'warn' | 'danger' {\n if (error !== null) return 'danger'\n const providers = data?.providers ?? []\n const active = providers.filter((provider) => provider.active)\n const relevant = active.length > 0 ? active : providers\n let tone: 'ok' | 'warn' | 'danger' = 'ok'\n for (const provider of relevant) {\n if (provider.status === 'error' || provider.status === 'missing-credential' || provider.status === 'missing-authorization') return 'danger'\n const providerToneValue = providerTone(provider, red, yellow)\n if (providerToneValue === 'danger') return 'danger'\n if (providerToneValue === 'warn') tone = 'warn'\n }\n return tone\n}\n\nfunction formatAmount(value: number | null): string {\n if (value === null) return '—'\n return Number.isInteger(value) ? String(value) : value.toFixed(2)\n}\n\n/**\n * Refine the host's `active` flags with the provider of the FOCUSED session\n * (the composer's own state), so a session switch re-highlights instantly\n * even though the host only tracks the last explicit selection. Returns the\n * same object when nothing changes; `undefined` provider leaves the host's\n * flags untouched.\n */\nfunction applyActiveProvider(data: UsageListResult, provider: string | undefined): UsageListResult {\n if (provider === undefined || !Array.isArray(data.providers)) return data\n let changed = false\n const providers = data.providers.map((entry) => {\n const active = entry.id === provider\n if (active === entry.active) return entry\n changed = true\n return { ...entry, active }\n })\n return changed ? { ...data, providers } : data\n}\n\n/* ------------------------------------------------------------------ *\n * Provider card\n * ------------------------------------------------------------------ */\n\nfunction UsageRows({ provider, now, red, yellow, t }: { provider: ProviderUsageView; now: number; red: number; yellow: number; t: UsageActionProps['t'] }) {\n const rows = provider.usages ?? []\n if (rows.length === 0) return null\n return (\n <>\n {rows.map((row, index) => {\n const percent = row.percent\n const isCredits = row.label === 'credits'\n // Credits rows carry a remaining balance and are judged by the amount;\n // quota windows keep the percent-based tone.\n const tone = isCredits ? balanceTone(row.remaining, red, yellow) : barTone(row.percent)\n const reset = row.resetAt !== null ? formatCountdown(row.resetAt, now, t) : null\n return (\n <div className=\"dsh-usage-row\" key={`${row.label}-${index}`}>\n <div className=\"dsh-usage-rowHead\">\n <span className=\"dsh-usage-rowLabel\">{row.label === 'weekly' ? t('usage.weekly') : row.label}</span>\n <span className={`dsh-usage-rowValue${tone === 'ok' ? '' : ` dsh-usage-rowValue--${tone}`}`}>\n {isCredits && row.remaining !== null\n ? formatAmount(row.remaining)\n : row.limit === null\n ? row.used !== null\n ? t('usage.used', { used: formatAmount(row.used) })\n : row.remaining !== null\n ? formatAmount(row.remaining)\n : t('usage.unlimited')\n : percent !== null\n ? `${Math.round(percent)}%`\n : t('usage.used', { used: formatAmount(row.used) })}\n </span>\n </div>\n {percent !== null ? (\n <div className=\"dsh-usage-barTrack\">\n <div\n className={`dsh-usage-barFill dsh-usage-barFill--${tone}`}\n style={{ width: `${Math.min(100, Math.max(0, percent))}%` }}\n />\n </div>\n ) : null}\n {reset !== null ? <span className=\"dsh-usage-rowReset\">{t('usage.resetIn', { countdown: reset })}</span> : null}\n </div>\n )\n })}\n </>\n )\n}\n\nfunction ProviderCard({ provider, now, red, yellow, t }: { provider: ProviderUsageView; now: number; red: number; yellow: number; t: UsageActionProps['t'] }) {\n return (\n <div className={`dsh-usage-card${provider.active ? ' dsh-usage-card--active' : ''}`}>\n <div className=\"dsh-usage-cardHead\">\n <span className=\"dsh-usage-providerName\">{provider.displayName}</span>\n {provider.active ? <span className=\"dsh-usage-inUse\">{t('status.inUse')}</span> : null}\n </div>\n {provider.status === 'unsupported' ? (\n <span className=\"dsh-usage-message\">{t('status.unsupported')}</span>\n ) : provider.status === 'missing-credential' ? (\n <span className=\"dsh-usage-message\">{t('status.missingCredential', { ref: provider.message })}</span>\n ) : provider.status === 'missing-authorization' ? (\n <span className=\"dsh-usage-message\">{t('status.missingAuthorization', { key: provider.message })}</span>\n ) : provider.status === 'error' ? (\n <span className=\"dsh-usage-message\">{t('status.error')}: {provider.message}</span>\n ) : provider.kind === 'balance' ? (\n (provider.balances ?? []).map((row, index) => {\n const tone = balanceTone(toAmount(row.total), red, yellow)\n return (\n <div key={`${row.currency}-${index}`}>\n <div className=\"dsh-usage-balanceRow\">\n <span className={`dsh-usage-balanceTotal${tone === 'ok' ? '' : ` dsh-usage-balanceTotal--${tone}`}`}>{row.total}</span>\n <span className=\"dsh-usage-balanceCurrency\">{row.currency}</span>\n </div>\n <span className=\"dsh-usage-balanceParts\">\n {[row.granted !== '0' && row.granted !== '0.00' ? t('balance.granted', { amount: row.granted }) : null,\n row.toppedUp !== '0' && row.toppedUp !== '0.00' ? t('balance.toppedUp', { amount: row.toppedUp }) : null]\n .filter(Boolean)\n .join(' · ')}\n </span>\n </div>\n )\n })\n ) : (\n <UsageRows provider={provider} now={now} red={red} yellow={yellow} t={t} />\n )}\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ *\n * Trigger + popover\n * ------------------------------------------------------------------ */\n\n/** Battery level: remaining charge reads as remaining quota. */\nfunction BatteryIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <rect width=\"16\" height=\"10\" x=\"2\" y=\"7\" rx=\"2\" ry=\"2\" />\n <path d=\"M22 11v2\" />\n <path d=\"M6 11v2\" />\n <path d=\"M10 11v2\" />\n </svg>\n )\n}\n\nfunction RefreshIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 12a8 8 0 1 1-2.34-5.66\" />\n <path d=\"M20 4v4h-4\" />\n </svg>\n )\n}\n\nfunction GlobeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M3 12h18\" />\n <path d=\"M12 3c2.5 2.6 3.9 5.7 3.9 9s-1.4 6.4-3.9 9c-2.5-2.6-3.9-5.7-3.9-9S9.5 5.6 12 3z\" />\n </svg>\n )\n}\n\n/** Crosshair for the \"reset ball to its default spot\" head button. */\nfunction HomeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"7\" />\n <path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n )\n}\n\nexport default function UsageAction({ t, fetchUsage, getActiveProvider, subscribeActiveChange }: UsageActionProps) {\n const [open, setOpen] = useState(false)\n const [data, setData] = useState<UsageListResult | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [intervalSec, setIntervalSec] = useState(() => readStoredInterval() ?? DEFAULT_INTERVAL)\n const [langOverride, setLangOverride] = useState<Lang | null>(() => readStoredLang())\n /** Balance thresholds as raw input strings (kept as text so typing never\n fights the controlled input); parsed on use, overridden locally only\n after the user edits them. */\n const [redThreshold, setRedThreshold] = useState<string>(() => readStoredThreshold(THRESHOLD_RED_KEY) ?? String(DEFAULT_BALANCE_RED))\n const [yellowThreshold, setYellowThreshold] = useState<string>(() => readStoredThreshold(THRESHOLD_YELLOW_KEY) ?? String(DEFAULT_BALANCE_YELLOW))\n const [now, setNow] = useState(() => Date.now())\n /** Pinned spot (user-dragged) as viewport fractions; null = follow the default dock. */\n const [pinnedPos, setPinnedPos] = useState<FloatPos | null>(() => readStoredFloatPos())\n /** Viewport size drives the fraction → px conversion and the default dock. */\n const [viewport, setViewport] = useState({ w: window.innerWidth, h: window.innerHeight })\n /** Transient ball position while dragging (cursor-centered); null when docked. */\n const [dragPos, setDragPos] = useState<{ x: number; y: number } | null>(null)\n /** User-resized panel height in px; null = auto-size to content (max-height\n + scroll). Persisted, like every other panel preference. */\n const [panelHeight, setPanelHeight] = useState<number | null>(() => readStoredPanelHeight())\n\n // Follow the harness language by default: probe the shell-injected\n // translator with a known key, then let a panel-level override win.\n const harnessLang: Lang | null =\n t('lang.switch') === zh['lang.switch'] ? 'zh' : t('lang.switch') === en['lang.switch'] ? 'en' : null\n const lang: Lang = langOverride ?? harnessLang ?? 'en'\n const tt = useMemo(() => makeT(lang === 'zh' ? (zh as Record<string, string>) : (en as Record<string, string>)), [lang])\n const toggleLang = useCallback(() => {\n setLangOverride((current) => {\n const next: Lang = (current ?? harnessLang ?? 'en') === 'zh' ? 'en' : 'zh'\n storeLang(next)\n return next\n })\n }, [harnessLang])\n /** Panel-head home button: unpin the ball so it follows the default dock. */\n const resetFloatPos = () => {\n clearStoredFloatPos()\n setPinnedPos(null)\n }\n const [panelPos, setPanelPos] = useState<{ left: number; top?: number; bottom?: number } | null>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n const ballRef = useRef<HTMLButtonElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const inFlight = useRef(false)\n /** Active ball-drag session; cleared on pointerup/pointercancel. */\n const dragRef = useRef<{ pointerId: number; startX: number; startY: number; dragging: boolean } | null>(null)\n /** Set when a drag ends so the trailing click does not toggle the panel. */\n const suppressClickRef = useRef(false)\n /** Active panel-height drag; cleared on pointerup/pointercancel. */\n const resizeRef = useRef<{ pointerId: number; startY: number; startHeight: number; current: number } | null>(null)\n /** Resize bounds snapshotted at drag start from the live layout: min = first\n provider card fully visible, max = content height (no scroll, no blank). */\n const resizeLimitsRef = useRef<{ min: number; max: number } | null>(null)\n\n const refresh = useCallback(async () => {\n if (inFlight.current) return\n inFlight.current = true\n setLoading(true)\n try {\n const result = await fetchUsage()\n // Refine \"in use\" with the focused session's provider (the host only\n // tracks the last explicit composer selection).\n setData(applyActiveProvider(result, getActiveProvider?.()))\n setError(null)\n } catch (reason) {\n setError(reason instanceof Error ? reason.message : String(reason))\n } finally {\n inFlight.current = false\n setLoading(false)\n }\n }, [fetchUsage, getActiveProvider])\n\n // Initial fetch + fixed-interval polling; skip ticks while the tab is hidden.\n useEffect(() => {\n refresh()\n }, [refresh])\n useEffect(() => {\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') refresh()\n }, intervalSec * 1000)\n return () => clearInterval(timer)\n }, [intervalSec, refresh])\n\n // Adopt the deployment-suggested interval until the user picks one locally.\n useEffect(() => {\n if (data !== null && readStoredInterval() === null && data.refreshSeconds !== intervalSec) {\n setIntervalSec(data.refreshSeconds)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data])\n\n // Adopt the deployment-suggested balance thresholds until the user edits\n // them locally (a stored override always wins, like the refresh interval).\n useEffect(() => {\n if (data === null) return\n if (readStoredThreshold(THRESHOLD_RED_KEY) === null && typeof data.balanceRedThreshold === 'number') {\n setRedThreshold(String(data.balanceRedThreshold))\n }\n if (readStoredThreshold(THRESHOLD_YELLOW_KEY) === null && typeof data.balanceYellowThreshold === 'number') {\n setYellowThreshold(String(data.balanceYellowThreshold))\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data])\n\n // Follow the FOCUSED session: when it switches (or its model selection\n // changes), re-derive which provider is \"in use\" immediately — the host's\n // global-default flag would otherwise stay stale until the next poll. Falls\n // back to the host flag when no live client state is available.\n useEffect(() => {\n const unsubscribe = subscribeActiveChange?.(() => {\n setData((prev) => (prev === null ? prev : applyActiveProvider(prev, getActiveProvider?.())))\n })\n return unsubscribe\n // The subscription callbacks are stable closures created once by the\n // client half; re-subscribing on prop identity churn is not needed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Slow tick so reset countdowns advance while the panel is open.\n useEffect(() => {\n if (!open) return\n setNow(Date.now())\n const timer = setInterval(() => setNow(Date.now()), 30000)\n return () => clearInterval(timer)\n }, [open])\n\n // Close on outside press / Escape, mirroring the shipped popover behavior.\n // The panel and the ball are portaled to document.body, so all trees count\n // as inside.\n useEffect(() => {\n if (!open) return\n const closeOutside = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) return\n if (rootRef.current?.contains(event.target)) return\n if (ballRef.current?.contains(event.target)) return\n if (panelRef.current?.contains(event.target)) return\n setOpen(false)\n }\n document.addEventListener('pointerdown', closeOutside)\n return () => document.removeEventListener('pointerdown', closeOutside)\n }, [open])\n\n // Anchor the fixed-position panel to the ball; recompute while open so a\n // position reset or window resize keeps it attached. A ball in the lower\n // half of the viewport opens the panel above it, upper half below.\n useEffect(() => {\n if (!open) return\n const update = () => {\n const rect = ballRef.current?.getBoundingClientRect()\n if (!rect) return\n const left = Math.min(Math.max(8, rect.left), Math.max(8, window.innerWidth - 336))\n setPanelPos(\n rect.top > window.innerHeight / 2\n ? { left, bottom: window.innerHeight - rect.top + 6 }\n : { left, top: rect.bottom + 6 },\n )\n }\n update()\n window.addEventListener('resize', update)\n return () => window.removeEventListener('resize', update)\n }, [open, pinnedPos, viewport])\n\n // Keep a persisted height honest while open: never render taller than the\n // current content (no blank strip at the bottom when providers vanish) or\n // the viewport. Runs before paint, so no flash; a no-op during a drag.\n useLayoutEffect(() => {\n const panel = panelRef.current\n if (panel === null || panelHeight === null) return\n const clamped = Math.min(panelHeight, panelContentHeight(panel), panelMaxHeight())\n if (clamped !== panelHeight) setPanelHeight(clamped)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [panelHeight, data, open])\n\n // Track the viewport: fraction-based positions and the default dock follow\n // window resizes automatically.\n useEffect(() => {\n const onResize = () => setViewport({ w: window.innerWidth, h: window.innerHeight })\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n // Effective thresholds: parse the inputs, fall back to defaults, and keep\n // the red zone at or below the yellow one so both stay meaningful.\n const effYellow = parseThreshold(yellowThreshold) ?? DEFAULT_BALANCE_YELLOW\n const effRed = Math.min(parseThreshold(redThreshold) ?? DEFAULT_BALANCE_RED, effYellow)\n\n const tone = healthTone(data, error, effRed, effYellow)\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n if (event.key !== 'Escape' || !open) return\n event.preventDefault()\n setOpen(false)\n }\n\n /* Ball dragging: pointer events only (touch included via touch-action:\n none). A press becomes a drag past a 5px dead zone; while dragging the\n ball follows the cursor centered on it, and on release it stays exactly\n where dropped (no edge snapping). A plain press falls through to the\n click handler. */\n const onBallPointerDown = (event: React.PointerEvent<HTMLButtonElement>) => {\n event.currentTarget.setPointerCapture(event.pointerId)\n dragRef.current = { pointerId: event.pointerId, startX: event.clientX, startY: event.clientY, dragging: false }\n }\n\n const onBallPointerMove = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n if (drag === null || drag.pointerId !== event.pointerId) return\n if (!drag.dragging) {\n if (Math.abs(event.clientX - drag.startX) < 5 && Math.abs(event.clientY - drag.startY) < 5) return\n drag.dragging = true\n // The panel would detach from a moving anchor: close it on drag start.\n if (open) setOpen(false)\n }\n const half = BALL_SIZE / 2\n setDragPos({\n x: Math.min(Math.max(half, event.clientX), window.innerWidth - half),\n y: Math.min(Math.max(half, event.clientY), window.innerHeight - half),\n })\n }\n\n const endBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n dragRef.current = null\n if (drag === null || drag.pointerId !== event.pointerId || !drag.dragging) return\n const half = BALL_SIZE / 2\n const x = Math.min(Math.max(half, event.clientX), window.innerWidth - half)\n const y = Math.min(Math.max(half, event.clientY), window.innerHeight - half)\n // Pin the drop point as viewport fractions so it follows window resizes.\n const next: FloatPos = { fx: x / window.innerWidth, fy: y / window.innerHeight }\n setPinnedPos(next)\n storeFloatPos(next)\n setDragPos(null)\n suppressClickRef.current = true\n }\n\n const cancelBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (dragRef.current?.pointerId !== event.pointerId) return\n dragRef.current = null\n setDragPos(null)\n }\n\n /* Panel height resize: grab the top-edge handle, drag up to grow / down to\n shrink. The panel itself never moves — the anchored edge stays put, only\n the height changes. The drag is bounded at drag start: growing stops at\n the content height (no scroll, no blank strip), shrinking stops once the\n first provider card is fully visible. */\n const onResizePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n event.currentTarget.setPointerCapture(event.pointerId)\n const panel = panelRef.current\n const rect = panel?.getBoundingClientRect()\n const startHeight = panelHeight ?? Math.round(rect?.height ?? 0)\n if (panel !== null) {\n const viewportMax = panelMaxHeight()\n const max = Math.min(panelContentHeight(panel), viewportMax)\n const min = Math.min(Math.max(firstCardMinHeight(panel), PANEL_MIN_H), max)\n resizeLimitsRef.current = { min, max }\n } else {\n resizeLimitsRef.current = { min: PANEL_MIN_H, max: panelMaxHeight() }\n }\n resizeRef.current = { pointerId: event.pointerId, startY: event.clientY, startHeight, current: startHeight }\n }\n\n const onResizePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const resize = resizeRef.current\n if (resize === null || resize.pointerId !== event.pointerId) return\n const limits = resizeLimitsRef.current\n const next = limits === null\n ? resize.startHeight + (resize.startY - event.clientY)\n : Math.min(Math.max(resize.startHeight + (resize.startY - event.clientY), limits.min), limits.max)\n resize.current = next\n setPanelHeight(next)\n }\n\n const endResize = (event: React.PointerEvent<HTMLDivElement>) => {\n const resize = resizeRef.current\n if (resize === null || resize.pointerId !== event.pointerId) return\n resizeRef.current = null\n resizeLimitsRef.current = null\n storePanelHeight(resize.current)\n }\n\n const onBallClick = () => {\n if (suppressClickRef.current) {\n suppressClickRef.current = false\n return\n }\n setNow(Date.now())\n setOpen((current) => !current)\n if (!open) refresh()\n }\n\n // Ball center in px: transient drag point > pinned fractions > default dock.\n // (The default reads the sidebar edge from the DOM at render time; cheap.)\n const docked = pinnedPos ?? defaultFloatPos()\n const centerX = dragPos !== null ? dragPos.x : docked.fx * viewport.w\n const centerY = dragPos !== null ? dragPos.y : docked.fy * viewport.h\n\n return (\n <div ref={rootRef} className=\"dsh-usage-root\" onKeyDown={onKeyDown}>\n {createPortal(\n <button\n ref={ballRef}\n type=\"button\"\n className={`dsh-usage-ball dsh-usage-tone-${tone}`}\n style={{ left: centerX - BALL_SIZE / 2, top: centerY - BALL_SIZE / 2 }}\n aria-expanded={open}\n aria-label={tt('action.aria')}\n title={`${tt('action.aria')} · ${tt(`status.tone.${tone}`)}`}\n onPointerDown={onBallPointerDown}\n onPointerMove={onBallPointerMove}\n onPointerUp={endBallDrag}\n onPointerCancel={cancelBallDrag}\n onClick={onBallClick}\n onKeyDown={onKeyDown}\n >\n <BatteryIcon />\n </button>,\n document.body,\n )}\n {open && panelPos !== null\n ? createPortal(\n <div\n ref={panelRef}\n className=\"dsh-usage-panel\"\n role=\"dialog\"\n aria-label={tt('panel.title')}\n style={{\n left: panelPos.left,\n top: panelPos.top,\n bottom: panelPos.bottom,\n ...(panelHeight !== null ? { height: Math.min(panelHeight, panelMaxHeight()), maxHeight: 'none' } : {}),\n }}\n onKeyDown={onKeyDown}\n >\n <div\n className=\"dsh-usage-resize\"\n title={tt('panel.resize')}\n aria-label={tt('panel.resize')}\n onPointerDown={onResizePointerDown}\n onPointerMove={onResizePointerMove}\n onPointerUp={endResize}\n onPointerCancel={endResize}\n />\n <div className=\"dsh-usage-head\">\n <span className=\"dsh-usage-title\">{tt('panel.title')}</span>\n {data?.version ? <span className=\"dsh-usage-version\">v{data.version}</span> : null}\n <button\n type=\"button\"\n className=\"dsh-usage-home\"\n onClick={resetFloatPos}\n title={tt('panel.resetPos')}\n aria-label={tt('panel.resetPos')}\n >\n <HomeIcon />\n </button>\n <button\n type=\"button\"\n className=\"dsh-usage-lang\"\n onClick={toggleLang}\n title={tt('lang.switch')}\n aria-label={tt('lang.switch')}\n >\n <GlobeIcon />\n <span>{lang === 'zh' ? '中' : 'EN'}</span>\n </button>\n <button\n type=\"button\"\n className={`dsh-usage-refresh${loading ? ' dsh-usage-refresh--loading' : ''}`}\n disabled={loading}\n onClick={refresh}\n title={tt('panel.refresh')}\n aria-label={tt('panel.refresh')}\n >\n <RefreshIcon />\n </button>\n </div>\n {data === null && error !== null ? (\n <span className=\"dsh-usage-message\">{tt('status.error')}: {error}</span>\n ) : null}\n {data !== null && data.providers.length === 0 ? (\n <span className=\"dsh-usage-message\">{tt('panel.empty')}</span>\n ) : null}\n {(data?.providers ?? []).map((provider) => (\n <ProviderCard key={provider.id} provider={provider} now={now} red={effRed} yellow={effYellow} t={tt} />\n ))}\n <div className=\"dsh-usage-foot\">\n <span className=\"dsh-usage-footLabel\">{tt('panel.interval')}</span>\n <select\n className=\"dsh-usage-select\"\n value={intervalSec}\n onChange={(event) => {\n const value = Number(event.target.value)\n setIntervalSec(value)\n storeInterval(value)\n }}\n >\n {INTERVAL_OPTIONS.map((seconds) => (\n <option key={seconds} value={seconds}>{tt(`interval.${seconds}s`)}</option>\n ))}\n </select>\n <span className=\"dsh-usage-updated\">\n {data === null\n ? tt('panel.never')\n : tt('panel.updated', { time: new Date(data.fetchedAt).toLocaleTimeString(lang === 'zh' ? 'zh-CN' : 'en-US') })}\n </span>\n </div>\n <div className=\"dsh-usage-thresholds\">\n <span className=\"dsh-usage-footLabel\">{tt('panel.thresholds')}</span>\n <label className=\"dsh-usage-thresholdField\" title={tt('panel.thresholdRed')}>\n <span className=\"dsh-usage-thresholdLabel dsh-usage-thresholdLabel--red\">{tt('panel.thresholdRed')}</span>\n <input\n className=\"dsh-usage-thresholdInput\"\n type=\"number\"\n min={0}\n step={0.01}\n inputMode=\"decimal\"\n value={redThreshold}\n onChange={(event) => {\n setRedThreshold(event.target.value)\n storeThreshold(THRESHOLD_RED_KEY, event.target.value)\n }}\n />\n </label>\n <label className=\"dsh-usage-thresholdField\" title={tt('panel.thresholdYellow')}>\n <span className=\"dsh-usage-thresholdLabel dsh-usage-thresholdLabel--yellow\">{tt('panel.thresholdYellow')}</span>\n <input\n className=\"dsh-usage-thresholdInput\"\n type=\"number\"\n min={0}\n step={0.01}\n inputMode=\"decimal\"\n value={yellowThreshold}\n onChange={(event) => {\n setYellowThreshold(event.target.value)\n storeThreshold(THRESHOLD_YELLOW_KEY, event.target.value)\n }}\n />\n </label>\n </div>\n </div>,\n document.body,\n )\n : null}\n </div>\n )\n}\n","/**\n * Panel stylesheet, injected once per page. Follows the shipped plugins'\n * convention of a `<style data-plugin-css>` tag and design-token colors.\n */\nconst CSS = `\n.dsh-usage-root { position: relative; display: inline-flex; }\n/* Tri-tone health halo: only the shadow carries the state of the provider IN\n USE (green ok / amber remaining<30% / red error or usage>=90%) — the button\n face itself stays neutral. A 1px tone ring plus a soft halo keeps the\n state readable on both light and dark skins. */\n.dsh-usage-tone-ok { --dsh-usage-tone: #22a06b; }\n.dsh-usage-tone-warn { --dsh-usage-tone: #e2b93b; }\n.dsh-usage-tone-danger { --dsh-usage-tone: #d94f4f; }\n/* Floating ball trigger: a draggable fixed circle portaled to document.body,\n one layer below the panel (1000) so the popover always covers it. Position\n comes from inline style: docked it sits at its stored coordinates, while\n dragging it follows the cursor. Keeps the shell's lv3 lift shadow; the\n tone halo stacks on top. */\n.dsh-usage-ball {\n position: fixed; z-index: 999; width: 32px; height: 32px; border-radius: 50%;\n /* The shell's theme opts every element into squircle corners\n (corner-shape: superellipse) under @supports; a ball must stay round. */\n corner-shape: round;\n cursor: pointer; touch-action: none;\n display: inline-flex; align-items: center; justify-content: center;\n border: 1px solid var(--dsw-alias-border-l2);\n background-color: var(--dsw-specific-menu);\n color: var(--dsw-alias-label-tertiary);\n transition: box-shadow 0.15s;\n}\n.dsh-usage-ball:hover, .dsh-usage-ball:focus-visible { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-ball svg { width: 15px; height: 15px; }\n.dsh-usage-ball.dsh-usage-tone-ok,\n.dsh-usage-ball.dsh-usage-tone-warn,\n.dsh-usage-ball.dsh-usage-tone-danger {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 40%, transparent),\n 0 0 14px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent);\n}\n.dsh-usage-ball.dsh-usage-tone-ok:hover,\n.dsh-usage-ball.dsh-usage-tone-warn:hover,\n.dsh-usage-ball.dsh-usage-tone-danger:hover,\n.dsh-usage-ball.dsh-usage-tone-ok:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-warn:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-danger:focus-visible {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent),\n 0 0 18px color-mix(in srgb, var(--dsh-usage-tone) 70%, transparent);\n}\n.dsh-usage-panel {\n z-index: 1000; box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-menu);\n box-shadow: var(--dsw-shadow-lv3);\n border-radius: 12px; margin: 0; padding: 8px;\n width: 320px; max-width: min(360px, 100vw - 16px);\n max-height: min(460px, 100vh - 60px); overflow: auto;\n display: flex; flex-direction: column; gap: 6px;\n /* position: fixed with viewport coordinates computed inline from the\n trigger rect — absolute positioning inside the sidebar gets clipped by\n the column's overflow. */\n position: fixed;\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n}\n/* Top-edge resize handle: sticks to the panel top even while the body\n scrolls, so it is always grabbable; drag to make the panel taller/shorter\n (height persisted in localStorage). */\n.dsh-usage-resize {\n position: sticky; top: 0; z-index: 2; flex: none;\n height: 6px; margin: -4px 0 0;\n cursor: ns-resize; touch-action: none;\n border-radius: 3px;\n}\n.dsh-usage-resize::after {\n content: ''; position: absolute; left: 50%; top: 50%;\n transform: translate(-50%, -50%);\n width: 32px; height: 3px; border-radius: 2px;\n background: var(--dsw-alias-border-l2);\n}\n.dsh-usage-resize:hover, .dsh-usage-resize:active {\n background: color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 25%, transparent);\n}\n.dsh-usage-resize:hover::after, .dsh-usage-resize:active::after {\n background: var(--dsw-alias-accent, #4f8cff);\n}\n.dsh-usage-head {\n display: flex; align-items: center; gap: 8px; padding: 2px 4px 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-title { color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600; }\n.dsh-usage-version {\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n padding: 0 5px; border-radius: 4px; background: var(--dsw-alias-fill-l2);\n font-variant-numeric: tabular-nums; user-select: none; margin-right: auto;\n}\n.dsh-usage-lang {\n cursor: pointer; display: inline-flex; align-items: center; gap: 4px;\n border: 0; border-radius: 6px; background: 0; padding: 3px 5px;\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n}\n.dsh-usage-lang:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-refresh:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh:disabled { opacity: 0.5; cursor: default; }\n/* Home button in the panel head: sends the ball back to its default spot. */\n.dsh-usage-home {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-home:hover { color: var(--dsw-alias-label-secondary); }\n@keyframes dsh-usage-spin { to { transform: rotate(360deg); } }\n.dsh-usage-refresh--loading svg { animation: dsh-usage-spin 0.9s linear infinite; }\n.dsh-usage-card {\n border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px;\n background: var(--dsw-alias-fill-l2);\n}\n/* The provider in use, whose quota alone drives the ball tone. */\n.dsh-usage-card--active {\n outline: 1px solid color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 45%, transparent);\n outline-offset: -1px;\n}\n.dsh-usage-cardHead { display: flex; align-items: center; gap: 8px; }\n.dsh-usage-providerName {\n color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600;\n min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;\n}\n.dsh-usage-inUse {\n flex: none; font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 999px;\n color: var(--dsw-alias-label-secondary);\n background: color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 18%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 35%, transparent);\n}\n.dsh-usage-message { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; word-break: break-all; }\n.dsh-usage-balanceRow { display: flex; align-items: baseline; gap: 8px; }\n.dsh-usage-balanceTotal {\n color: var(--dsw-alias-label-primary); font-size: 18px; font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n/* Balance below the user-configured thresholds: red < red threshold, yellow\n below the yellow threshold; a healthy balance keeps the default label color. */\n.dsh-usage-balanceTotal--warn { color: #e2b93b; }\n.dsh-usage-balanceTotal--danger { color: #d94f4f; }\n.dsh-usage-balanceCurrency { color: var(--dsw-alias-label-secondary); font-size: 12px; }\n.dsh-usage-balanceParts { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-row { display: flex; flex-direction: column; gap: 3px; }\n.dsh-usage-rowHead { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; }\n.dsh-usage-rowLabel { color: var(--dsw-alias-label-secondary); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dsh-usage-rowValue { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; flex: none; }\n/* Credits rows show a remaining balance: tinted by the balance thresholds. */\n.dsh-usage-rowValue--warn { color: #e2b93b; }\n.dsh-usage-rowValue--danger { color: #d94f4f; }\n.dsh-usage-barTrack {\n height: 4px; border-radius: 2px; overflow: hidden;\n background: var(--dsw-alias-border-l2);\n}\n.dsh-usage-barFill { height: 100%; border-radius: 2px; transition: width 0.3s; }\n.dsh-usage-barFill--ok { background: #22a06b; }\n.dsh-usage-barFill--warn { background: #e2b93b; }\n.dsh-usage-barFill--danger { background: #d94f4f; }\n.dsh-usage-rowReset { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-foot {\n display: flex; align-items: center; gap: 8px; padding: 6px 4px 2px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-footLabel { color: var(--dsw-alias-label-tertiary); font-size: 11px; }\n.dsh-usage-select {\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: 0;\n color: var(--dsw-alias-label-secondary); font-size: 11px; padding: 2px 4px; cursor: pointer;\n}\n.dsh-usage-updated { color: var(--dsw-alias-label-tertiary); font-size: 11px; margin-left: auto; }\n/* Balance threshold row under the footer: red/yellow inputs persisted locally. */\n.dsh-usage-thresholds {\n display: flex; align-items: center; gap: 10px; padding: 2px 4px 4px;\n}\n.dsh-usage-thresholdField { display: inline-flex; align-items: center; gap: 4px; }\n.dsh-usage-thresholdLabel { font-size: 11px; font-weight: 600; }\n.dsh-usage-thresholdLabel--red { color: #d94f4f; }\n.dsh-usage-thresholdLabel--yellow { color: #e2b93b; }\n.dsh-usage-thresholdInput {\n width: 58px; box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: 0;\n color: var(--dsw-alias-label-secondary); font-size: 11px; padding: 2px 4px;\n font-variant-numeric: tabular-nums;\n}\n.dsh-usage-thresholdInput:focus {\n outline: none;\n border-color: var(--dsw-alias-accent, #4f8cff);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--dsw-alias-accent, #4f8cff) 40%, transparent);\n}\n`\n\nconst TAG_ID = 'dsh-provider-usage/panel.css'\n\n/** Inject the panel stylesheet once; on HMR swap, refresh the existing tag's payload in place. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector('style[data-plugin-css=' + JSON.stringify(TAG_ID) + ']')\n if (existing !== null) {\n // A hot-swapped bundle re-runs this module while the old <style> tag is\n // still in the DOM — sync its content so the new CSS actually applies.\n if (existing.textContent !== CSS) existing.textContent = CSS\n return\n }\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-provider-usage'\n tag.dataset.pluginCss = TAG_ID\n tag.textContent = CSS\n document.head.appendChild(tag)\n}\n","/**\n * provider-usage — browser half.\n *\n * Registers the locale dictionaries and contributes the usage widget into the\n * sidebar footer's additive action slot. Data comes from the host `usage`\n * Typert Remote (SRC mode — no generated descriptors) through the raw RPC\n * caller on the connection service.\n *\n * The \"provider in use\" flag is refined CLIENT-side: the host marks the route\n * from the GLOBAL default model selection, which only moves on an explicit\n * composer pick — switching between sessions with different per-session\n * selections would leave it stale. The focused session's own effective\n * selection (pending → last used → host default, mirrored by the composer's\n * model seat) is read live from the client session services and overrides the\n * host flag, so the panel follows session switches immediately.\n */\nimport UsageAction, { type UsageActionProps } from './Panel'\nimport { en, zh } from './locales'\nimport { ensureStyles } from './styles'\n\n/**\n * Required client services: slot registry, locale seats, the connection RPC\n * caller, and the live session/model-selection state used to refine the\n * \"in use\" flag (`sessions` + `modelDirectories`, both mounted by the web\n * app — the latter is the composer model seat's own service).\n */\nconst inject = ['slots', 'locale', 'connection', 'sessions', 'modelDirectories']\n\ninterface ClientContext {\n effect: (fn: () => unknown, label: string) => void\n locale: { register: (ns: string, dicts: { zh: unknown; en: unknown }) => unknown }\n slots: {\n inject: (name: string, callback: () => unknown) => void\n register: (entry: Record<string, unknown>, component: unknown) => unknown\n }\n connection: {\n rpc: {\n call: (\n channel: string,\n endpoint: string,\n payload: { args: Record<string, unknown> },\n signal?: AbortSignal,\n ) => Promise<{ ok: true; value: unknown } | { ok: false; error: { code: string; message: string } }>\n }\n }\n /** Client session list: the focused session id rides the same snapshot as\n the sidebar highlight (duck-typed SnapshotStore face). */\n sessions: {\n list: {\n getSnapshot(): { current?: string }\n subscribe(fn: () => void): () => void\n }\n }\n /** Per-session model directories (the composer seat's shared state): the\n focused session's effective selection mirrors the agent's resolution —\n pending selection → last used → deployment default. */\n modelDirectories: {\n directoryFor(sessionId: string): {\n store: {\n getSnapshot(): { current?: { provider: string; model: string } | null }\n subscribe(fn: () => void): () => void\n }\n }\n }\n}\n\nfunction apply(ctx: ClientContext) {\n ensureStyles()\n ctx.effect(() => ctx.locale.register('provider-usage', { zh, en }), 'provider-usage: dictionaries')\n\n const fetchUsage: UsageActionProps['fetchUsage'] = async () => {\n const result = await ctx.connection.rpc.call('/api', 'usage/list', { args: {} })\n if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`)\n return result.value as Awaited<ReturnType<UsageActionProps['fetchUsage']>>\n }\n\n /**\n * The provider the FOCUSED session's composer shows (and its agent would\n * use): the session's durable model selection, else the deployment default\n * — exactly what the host resolves per session. Undefined while the client\n * session state is not ready; the host's own flag then stands.\n */\n const getActiveProvider: UsageActionProps['getActiveProvider'] = () => {\n const current = ctx.sessions?.list?.getSnapshot()?.current\n if (typeof current !== 'string' || current === '') return undefined\n try {\n const directory = ctx.modelDirectories?.directoryFor(current)\n const provider = directory?.store?.getSnapshot()?.current?.provider\n return typeof provider === 'string' && provider !== '' ? provider : undefined\n } catch {\n // Session scope not resolvable yet — the next list event retries.\n return undefined\n }\n }\n\n /**\n * Re-derive the panel's \"in use\" flag the moment the focused session or its\n * model selection changes — no need to wait for the next poll tick. The\n * directory subscription follows the CURRENT focused session and is\n * re-pointed on every session switch, so a late projection load still lands.\n */\n const subscribeActiveChange: UsageActionProps['subscribeActiveChange'] = (onChange) => {\n const sessions = ctx.sessions\n if (typeof sessions?.list?.subscribe !== 'function') return () => {}\n let directoryUnsub: (() => void) | null = null\n const followDirectory = () => {\n directoryUnsub?.()\n directoryUnsub = null\n const current = sessions.list.getSnapshot()?.current\n if (typeof current === 'string' && current !== '') {\n try {\n const store = ctx.modelDirectories?.directoryFor(current)?.store\n if (typeof store?.subscribe === 'function') directoryUnsub = store.subscribe(onChange)\n } catch {\n /* directory not resolvable yet — the next session-list event retries */\n }\n }\n }\n const offList = sessions.list.subscribe(() => {\n followDirectory()\n onChange()\n })\n followDirectory()\n return () => {\n offList?.()\n directoryUnsub?.()\n }\n }\n\n ctx.slots.inject('sidebar.footer.action', () =>\n ctx.slots.register(\n {\n name: 'sidebar.footer.action',\n id: 'provider-usage',\n order: 10,\n locale: 'provider-usage',\n inject: () => ({ fetchUsage, getActiveProvider, subscribeActiveChange }),\n },\n UsageAction,\n ),\n )\n}\n\nexport { apply, inject }\n"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -93,7 +93,7 @@ async function refreshOpenAiCodexGrant(refreshToken, signal) {
|
|
|
93
93
|
const name = "provider-usage";
|
|
94
94
|
/** Own package version, baked in by tsdown `define` at build time (the
|
|
95
95
|
fallback only fires if the sources are compiled some other way). */
|
|
96
|
-
const version = "0.3.
|
|
96
|
+
const version = "0.3.12";
|
|
97
97
|
/** Settings namespace this plugin owns (registered through `ctx.settings`). */
|
|
98
98
|
const NS = "provider-usage";
|
|
99
99
|
const PROVIDER_KINDS = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-provider-usage",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "DeepSeek Harness plugin: live account balance/usage panel for every configured LLM provider (draggable floating-ball widget, configurable refresh interval and balance thresholds)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|