dsh-plugin-subscription-oauth 0.12.2 → 0.12.3

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.
@@ -436,9 +436,7 @@ export function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBala
436
436
  // The document listener would otherwise close what this click just opened.
437
437
  event.stopPropagation();
438
438
  setOpen((was) => !was);
439
- }, children: [_jsx("span", { className: "dsh-subscription-usage-chip-label", children: short }), merged.map((window) => (_jsxs("span", { className: "dsh-subscription-usage-chip-cell", children: [_jsx("span", { className: "dsh-subscription-usage-chip-window", children: shortWindow(window.label) }), _jsx("span", { className: "dsh-subscription-usage-chip-value", children: window.used !== undefined && window.limit !== undefined
440
- ? `${String(window.used)}/${String(window.limit)}`
441
- : `${String(Math.round(window.usedPercent ?? 0))}%` })] }, window.label)))] }), open && (_jsxs("span", { className: "dsh-subscription-usage-chip-panel", role: "status", children: [_jsx("span", { className: "dsh-subscription-usage-chip-full", children: t('usageChipFull', { provider: selected ?? '' }) }), accountReadings.map((account) => (_jsxs("span", { className: "dsh-subscription-usage-chip-account", children: [_jsx("span", { className: "dsh-subscription-usage-chip-meterHead", children: _jsx("span", { children: account.plan ?? account.account }) }), account.windows.map((window) => {
439
+ }, children: [_jsx("span", { className: "dsh-subscription-usage-chip-label", children: short }), merged.map((window) => (_jsxs("span", { className: "dsh-subscription-usage-chip-cell", children: [_jsx("span", { className: "dsh-subscription-usage-chip-window", children: shortWindow(window.label) }), _jsx("span", { className: "dsh-subscription-usage-chip-value", children: `${String(Math.round(window.usedPercent ?? 0))}%` })] }, window.label)))] }), open && (_jsxs("span", { className: "dsh-subscription-usage-chip-panel", role: "status", children: [_jsx("span", { className: "dsh-subscription-usage-chip-full", children: t('usageChipFull', { provider: selected ?? '' }) }), accountReadings.map((account) => (_jsxs("span", { className: "dsh-subscription-usage-chip-account", children: [_jsx("span", { className: "dsh-subscription-usage-chip-meterHead", children: _jsx("span", { children: account.plan ?? account.account }) }), account.windows.map((window) => {
442
440
  const windowReset = formatReset(window.resetsAt, now);
443
441
  // A credit balance reads as money (`used`/`limit`), a quota as a percent.
444
442
  const amount = window.usedPercent === undefined && window.used !== undefined && window.limit !== undefined
package/lib/client.js CHANGED
@@ -2865,7 +2865,7 @@ function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, us
2865
2865
  children: shortWindow(window$1.label)
2866
2866
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2867
2867
  className: "dsh-subscription-usage-chip-value",
2868
- children: window$1.used !== void 0 && window$1.limit !== void 0 ? `${String(window$1.used)}/${String(window$1.limit)}` : `${String(Math.round(window$1.usedPercent ?? 0))}%`
2868
+ children: `${String(Math.round(window$1.usedPercent ?? 0))}%`
2869
2869
  })]
2870
2870
  }, window$1.label))]
2871
2871
  }),
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["code: string","messageOf","result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>","error: unknown","template: string","formatReset","styles: Record<string, CSSProperties>","window","accounts: readonly AccountStatus[]","caught: unknown","SHORT_LABELS: Record<string, string>","SHORT_WINDOWS: Record<string, string>","wallets: ChipBalance[]","out: AccountReading[]","window","merged: UsageWindow[]","short"],"sources":["../src/client/locales.ts","../src/client/styles.ts","../src/protocol.ts","../src/client/subscriptions-rpc.ts","../src/client/SubscriptionsSection.tsx","../src/client/UsageChip.tsx","../src/client/FastModeButton.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Copy dictionaries for the Subscriptions settings page.\n *\n * English is the key-set source of truth; the Chinese dictionary is typed\n * against it, so a key added to one and forgotten in the other is a compile\n * error rather than a silent fallback.\n */\n\n/** English strings. */\nexport const en = {\n nav: 'Subscriptions',\n intro: 'Sign in with the AI subscriptions you already pay for — no API key. Pick a provider below, connect once, and its models join the model picker with live plan usage. Tokens stay on this machine.',\n loading: 'Checking subscriptions…',\n unavailable: 'The subscriptions page needs the web connection service, which this composition does not provide.',\n statusConnected: 'Connected · {count} account(s)',\n statusSignedOut: 'Not signed in',\n statusBusy: 'Waiting for browser approval',\n statusSignIn: 'Sign in to add this provider\\'s models to the model picker.',\n statusAuto: 'Follows {count} enabled model(s) from every provider',\n connect: 'Connect',\n edit: 'Edit',\n collapse: 'Collapse',\n addAccount: 'Add account',\n manage: 'Manage',\n cancel: 'Cancel',\n close: 'Close',\n logout: 'Sign out',\n makeDefault: 'Use by default',\n defaultBadge: 'Default',\n badgeLive: 'Live',\n accountNeedsLogin: 'Sign in again',\n accountNeedsLoginHint: 'The provider refused this account\\'s grant, so its token cannot be renewed',\n accountExpired: 'token expired',\n accountExpires: 'expires {time}',\n deviceTitle: 'Approve this device',\n devicePrompt: 'Open the verification page and confirm the code below. This page updates by itself once the provider approves.',\n deviceOpen: 'Open verification page',\n deviceCopy: 'Copy code',\n deviceCopied: 'Copied',\n deviceExpires: 'The code expires at {time}.',\n openLogin: 'Open sign-in page',\n openLoginHint: 'Finish signing in in the browser tab that opened. This page updates by itself once the provider redirects back.',\n modelsTitle: 'Models',\n modelsCount: '{count} available',\n modelsLive: 'live',\n modelsBuiltIn: 'built-in',\n modelsShow: 'Show in picker',\n modelsHiddenCount: '· {count} hidden',\n modelsHidden: 'Hidden from the model picker',\n modelsEmpty: 'No models reported yet.',\n modelsAdd: 'Add model',\n modelsAddHint: 'Publish a model id this build does not know yet',\n modelsAddPlaceholder: 'model id, e.g. k3-next',\n modelsAddConfirm: 'Add',\n modelsRemove: 'Remove this hand-added model',\n modelsContext: '{tokens} context',\n modelsAuto: 'Auto',\n modelsAutoHint: 'Pinned first: moves to the next model when one is unavailable',\n modelsAutoOff: 'Off — the picker offers the models directly instead',\n modelsAutoChain: 'Auto tries these, in this order — drag to re-order',\n modelsDrag: 'Drag to re-order (or press ↑ / ↓ here)',\n modelsAutoChainOff: 'Take out of Auto',\n modelsAutoChainEmpty: 'No model is enabled, so Auto has nothing to try.',\n autoSwitchLabel: 'Offer Auto in the model picker',\n modelsPinned: 'pinned',\n usageTitle: 'Plan and usage',\n usagePlan: 'Plan: {plan}',\n usageRenews: 'renews {time}',\n usageReset: 'resets {time}',\n usageResetsInMinutes: 'resets in {minutes}m',\n usageResetsInHours: 'resets in {hours}h',\n usageResetDue: 'resets shortly',\n usageRefresh: 'Refresh usage',\n usageFetched: 'read {time}',\n usageNone: 'This provider reports no plan quota.',\n usageError: 'Could not read usage: {message}',\n usage_5h: '5-hour window',\n usage_weekly: 'Weekly window',\n usage_credits: 'Included credit',\n usage_gemini: 'Gemini allowance (rolling)',\n usage_gemini_weekly: 'Gemini allowance (weekly)',\n usage_claude: 'Claude allowance (rolling)',\n usage_claude_weekly: 'Claude allowance (weekly)',\n usage_other: 'Allowance',\n refresh: 'Refresh',\n refreshing: 'Refreshing…',\n modelsRefreshed: 'Model list refreshed.',\n refreshToken: 'Refresh token',\n tokenRefreshed: 'Token refreshed; valid until {time}.',\n hint: 'Authorization follows the account the browser is signed in with. To add a different account, switch accounts there first, or use a private window.',\n manageTitle: 'Manage {provider}',\n manageAccounts: 'Accounts',\n reload: 'Reload credentials',\n reloadHint: 'Use this after signing in from another DeepSeek Harness window.',\n detail_oauthHost: 'Authorization server',\n detail_apiBase: 'API base',\n detail_credentialFile: 'Credential file',\n detail_project: 'Cloud project',\n lastError: 'Last login attempt failed: {message}',\n loginFailed: 'Could not start the sign-in: {message}',\n actionFailed: 'Action failed: {message}',\n usageChipTitle: '{provider} subscription usage',\n usageChipFull: '{provider} allowance',\n} as const\n\n/** The Subscriptions namespace key union. */\nexport type SubscriptionsKey = keyof typeof en\n\n/** Chinese strings. */\nexport const zh = {\n nav: '订阅',\n intro: '使用你已付费的 AI 订阅 —— 无需 API Key。在下方选择服务,连接一次,其模型即进入模型选择器并显示实时额度。令牌仅保存在本机。',\n loading: '正在检查订阅…',\n unavailable: '订阅页面需要 Web 连接服务,当前组合未提供该服务。',\n statusConnected: '已连接 {count} 个账号',\n statusSignedOut: '未登录',\n statusBusy: '等待浏览器确认',\n statusSignIn: '登录后即可在模型选择器中使用该服务的模型。',\n statusAuto: '依次使用来自各服务的 {count} 个已启用模型',\n connect: '连接',\n edit: '编辑',\n collapse: '收起',\n addAccount: '添加账号',\n manage: '管理',\n cancel: '取消',\n close: '关闭',\n logout: '退出登录',\n makeDefault: '设为默认',\n defaultBadge: '默认',\n badgeLive: '已连接',\n accountNeedsLogin: '需重新登录',\n accountNeedsLoginHint: '服务方已拒绝该账号的授权,令牌无法自动续期',\n accountExpired: '令牌已过期',\n accountExpires: '过期时间 {time}',\n deviceTitle: '确认此设备',\n devicePrompt: '打开验证页面并确认下方代码,服务确认后本页面会自动更新。',\n deviceOpen: '打开验证页面',\n deviceCopy: '复制代码',\n deviceCopied: '已复制',\n deviceExpires: '代码将于 {time} 过期。',\n openLogin: '打开登录页面',\n openLoginHint: '请在打开的浏览器标签页中完成登录;服务回调后本页面会自动更新。',\n modelsTitle: '模型',\n modelsCount: '共 {count} 个',\n modelsLive: '实时',\n modelsBuiltIn: '内置',\n modelsShow: '在选择器中显示',\n modelsHiddenCount: '· 已隐藏 {count} 个',\n modelsHidden: '已从模型选择器隐藏',\n modelsEmpty: '暂未获取到模型。',\n modelsAdd: '添加模型',\n modelsAddHint: '手动添加一个本版本还不认识的模型 ID',\n modelsAddPlaceholder: '模型 ID,例如 k3-next',\n modelsAddConfirm: '添加',\n modelsRemove: '移除这个手动添加的模型',\n modelsContext: '{tokens} 上下文',\n modelsAuto: '自动',\n modelsAutoHint: '固定在首位:某个模型不可用时自动改用下一个',\n modelsAutoOff: '已关闭 —— 选择器将直接列出各个模型',\n modelsAutoChain: '自动会按这个顺序依次尝试(拖动可调整)',\n modelsDrag: '拖动调整顺序(也可在此按 ↑ / ↓)',\n modelsAutoChainOff: '从自动中移除',\n modelsAutoChainEmpty: '没有启用任何模型,自动没有可尝试的模型。',\n autoSwitchLabel: '在模型选择器中提供 Auto',\n modelsPinned: '固定',\n usageTitle: '套餐与额度',\n usagePlan: '套餐:{plan}',\n usageRenews: '续费时间 {time}',\n usageReset: '重置时间 {time}',\n usageResetsInMinutes: '{minutes} 分钟后重置',\n usageResetsInHours: '{hours} 小时后重置',\n usageResetDue: '即将重置',\n usageRefresh: '刷新额度',\n usageFetched: '读取于 {time}',\n usageNone: '该服务未提供套餐额度信息。',\n usageError: '额度读取失败:{message}',\n usage_5h: '5 小时窗口',\n usage_weekly: '每周窗口',\n usage_credits: '包含额度',\n usage_gemini: 'Gemini 模型额度(滚动)',\n usage_gemini_weekly: 'Gemini 模型额度(每周)',\n usage_claude: 'Claude 模型额度(滚动)',\n usage_claude_weekly: 'Claude 模型额度(每周)',\n usage_other: '额度',\n refresh: '刷新',\n refreshing: '正在刷新…',\n modelsRefreshed: '模型列表已刷新。',\n refreshToken: '刷新令牌',\n tokenRefreshed: '令牌已刷新,有效至 {time}。',\n hint: '授权以浏览器当前登录的账号为准;要添加不同账号,请先在浏览器里切换账号,或使用无痕窗口。',\n manageTitle: '管理 {provider}',\n manageAccounts: '账号',\n reload: '重新读取凭据',\n reloadHint: '在另一个 DeepSeek Harness 窗口中登录后,请使用此操作。',\n detail_oauthHost: '授权服务器',\n detail_apiBase: 'API 地址',\n detail_credentialFile: '凭据文件',\n detail_project: '云项目',\n lastError: '上次登录失败:{message}',\n loginFailed: '无法开始登录:{message}',\n actionFailed: '操作失败:{message}',\n usageChipTitle: '{provider} 订阅用量',\n usageChipFull: '{provider} 额度',\n} satisfies Record<SubscriptionsKey, string>\n","/**\n * Scoped stylesheet for the Subscriptions page and its manage dialog.\n *\n * The host loads plugin markup without its own control classes, and a bare\n * `<button>` therefore renders with the browser's default raised chrome — which\n * reads as a stray highlight in a dark theme. Rather than invent a look, this\n * follows the control vocabulary of the sibling `dsh-plugin-subscriptions`\n * plugin (same author-facing design as the shell's settings forms): a 6 px\n * radius small control, a transparent fill, a hairline border from the theme's\n * alias tokens, and the theme's hover/active/focus tokens for state.\n *\n * The tokens all carry fallbacks, so the page still renders correctly on a host\n * build that does not define them.\n *\n * @module dsh-plugin-subscription-oauth/client/styles\n */\n\n/**\n * Everything is scoped to this plugin's own roots, so no other surface in the\n * shell can be affected by a rule here.\n */\nconst scope = ':is(.dsh-subscriptions-settings, .dsh-subscription-manager)'\n\n/** The page's and dialog's stylesheet. */\nexport const subscriptionsCss = `\n.dsh-subscriptions-settings,\n.dsh-subscription-manager {\n font-family: inherit;\n font-size: 13px;\n line-height: 1.5;\n /* Native controls (checkbox, dialog) follow the host theme, not the OS. */\n color-scheme: light;\n}\nbody[data-ds-dark-theme] ${scope} {\n color-scheme: dark;\n}\n.dsh-subscription-manager {\n box-sizing: border-box;\n padding: 0;\n border: 0;\n border-radius: var(--dsw-radius-panel, 16px);\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n color: var(--dsw-alias-label-primary);\n box-shadow: var(--dsw-elevation-prominent, 0 20px 70px #0004);\n}\n.dsh-subscription-manager::backdrop {\n background: var(--dsw-alias-bg-mask-1, #0006);\n backdrop-filter: var(--dsw-mask-blur, none);\n}\n${scope} h2 {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n}\n${scope} strong,\n${scope} .dsh-subscription-label {\n font-size: 13px;\n line-height: 1.5;\n font-weight: 500;\n}\n${scope} small,\n${scope} .dsh-subscription-hint,\n${scope} [role=\"status\"] {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n${scope} .dsh-subscription-error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-state-error-primary, #b42318);\n}\n${scope} .dsh-subscription-success {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-state-success-primary, #067647);\n}\n${scope} label:has(> input[type=\"checkbox\"]) {\n cursor: pointer;\n}\n${scope} input[type=\"checkbox\"] {\n accent-color: var(--dsw-alias-brand-primary);\n}\n/*\n * A model's on/off control is a switch, not a checkbox: it takes effect\n * immediately and its state is what matters, not a tick. Drawn from the theme\n * tokens with appearance: none, so it follows the host palette in both themes\n * instead of the platform's checkbox chrome.\n */\n${scope} input[type=\"checkbox\"].dsh-subscription-switch {\n appearance: none;\n -webkit-appearance: none;\n position: relative;\n box-sizing: border-box;\n flex-shrink: 0;\n width: 34px;\n height: 20px;\n margin: 0;\n border: 0.5px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-2));\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch::after {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary-foreground, #fff);\n box-shadow: 0 1px 2px #0003;\n transition: transform 120ms ease;\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch:checked {\n border-color: transparent;\n background: var(--dsw-alias-state-success-primary, var(--dsw-alias-brand-primary, #067647));\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch:checked::after {\n transform: translateX(14px);\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch:disabled {\n opacity: 0.4;\n cursor: default;\n}\n${scope} button {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n min-height: 28px;\n padding: 0 10px;\n border: 0.5px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n${scope} button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-alias-bg-layer-3));\n}\n${scope} button:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active, var(--dsw-alias-bg-layer-3));\n}\n${scope} button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n/* A control that is present but should not read as a bordered button. */\n${scope} button.dsh-subscription-quiet {\n min-height: 22px;\n padding: 0 4px;\n border-color: transparent;\n color: var(--dsw-alias-label-secondary, var(--dsw-alias-label-primary));\n}\n/* The free-text field for a hand-added model id. */\n${scope} input.dsh-subscription-input {\n box-sizing: border-box;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 28px;\n padding: 0 10px;\n border: 0.5px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));\n border-radius: var(--dsw-radius-sm, 6px);\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n}\n${scope} input.dsh-subscription-input::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n${scope} input.dsh-subscription-input:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n/* A drag handle: no border or fill, so it reads as a grip rather than a button. */\n${scope} button.dsh-subscription-handle {\n min-height: 18px;\n height: 18px;\n padding: 0 2px;\n border-color: transparent;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-tertiary, var(--dsw-alias-label-secondary));\n font-size: 12px;\n line-height: 18px;\n cursor: grab;\n}\n${scope} button.dsh-subscription-handle:hover:not(:disabled) {\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n${scope} button.dsh-subscription-handle:active:not(:disabled) { cursor: grabbing; }\n/* Controls inside the manage dialog are one step larger, like a form footer. */\n${scope} .dsh-subscription-dialog-actions button {\n min-height: 34px;\n padding: 0 14px;\n border-radius: var(--dsw-radius-md, 8px);\n font-size: 13px;\n line-height: 1.5;\n}\n${scope} button.dsh-subscription-primary {\n border-color: transparent;\n background: var(--dsw-alias-button-primary-fill, var(--dsw-alias-label-primary));\n color: var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-layer-1));\n font-weight: 500;\n}\n${scope} button.dsh-subscription-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover, var(--dsw-alias-label-secondary));\n}\n${scope} button:focus-visible,\n${scope} input[type=\"checkbox\"]:focus-visible,\n${scope} .dsh-subscription-disclosure:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n/* The catalog heading is a summary-style disclosure, not a bordered button. */\n${scope} .dsh-subscription-disclosure {\n border-color: transparent;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n padding: 0 4px;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary, var(--dsw-alias-label-primary));\n}\n${scope} .dsh-subscription-disclosure:hover:not(:disabled) {\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n/* A usage meter: the track, with the fill sized by the caller. */\n${scope} .dsh-subscription-meter {\n height: 6px;\n border-radius: 999px;\n /* The track must stay visible when nothing is spent — a 0 % window otherwise reads as\n \"no meter at all\", which is the opposite of \"nothing used\". The layer token falls\n back to the card's own colour on a host that does not define it, so the neutral is\n explicit and translucent, and the inset ring keeps the empty track visible on a\n light theme as well as a dark one. */\n background: #8a8f9826;\n box-shadow: inset 0 0 0 1px #8a8f9833;\n overflow: hidden;\n}\n${scope} .dsh-subscription-meter > span {\n display: block;\n height: 100%;\n background: var(--dsw-alias-state-success-primary, #067647);\n}\n${scope} .dsh-subscription-meter > span[data-exhausted=\"true\"] {\n background: var(--dsw-alias-state-error-primary, #b42318);\n}\n`\n\n/**\n * The composer usage chip's stylesheet.\n *\n * Separate from the page's, because the chip renders in the composer's own bar rather\n * than inside either page container — the page stylesheet is scoped to those\n * containers on purpose, so nothing here may leak into the shell.\n *\n * The chip inherits the bar's type and only draws a meter, so it sits beside the shell's\n * own stats pills without looking like a second design. The trigger is a bare control:\n * the host gives plugin markup no control classes, and a default `<button>` would\n * otherwise read as a raised chip. Every token carries a fallback for hosts that do not\n * define it.\n */\nexport const usageChipCss = `\n.dsh-subscription-usage-chip {\n position: relative;\n display: inline-flex;\n align-items: center;\n /* The bar is a centred flex row whose children are this slot's entries followed by the\n shell's context meter, and a list slot renders its entries as bare children (no\n wrapper). So the chip is itself a flex item: a higher order places it after the\n context meter, and no auto margin is set, so it sits immediately beside it rather\n than being pushed to the edge of the bar. */\n order: 1;\n min-width: 0;\n font-family: inherit;\n font-size: 12px;\n line-height: 1;\n}\n.dsh-subscription-usage-chip-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 2px 6px;\n border: 0;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font: inherit;\n cursor: pointer;\n}\n.dsh-subscription-usage-chip-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n}\n.dsh-subscription-usage-chip-trigger:focus-visible {\n outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);\n outline-offset: 1px;\n}\n.dsh-subscription-usage-chip-label {\n overflow: hidden;\n max-width: 6ch;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-subscription-usage-chip-cell {\n display: inline-flex;\n align-items: baseline;\n gap: 2px;\n}\n.dsh-subscription-usage-chip-window {\n opacity: 0.75;\n}\n.dsh-subscription-usage-chip-value {\n color: var(--dsw-alias-label-primary, #e8e8e8);\n font-variant-numeric: tabular-nums;\n}\n.dsh-subscription-usage-chip-account {\n display: flex;\n flex-direction: column;\n gap: 6px;\n /* A hairline between accounts, so two sign-ins read as two groups not one run. */\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-secondary, #ffffff14);\n}\n.dsh-subscription-usage-chip-account:first-of-type {\n padding-top: 0;\n border-top: 0;\n}\n.dsh-subscription-usage-chip-panel {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 180px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-secondary, #ffffff1f);\n border-radius: var(--dsw-radius-md, 10px);\n background: var(--dsw-alias-bg-layer-2, #26262a);\n box-shadow: var(--dsw-elevation-prominent, 0 12px 32px #0007);\n color: var(--dsw-alias-label-primary, #e8e8e8);\n white-space: nowrap;\n}\n.dsh-subscription-usage-chip-full {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n}\n.dsh-subscription-usage-chip-meter {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.dsh-subscription-usage-chip-meterHead {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n.dsh-subscription-usage-chip-detail {\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font-variant-numeric: tabular-nums;\n}\n.dsh-subscription-usage-chip-track {\n display: block;\n width: 100%;\n height: 5px;\n border-radius: 999px;\n /* As on the settings meter: an unspent window has to show its track, so the neutral and\n its ring are explicit rather than a layer token that may match the panel. */\n background: #8a8f9826;\n box-shadow: inset 0 0 0 1px #8a8f9833;\n overflow: hidden;\n}\n.dsh-subscription-usage-chip-fill {\n display: block;\n height: 100%;\n background: var(--dsw-alias-state-success-primary, #067647);\n}\n`\n\n/**\n * The composer's quick-mode button stylesheet.\n *\n * It renders in the composer's own control row — the same row as the model selector —\n * so it borrows that row's geometry: a compact, borderless control that only colours on\n * hover and when the mode is on. Tokens carry fallbacks for hosts that define none.\n */\nexport const fastModeCss = `\n.dsh-subscription-fast-mode {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 3px;\n border: 0;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n}\n.dsh-subscription-fast-mode:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n}\n/* A click has to look like one, even before the selection lands. */\n.dsh-subscription-fast-mode:active {\n background: var(--dsw-alias-interactive-bg-pressed, #ffffff2e);\n}\n.dsh-subscription-fast-mode[data-pending=\"true\"] {\n opacity: 0.7;\n cursor: progress;\n}\n/* A switch the host refused is marked rather than left looking merely off, so a failure\n is visible where it happened; the reason rides the title and the console. */\n.dsh-subscription-fast-mode[data-failed=\"true\"] {\n color: var(--dsw-alias-state-error-primary, #b42318);\n}\n.dsh-subscription-fast-mode:focus-visible {\n outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);\n outline-offset: 1px;\n}\n/* Off: the bolt is dim, so an available mode reads as an invitation, not a state. */\n.dsh-subscription-fast-mode[data-active=\"false\"] .dsh-subscription-fast-mode-bolt {\n opacity: 0.55;\n}\n/* On: the bolt takes the accent and glows, which is the only thing distinguishing the\n fast variant from its base — both answer with the same model name. */\n.dsh-subscription-fast-mode[data-active=\"true\"] .dsh-subscription-fast-mode-bolt {\n color: var(--dsw-alias-state-warning-primary, #f5a524);\n filter: drop-shadow(0 0 6px var(--dsw-alias-state-warning-primary, #f5a52455));\n}\n.dsh-subscription-fast-mode-reason {\n max-width: 14ch;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n}\n`\n","/**\n * Wire contract shared by the plugin's node half (the RPC routes) and its\n * browser half (the Settings page).\n *\n * Only this module crosses the boundary: the client bundle imports the types\n * and the channel name, and every call travels as one JSON envelope over a\n * `POST /api/<prefix><endpoint>` route. One status document describes every\n * provider route the plugin serves, so the page renders one card per entry.\n *\n * @module dsh-plugin-subscription-oauth/protocol\n */\n\n/** Prefix of every RPC method this plugin serves. */\nexport const RPC_PREFIX = 'subscription-oauth.'\n\n/** Endpoints the Settings page may call. */\nexport const RPC_ENDPOINTS = [\n 'status',\n 'login',\n 'cancel',\n 'logout',\n 'setDefault',\n 'refresh',\n 'discover',\n 'setModelVisible',\n 'setModelOrder',\n 'addModel',\n 'removeModel',\n 'usage',\n 'reload',\n 'auto-state',\n 'setAutoOn',\n 'deepseek-balance',\n 'ccswitch-balance',\n 'model-variants',\n] as const\n\n/** One endpoint name. */\nexport type RpcEndpoint = typeof RPC_ENDPOINTS[number]\n\n/** Client → host envelope. */\nexport interface RpcRequest {\n type: 'client-request'\n rpcId: string\n method: string\n payload: unknown\n}\n\n/** Host → client envelope, carrying either the value or the failure. */\nexport interface RpcResponse {\n type: 'server-response'\n rpcId: string\n result: RpcResult\n}\n\n/** Discriminated result inside {@link RpcResponse}. */\nexport type RpcResult =\n | { ok: true, value: unknown }\n | { ok: false, error: RpcError }\n\n/** Failure detail, with the provider-neutral code when one applies. */\nexport interface RpcError {\n code: string\n message: string\n /**\n * Carrier-owned structured detail. The Connection transport models a failure\n * as `{code, message, details}`, so an empty object travels here rather than\n * an absent field the client would have to repair.\n */\n details: Record<string, unknown>\n}\n\n/**\n * One provider-owned connection fact the manage dialog shows.\n *\n * The key is a stable identifier (`oauthHost`, `apiBase`, …) that the browser\n * half localizes; the host never sends user-facing prose.\n */\nexport interface DetailRow {\n key: string\n value: string\n}\n\n/** How the browser should present one pending login. */\nexport type LoginMode =\n /** Open the URL in a new tab; the redirect completes the login. */\n | 'open-url'\n /** Show the code (and a link) while the host polls the token endpoint. */\n | 'device-code'\n\n/** What the Settings page has to show to finish a login. */\nexport interface LoginPrompt {\n /** URL the user opens (or that was opened for them). */\n readonly url: string\n /** Code the user confirms in the browser; device flows only. */\n readonly userCode?: string\n /** Epoch milliseconds after which the attempt lapses. */\n readonly expiresAt: number\n readonly mode: LoginMode\n}\n\n/** One signed-in account as the page displays it. */\nexport interface AccountStatus {\n /** Account key the store is keyed by. */\n account: string\n /** Display label (email, user id, or the key's digest). */\n label: string\n /** Epoch milliseconds when the access token lapses. */\n expiresAt: number\n /** Whether the access token is already past its expiry. */\n expired: boolean\n /** Whether this account serves requests by default. */\n isDefault: boolean\n /**\n * Whether the provider refused this account's grant outright.\n *\n * A refresh answered with `invalid_grant` means the authorization is gone — the\n * stored token can never be renewed — so the page asks for a new sign-in rather\n * than reporting the account as connected.\n */\n needsLogin?: boolean\n}\n\n/** One selectable model, as the page lists it. */\nexport interface ModelSummary {\n id: string\n name: string\n description?: string\n contextWindow: number\n inputModalities?: readonly string[]\n defaultEffort?: string\n /** Whether the session model picker offers this model. */\n visible: boolean\n /**\n * Route that owns this model, when the listing spans routes.\n *\n * The \u0007uto provider mixes models from every route, so its rows name the\n * owning route; a single-route catalog leaves it out.\n */\n source?: string\n /** Whether the user added this id by hand, so it can be removed again. */\n userAdded?: boolean\n /**\n * Whether this entry is fixed at the head of the list.\n *\n * The synthetic `auto` entry follows the user's own order, so it cannot be\n * hidden or moved; the page renders it without those controls.\n */\n pinned?: boolean\n /** Fast/lightspeed variant wire id, when the provider reported one for this base. */\n fastVariant?: string\n}\n\n/** One metered allowance of a subscription plan. */\nexport interface UsageWindow {\n /** What the window meters, e.g. `weekly`, `5h`, `credits`. */\n label: string\n /** How much of the allowance is spent, when the provider states it. */\n usedPercent?: number\n /** Amount already spent, when the provider states an absolute figure. */\n used?: number\n /** Total allowance, when the provider states one. */\n limit?: number\n /** Epoch milliseconds when the window resets, when the provider states it. */\n resetsAt?: number\n}\n\n/** One account's plan and metered allowances. */\nexport interface ProviderUsage {\n /** Account the figures belong to. */\n account: string\n /** Plan name or tier, when the provider states one. */\n plan?: string\n /** Whether the plan renews on a date the provider states. */\n renewsAt?: number\n /** Metered allowances, most relevant first. */\n windows: readonly UsageWindow[]\n /** Epoch milliseconds these figures were read. */\n fetchedAt: number\n /**\n * Whether these figures are the route's substitute rather than its own accounting.\n *\n * A substitute is shown when the primary source cannot answer, and it is never\n * reused as if it were a reading: the next request asks again.\n */\n substitute?: boolean\n}\n\n/** Result of reading one route's usage. */\nexport interface UsageResult {\n provider: string\n /** Usage per account the endpoint could answer for. */\n usage: readonly ProviderUsage[]\n /** Why an account's usage is missing, when the read failed. */\n error?: string\n status: SubscriptionStatus\n}\n\n/** The full state of one provider route. */\nexport interface ProviderStatus {\n /** Provider route key, e.g. `kimi`. */\n provider: string\n displayName: string\n /** Where a user manages the underlying subscription. */\n codeUrl?: string\n /** Whether at least one account is stored. */\n loggedIn: boolean\n /**\n * Whether this route signs in at all.\n *\n * \u0007uto has no account of its own — it delegates to routes that do — so the\n * page hides the connect and manage controls for it.\n */\n connectable?: boolean\n /** Whether a login attempt is being polled. */\n busy: boolean\n /** The pending attempt, when `busy`. */\n pending?: LoginPrompt\n accounts: AccountStatus[]\n /** Account key serving requests, when one does. */\n activeAccount?: string\n /** Absolute path of the credential file, for support requests. */\n credentialFile: string\n /** Whether the live model list is consulted. */\n discovery: boolean\n /** Live catalog ids, when discovery has succeeded this session. */\n discovered?: readonly string[]\n models: readonly ModelSummary[]\n /** Model the picker preselects. */\n defaultModel?: string\n /** Last usage figures read for this route, when they were requested. */\n usage?: readonly ProviderUsage[]\n /** Why the last usage read failed, when it did. */\n usageError?: string\n /** Provider-owned connection facts shown in the manage dialog. */\n details: readonly DetailRow[]\n /** Last background failure (a login that could not complete). */\n lastError?: string\n}\n\n/** Every provider route's state, in display order. */\nexport interface SubscriptionStatus {\n providers: ProviderStatus[]\n}\n\n/** Result of starting a login. */\nexport interface LoginStart {\n prompt: LoginPrompt\n status: SubscriptionStatus\n}\n\n/** Result of a token refresh. */\nexport interface RefreshResult {\n account: string\n expiresAt: number\n status: SubscriptionStatus\n}\n\n/** Payload fields naming one provider route and optionally one account. */\nexport interface AccountRequest {\n provider: string\n account?: string\n}\n\n/** Payload of `setDefault`. */\nexport interface SetDefaultRequest {\n provider: string\n account: string\n}\n\n/** Payload of `discover`. */\nexport interface DiscoverRequest {\n provider: string\n /** Ask the endpoint for its live model list even when one is cached. */\n force?: boolean\n}\n\n/** Payload of `setModelVisible`. */\nexport interface SetModelVisibleRequest {\n provider: string\n /** Model id as the catalog reports it. */\n id: string\n /** Whether the session model picker should offer it. */\n visible: boolean\n}\n\n/** Payload of `addModel` and `removeModel`. */\nexport interface ModelIdRequest {\n provider: string\n /** Model id as the endpoint accepts it. */\n id: string\n}\n\n/** Payload of `setModelOrder`. */\nexport interface SetModelOrderRequest {\n provider: string\n /** Every catalog model id, in the order the user arranged them. */\n ids: readonly string[]\n}\n\n/** Payload naming only a provider route. */\nexport interface ProviderRequest {\n provider: string\n}\n\n/** Map an endpoint to its payload and value types. */\nexport interface RpcMap {\n status: { payload: undefined, value: SubscriptionStatus }\n login: { payload: AccountRequest, value: LoginStart }\n cancel: { payload: ProviderRequest, value: SubscriptionStatus }\n logout: { payload: AccountRequest, value: SubscriptionStatus }\n setDefault: { payload: SetDefaultRequest, value: SubscriptionStatus }\n refresh: { payload: AccountRequest, value: RefreshResult }\n discover: { payload: DiscoverRequest, value: SubscriptionStatus }\n setModelVisible: { payload: SetModelVisibleRequest, value: SubscriptionStatus }\n setModelOrder: { payload: SetModelOrderRequest, value: SubscriptionStatus }\n addModel: { payload: ModelIdRequest, value: SubscriptionStatus }\n removeModel: { payload: ModelIdRequest, value: SubscriptionStatus }\n usage: { payload: DiscoverRequest, value: UsageResult }\n reload: { payload: undefined, value: SubscriptionStatus }\n 'auto-state': { payload: undefined, value: AutoStateResult }\n setAutoOn: { payload: SetAutoOnRequest, value: AutoStateResult }\n 'deepseek-balance': { payload: DeepSeekBalanceRequest, value: DeepSeekBalanceResult }\n 'ccswitch-balance': { payload: CcSwitchBalanceRequest, value: CcSwitchBalanceResult }\n 'model-variants': { payload: ModelVariantsRequest, value: ModelVariantsResult }\n}\n\n/** One currency's wallet balance, as the platform reports it. */\nexport interface WalletBalance {\n /** ISO currency the balance is denominated in. */\n currency: 'CNY' | 'USD' | string\n /** Remaining balance, as a decimal string. */\n balance: string\n}\n\n/** The DeepSeek account's wallet balances, normalised from the host's account service. */\nexport interface AccountBalance {\n /** Normal (topped-up) wallets, one per currency. */\n wallets: readonly WalletBalance[]\n /** Bonus / promotional wallets, one per currency. */\n bonusWallets: readonly WalletBalance[]\n}\n\n/** Result of reading the DeepSeek account's balance through the host. */\nexport type BalanceResult =\n /** The account answered with its balances. */\n | { ok: true, balance: AccountBalance }\n /** The account is signed out, or the read failed. */\n | { ok: false }\n\n/** One model's fast and image variants, as discovery reported them. */\nexport interface ModelVariants {\n /** Fast/lightspeed variant wire id, when the provider reported one. */\n fast?: string\n /**\n * The base model the fast variant belongs to.\n *\n * Sent with `fast` so the picker button can switch back off when the selection is\n * already the fast variant, which is not itself a listed row.\n */\n fastBase?: string\n /**\n * The reasoning rung that selects the fast variant.\n *\n * Each provider names its own variant (xAI `lightspeed`, Kimi `highspeed`), so the\n * rung is the provider's word — the picker reads it, and the stream maps it back to\n * the variant's wire id. Defaults to `fast`.\n */\n fastRung?: string\n /** Image-generation model wire id, when the provider reported one. */\n image?: string\n}\n\n/** Result of `model-variants`: the variants for one model id. */\nexport type ModelVariantsResult = ModelVariants | undefined\n\n/** Payload of `model-variants`. */\nexport interface ModelVariantsRequest {\n provider: string\n id: string\n}\n\n/** Payload of `setAutoOn`: whether the `auto` provider is offered at all. */\nexport interface SetAutoOnRequest {\n enabled: boolean\n}\n\n/** Result of `setAutoOn`: the `auto` provider's live enabled state. */\nexport interface AutoStateResult {\n enabled: boolean\n}\n\n/** Payload of `deepseek-balance`. */\nexport interface DeepSeekBalanceRequest {\n /** Client metadata the host's account service expects. */\n version?: string\n locale?: string\n timezoneOffsetSeconds?: number\n}\n\n/** One currency's wallet, as the host's account service reports it. */\nexport interface DeepSeekWallet {\n /** ISO currency the balance is denominated in. */\n currency: string\n /** Remaining balance, as a decimal string. */\n balance: string\n}\n\n/**\n * Result of `deepseek-balance`.\n *\n * `undefined` means the account is signed out or the read failed, so a chip stays\n * silent rather than showing a wrong figure.\n */\nexport type DeepSeekBalanceResult =\n | {\n /** Normal (topped-up) wallets, one per currency. */\n wallets: readonly DeepSeekWallet[]\n /** Bonus / promotional wallets, one per currency. */\n bonusWallets: readonly DeepSeekWallet[]\n }\n | undefined\n\n/** Payload of `ccswitch-balance`. */\nexport interface CcSwitchBalanceRequest {\n /** The provider whose balance to read; the server maps it to its credential reference. */\n provider: string\n}\n\n/**\n * Result of `ccswitch-balance`.\n *\n * `undefined` means the provider did not answer with a balance — no key, a key it\n * rejects, or a body that is not the balance shape. A chip shows nothing rather than a\n * wrong figure, so this is deliberately not an error.\n */\nexport type CcSwitchBalanceResult =\n | {\n /** The currency the balance is denominated in. */\n currency: string\n /** The remaining balance, as a decimal string. */\n total: string\n /** Whether the account reports itself as usable. */\n available: boolean\n }\n | undefined\n","/**\n * Browser caller for the plugin's own RPC channel.\n *\n * The node half mounts one exact `POST /api/subscription-oauth.<endpoint>` route\n * per endpoint, so every call travels on the shared `/api` channel with a\n * prefixed endpoint name — the same wire the rest of the shell uses.\n *\n * @module dsh-plugin-subscription-oauth/client/subscriptions-rpc\n */\n\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\n\nimport { RPC_PREFIX } from '../protocol.js'\nimport type { CcSwitchBalanceResult, RpcEndpoint, RpcMap } from '../protocol.js'\n\n/** The shared channel the node half's routes are mounted on. */\nconst CHANNEL = '/api'\n\n/** A failed call: either the transport rejected or the endpoint answered `ok: false`. */\nexport class SubscriptionsRpcError extends Error {\n constructor(message: string, readonly code: string) {\n super(message)\n this.name = 'SubscriptionsRpcError'\n }\n}\n\n/** Human-readable message for any thrown value. */\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Call one endpoint, unwrapping the transport result. */\nexport type SubscriptionsCall = <K extends RpcEndpoint>(\n endpoint: K,\n payload: RpcMap[K]['payload'],\n) => Promise<RpcMap[K]['value']>\n\n/** A caller for one non-subscription provider's cc-switch balance. */\nexport interface CcSwitchBalanceCall {\n (provider: string): Promise<CcSwitchBalanceResult>\n}\n\n/**\n * Bind a caller to one connection's RPC transport.\n * @param rpc - the browser connection's RPC caller.\n * @returns a typed caller for this plugin's endpoints.\n */\nexport function createSubscriptionsCall(rpc: ConnectionHandle['rpc']): SubscriptionsCall {\n return async function call(endpoint, payload) {\n let result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>\n try {\n result = await rpc.call(CHANNEL, `${RPC_PREFIX}${endpoint}`, payload)\n } catch (error: unknown) {\n throw new SubscriptionsRpcError(messageOf(error), 'RPC_TRANSPORT')\n }\n if (!result.ok) throw new SubscriptionsRpcError(result.error.message, result.error.code)\n return result.value as never\n }\n}\n\n/**\n * The DeepSeek account's balance, as the plugin's node half reports it.\n *\n * The node half reads the host's `deepseekAccount` service (which owns the OAuth grant\n * and the platform endpoint) and serves the wallets here. It answers `undefined` when\n * the account is signed out or the read failed, so a chip simply does not render.\n */\nexport interface AccountBalance {\n /** Normal (topped-up) wallets, one per currency. */\n wallets: readonly { currency: string, balance: string }[]\n /** Bonus / promotional wallets, one per currency. */\n bonusWallets: readonly { currency: string, balance: string }[]\n}\n\n/** A narrow caller for the DeepSeek account's balance. */\nexport interface AccountBalanceCall {\n (): Promise<AccountBalance | undefined>\n}\n\n/**\n * Read the DeepSeek account's balance through the plugin's own RPC.\n *\n * The browser cannot call the host's account service directly — it sits behind the\n * host's Remote controller, not a `/api` fetch route — so the node half reads it and\n * serves it on the plugin's channel, the same one the rest of the chip's data travels.\n *\n * @param rpc - the browser connection's RPC caller.\n * @returns the balance, or undefined when there is nothing to show.\n */\nexport function createAccountBalanceCall(rpc: ConnectionHandle['rpc']): AccountBalanceCall {\n return async function call() {\n let result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>\n try {\n result = await rpc.call(CHANNEL, `${RPC_PREFIX}deepseek-balance`, {\n version: '1',\n locale: Intl.DateTimeFormat().resolvedOptions().locale ?? 'en-US',\n // East-positive whole seconds, as the host's account service expects (the same\n // value DSH's own account card sends).\n timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,\n })\n } catch (error: unknown) {\n throw new SubscriptionsRpcError(messageOf(error), 'RPC_TRANSPORT')\n }\n if (!result.ok) throw new SubscriptionsRpcError(result.error.message, result.error.code)\n return result.value as AccountBalance | undefined\n }\n}\n\n/**\n * Read a manually added provider's balance through the plugin's cc-switch endpoint.\n *\n * The caller names the credential reference the provider's profile uses (its\n * Read a non-subscription provider's balance through the plugin's cc-switch endpoint.\n *\n * The caller names the provider; the server maps it to the credential reference the\n * provider's profile names (`apiKeyEnv`) and resolves that to the API key. The result\n * is `undefined` when there is no key or the provider did not answer — never a wrong\n * figure.\n *\n * @param rpc - the browser connection's RPC caller.\n * @returns a narrow caller taking the provider id.\n */\nexport function createCcSwitchBalanceCall(rpc: ConnectionHandle['rpc']): CcSwitchBalanceCall {\n return async function call(provider: string) {\n let result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>\n try {\n result = await rpc.call(CHANNEL, `${RPC_PREFIX}ccswitch-balance`, { provider })\n } catch (error: unknown) {\n throw new SubscriptionsRpcError(messageOf(error), 'RPC_TRANSPORT')\n }\n if (!result.ok) throw new SubscriptionsRpcError(result.error.message, result.error.code)\n return result.value as CcSwitchBalanceResult\n }\n}\n","/**\n * Subscriptions settings page.\n *\n * One card per provider route — Kimi Code, Grok (X Premium), Google Antigravity\n * — describing that route's connection, accounts, and model catalog. Every fact\n * arrives from the node half's `subscription-oauth` RPC endpoints, so this\n * component holds no credential state of its own. The status poll runs only\n * while a login is pending, so an idle page performs no requests.\n *\n * Colors resolve through `--dsw-alias-*` design tokens, so the page follows the\n * active theme; every user-visible string goes through the locale-bound `t`.\n *\n * @module dsh-plugin-subscription-oauth/client/SubscriptionsSection\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { CSSProperties } from 'react'\n\nimport { en } from './locales.js'\nimport type { SubscriptionsKey } from './locales.js'\nimport { subscriptionsCss } from './styles.js'\nimport { SubscriptionsRpcError } from './subscriptions-rpc.js'\nimport type { SubscriptionsCall } from './subscriptions-rpc.js'\nimport type {\n AccountStatus,\n DetailRow,\n LoginPrompt,\n ProviderStatus,\n ProviderUsage,\n SubscriptionStatus,\n UsageWindow,\n} from '../protocol.js'\n/** Poll cadence while a login attempt is pending. */\nconst POLL_INTERVAL_MS = 2000\n\n/**\n * How often an open page re-reads every signed-in route's allowance.\n *\n * Slow on purpose: it is fast enough that a figure shown on screen is current, and\n * slow enough that leaving the page open costs one small request a minute per\n * signed-in provider.\n */\nconst USAGE_POLL_INTERVAL_MS = 60_000\n\n/**\n * Disclosures start folded, every time the page is opened.\n *\n * Nothing is remembered between visits on purpose: the section is a list of\n * providers first, and a card left open last time should not decide what this\n * visit looks like. A fold is a gesture within one visit, not a stored setting.\n */\n\n/** Horizontal room the page takes inside the settings panel. */\nconst SECTION_WIDTH = 560\n\n/** What the node half injects into this section. */\nexport interface SubscriptionsSectionInjected {\n /** Typed caller for the plugin's RPC endpoints. */\n call: SubscriptionsCall\n /** Translate a key of this plugin's settings namespace. */\n t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string\n}\n\n/** Component props: the shell supplies `close`, the plugin supplies the rest. */\nexport type SubscriptionsSectionProps = Partial<SubscriptionsSectionInjected> & {\n /** Close the settings panel (owned by the settings shell). */\n close?: () => void\n}\n\n/** One card's transient message. */\ninterface CardMessage {\n tone: 'error' | 'success'\n text: string\n}\n\n/** Minimal translator used when the locale seat is absent (standalone render). */\nfunction fallbackTranslate(key: SubscriptionsKey, params?: Record<string, unknown>): string {\n const template: string = en[key]\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n}\n\n/** Human-readable message for any thrown value. */\nfunction messageOf(error: unknown): string {\n if (error instanceof SubscriptionsRpcError) return error.message\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Local time for one epoch-millisecond instant. */\nfunction formatTime(at: number): string {\n try {\n return new Date(at).toLocaleString()\n } catch {\n return String(at)\n }\n}\n\n/** How far ahead a reset is still stated as hours rather than a date. */\nconst RESET_RELATIVE_MS = 24 * 60 * 60 * 1000\n\n/**\n * One allowance reset, in the form a reader acts on.\n *\n * How far off it is decides how it is said: minutes inside the hour, hours inside\n * the day, and the date beyond that — \"137 小时后\" would be arithmetic rather than\n * information, and a window that comes back in forty minutes is better described in\n * minutes than as \"1 小时后\". Every route's windows go through here, so all of them\n * read the same way.\n *\n * @param at - epoch milliseconds the window resets.\n * @param t - translator, so the wording follows the page language.\n * @returns the reset text.\n */\nfunction formatReset(\n at: number,\n t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string,\n): string {\n const remaining = at - Date.now()\n if (remaining <= 0) return t('usageResetDue')\n if (remaining >= RESET_RELATIVE_MS) return t('usageReset', { time: formatTime(at) })\n // Rounded, and never below one: \"0 分钟后重置\" would read as a bug rather than as\n // \"any moment now\".\n if (remaining < 60 * 60 * 1000) {\n return t('usageResetsInMinutes', { minutes: Math.max(1, Math.round(remaining / 60_000)) })\n }\n return t('usageResetsInHours', { hours: Math.max(1, Math.round(remaining / 3_600_000)) })\n}\n\n/** Compact token count for a context window. */\nfunction formatTokens(tokens: number): string {\n if (tokens >= 1_000_000) return `${String(Math.round(tokens / 100_000) / 10)}M`\n if (tokens >= 1_000) return `${String(Math.round(tokens / 1_000))}K`\n return String(tokens)\n}\n\n/**\n * Routes worth asking for allowances.\n *\n * A route with no account of its own — `auto` — reports itself as signed in so its\n * card reads as usable, but it holds no allowance to read: asking it every minute\n * spends a request to be told nothing. The routes it stands in for are each read\n * on their own.\n *\n * @param providers - the routes in the current status.\n * @returns the route keys to read usage for.\n */\nfunction usageTargets(providers: readonly ProviderStatus[]): string[] {\n return providers\n .filter((provider) => provider.loggedIn && provider.connectable !== false)\n .map((provider) => provider.provider)\n}\n\nconst styles: Record<string, CSSProperties> = {\n section: {\n display: 'flex', flexDirection: 'column', gap: 12, maxWidth: SECTION_WIDTH,\n color: 'var(--dsw-alias-label-primary)',\n },\n sectionHeader: { display: 'flex', flexDirection: 'column', gap: 4, marginBottom: 2 },\n heading: { margin: 0, fontSize: 18, lineHeight: '26px', fontWeight: 600, color: 'var(--dsw-alias-label-primary)' },\n intro: { margin: 0, color: 'var(--dsw-alias-label-tertiary)', fontSize: 13, lineHeight: '20px', maxWidth: 560 },\n card: {\n border: '0.5px solid var(--dsw-alias-settings-card-stroke, var(--dsw-alias-border-l4))',\n borderRadius: 'var(--dsw-radius-xl, 16px)',\n background: 'var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2))',\n padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 8,\n transition: 'border-color 120ms ease, background 120ms ease',\n },\n cardOpen: {\n borderColor: 'var(--dsw-alias-border-l2, var(--dsw-alias-border-l3))',\n background: 'var(--dsw-alias-settings-card-fill-hover, var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2)))',\n },\n cardHeader: { display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer', userSelect: 'none' },\n /** Everything a folded card hides. */\n cardBody: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 2 },\n /** The one-line state a folded card shows in place of its body. */\n cardSummary: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n /** The status indicator: filled when live, a hollow ring when not. */\n dot: {\n width: 8, height: 8, borderRadius: '50%', flexShrink: 0,\n border: '1.5px solid var(--dsw-alias-label-dimmed)',\n background: 'transparent',\n },\n dotLive: {\n borderColor: 'var(--dsw-alias-state-success-primary, #067647)',\n background: 'var(--dsw-alias-state-success-primary, #067647)',\n },\n dotBusy: {\n borderColor: 'var(--dsw-alias-state-warn-primary, #f5a524)',\n background: 'var(--dsw-alias-state-warn-primary, #f5a524)',\n },\n name: { fontWeight: 600, fontSize: 14, lineHeight: '22px', color: 'var(--dsw-alias-label-primary)' },\n statusLine: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n errorLine: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },\n successLine: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-success-primary)' },\n actions: { display: 'flex', gap: 8, marginTop: 4, alignItems: 'center', flexWrap: 'wrap' },\n accountRow: {\n display: 'flex', flexDirection: 'column', gap: 6,\n border: '0.5px solid var(--dsw-alias-border-l2)', borderRadius: 'var(--dsw-radius-lg, 12px)',\n padding: '10px 12px', background: 'var(--dsw-alias-bg-layer-1)',\n },\n accountHeader: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },\n starButton: {\n border: 'none', padding: 0, width: 20, background: 'transparent',\n font: 'inherit', fontSize: 14, lineHeight: '20px', cursor: 'pointer',\n color: 'var(--dsw-alias-state-warn-label)',\n },\n accountName: { fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)', userSelect: 'all' },\n badge: {\n fontSize: 10, lineHeight: '14px', padding: '1px 7px', borderRadius: 999, fontWeight: 500,\n background: 'var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-1))',\n border: '0.5px solid var(--dsw-alias-border-l2)',\n color: 'var(--dsw-alias-label-secondary)',\n },\n badgeDefault: {\n fontSize: 10, lineHeight: '14px', padding: '1px 7px', borderRadius: 999, fontWeight: 500,\n background: 'var(--dsw-alias-state-success-primary, #067647)',\n color: 'var(--dsw-alias-label-primary-foreground, #fff)',\n },\n badgeWarn: {\n fontSize: 10, lineHeight: '14px', padding: '1px 7px', borderRadius: 999, fontWeight: 500,\n background: 'var(--dsw-alias-state-warn-primary, #f5a524)',\n color: 'var(--dsw-alias-label-primary-foreground, #fff)',\n },\n pushRight: { marginLeft: 'auto', flexShrink: 0 },\n catalog: {\n display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4,\n borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: 8,\n },\n catalogHeader: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },\n catalogTitle: { fontSize: 12, lineHeight: '18px', fontWeight: 500, color: 'var(--dsw-alias-label-secondary)' },\n catalogMeta: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n /**\n * Button surface geometry lives in the scoped stylesheet; only the layout a\n * rule cannot express stays inline.\n */\n pill: {},\n pillSmall: {},\n /** The catalog heading: a summary-style disclosure, not a bordered control. */\n disclosure: {\n display: 'inline-flex', alignItems: 'center', gap: 4, margin: 0,\n },\n chevron: { fontSize: 10, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n modelList: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 2 },\n /** One model line: a visibility toggle and the model's own metadata. */\n modelRowGrow: { display: 'flex', alignItems: 'flex-start', gap: 8, flex: '1 1 auto', minWidth: 0 },\n /** The pinned `auto` line: listed, switchable, and never part of its own chain. */\n autoRow: {\n display: 'flex', alignItems: 'flex-start', gap: 8, cursor: 'pointer',\n fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)',\n },\n /** The models `auto` will walk, indented under it. */\n chain: {\n display: 'flex', flexDirection: 'column', gap: 3,\n margin: '2px 0 4px 26px', paddingLeft: 8,\n borderLeft: '0.5px solid var(--dsw-alias-border-l2)',\n },\n chainTitle: { fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)' },\n /** The add-a-model-id row: the field, then confirm and cancel. */\n addRow: { display: 'flex', alignItems: 'center', gap: 6, marginTop: 2 },\n addInput: {},\n /** The remove control on a hand-added row: a bare glyph beside the row. */\n removeButton: { flexShrink: 0, alignSelf: 'center', padding: '0 6px' },\n /** One draggable chain row: the grip, then the remove-from-auto switch. */\n chainRowWrap: { display: 'flex', alignItems: 'center', gap: 4 },\n chainRow: {\n display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer', flex: '1 1 auto',\n fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)',\n },\n usage: {\n display: 'flex', flexDirection: 'column', gap: 8, marginTop: 2,\n borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: 10,\n },\n usageHeader: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },\n usageRow: { display: 'flex', flexDirection: 'column', gap: 4 },\n usageLine: { display: 'flex', alignItems: 'baseline', gap: 8, justifyContent: 'space-between' },\n usageMeter: {\n height: 6, borderRadius: 999, background: 'var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-2))',\n overflow: 'hidden', boxShadow: 'inset 0 0 0 1px #8a8f9833',\n },\n usageAccount: {\n display: 'flex', flexDirection: 'column', gap: 6,\n padding: '8px 10px', borderRadius: 'var(--dsw-radius-md, 10px)',\n background: 'var(--dsw-alias-bg-layer-1)', border: '0.5px solid var(--dsw-alias-border-l2)',\n },\n /** The metered windows under one account row: no border, just the meters. */\n usageInline: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 },\n /** The auto provider's on/off row: a label and its switch. */\n autoSwitchRow: { display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' },\n modelRow: {\n display: 'flex', alignItems: 'flex-start', gap: 8,\n cursor: 'pointer', fontSize: 13, lineHeight: '20px',\n color: 'var(--dsw-alias-label-primary)',\n },\n /** A hidden model stays listed and editable, but reads as inactive. */\n modelRowHidden: {\n display: 'flex', alignItems: 'flex-start', gap: 8,\n cursor: 'pointer', fontSize: 13, lineHeight: '20px',\n color: 'var(--dsw-alias-label-dimmed)',\n },\n modelBody: { display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0 },\n modelName: { fontSize: 13, lineHeight: '20px' },\n modelMeta: { fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)', userSelect: 'all' },\n device: {\n marginTop: 4, display: 'flex', flexDirection: 'column', gap: 6,\n border: '0.5px solid var(--dsw-alias-border-l2)', borderRadius: 'var(--dsw-radius-lg, 16px)',\n padding: '10px 12px', background: 'var(--dsw-alias-bg-layer-1)',\n },\n deviceTitle: { fontWeight: 500, fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)' },\n deviceCode: {\n fontFamily: 'monospace', fontSize: 18, lineHeight: '24px', letterSpacing: 2,\n color: 'var(--dsw-alias-label-primary)', userSelect: 'all',\n },\n details: {\n display: 'flex', flexDirection: 'column', gap: 4,\n fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)',\n },\n detailRow: { display: 'flex', gap: 8, justifyContent: 'space-between', alignItems: 'baseline' },\n detailValue: { userSelect: 'all', overflowWrap: 'anywhere', textAlign: 'right' },\n /** Dialog layout only: its surface comes from `.dsh-subscription-manager`. */\n modal: {\n width: 440, maxWidth: '100%', maxHeight: '85vh', overflowY: 'auto',\n boxSizing: 'border-box', display: 'flex', flexDirection: 'column', gap: 10,\n padding: '16px 18px', margin: 'auto',\n },\n modalHeader: { display: 'flex', alignItems: 'center', gap: 8 },\n modalTitle: { fontWeight: 500, fontSize: 16, lineHeight: '24px', color: 'var(--dsw-alias-label-primary)' },\n modalActions: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 2 },\n divider: { borderTop: '0.5px solid var(--dsw-alias-border-l2)' },\n}\n\n/** Status dot style for one provider's connection state. */\nfunction dotStyle(status: ProviderStatus): CSSProperties {\n if (status.busy) return styles.dotBusy\n if (status.loggedIn) return styles.dotLive\n return {}\n}\n\n/** Localized label for one provider-owned detail row. */\nfunction detailLabel(t: SubscriptionsSectionInjected['t'], row: DetailRow): string {\n const key = `detail_${row.key}` as SubscriptionsKey\n return key in en ? t(key) : row.key\n}\n\n/** Localized label for one metered window the provider states. */\nfunction windowLabel(t: SubscriptionsSectionInjected['t'], window: UsageWindow): string {\n const key = `usage_${window.label}` as SubscriptionsKey\n return key in en ? t(key) : window.label\n}\n\n/** How much of one window is spent, as a display string. */\nfunction windowAmount(window: UsageWindow): string {\n if (window.usedPercent !== undefined) return `${String(Math.round(window.usedPercent))}%`\n if (window.used !== undefined && window.limit !== undefined) return `${String(window.used)}/${String(window.limit)}`\n return '—'\n}\n\n/** One account's metered windows, rendered under its account row. */\nfunction AccountUsage(props: {\n entry: ProviderUsage | undefined\n t: SubscriptionsSectionInjected['t']\n}) {\n const { entry, t } = props\n if (entry === undefined) return null\n return (\n <div style={styles.usageInline}>\n {entry.windows.map((window) => (\n <div key={`${entry.account}-${window.label}`} style={styles.usageRow}>\n <div style={styles.usageLine}>\n <span style={styles.modelName}>\n {windowLabel(t, window)}\n {' · '}\n {windowAmount(window)}\n </span>\n <span style={styles.modelMeta}>\n {window.resetsAt === undefined ? '' : formatReset(window.resetsAt, t)}\n </span>\n </div>\n {window.usedPercent !== undefined && (\n <div className=\"dsh-subscription-meter\">\n <span\n style={{ width: `${String(Math.min(100, Math.max(0, window.usedPercent)))}%` }}\n data-exhausted={window.usedPercent >= 90 ? 'true' : 'false'}\n />\n </div>\n )}\n </div>\n ))}\n {entry.windows.length === 0 && (\n <p style={styles.statusLine}>{t('usageNone')}</p>\n )}\n </div>\n )\n}\n\n/** The plan-and-usage block for the whole route. */\nfunction UsageBlock(props: {\n usage: readonly ProviderUsage[]\n error?: string\n t: SubscriptionsSectionInjected['t']\n busyAction: boolean\n onRefresh(): void\n}) {\n const { usage, error, t, busyAction, onRefresh } = props\n return (\n <div style={styles.usage}>\n <div style={styles.usageHeader}>\n <span style={styles.catalogTitle}>{t('usageTitle')}</span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n disabled={busyAction}\n onClick={onRefresh}\n >\n {busyAction ? t('refreshing') : t('usageRefresh')}\n </button>\n </div>\n {error !== undefined && <p className=\"dsh-subscription-error\">{t('usageError', { message: error })}</p>}\n {usage.length === 0 && error === undefined && <p style={styles.statusLine}>{t('usageNone')}</p>}\n {usage.map((entry) => (\n <div key={entry.account} style={styles.usageAccount}>\n <div style={styles.usageLine}>\n <span style={styles.modelMeta}>\n {entry.plan === undefined ? entry.account : t('usagePlan', { plan: entry.plan })}\n {entry.renewsAt === undefined ? '' : ` · ${t('usageRenews', { time: formatTime(entry.renewsAt) })}`}\n </span>\n <span style={styles.modelMeta}>{t('usageFetched', { time: formatTime(entry.fetchedAt) })}</span>\n </div>\n {entry.windows.map((window) => (\n <div key={`${entry.account}-${window.label}`} style={styles.usageRow}>\n <div style={styles.usageLine}>\n <span style={styles.modelName}>\n {windowLabel(t, window)}\n {' · '}\n {windowAmount(window)}\n </span>\n <span style={styles.modelMeta}>\n {window.resetsAt === undefined ? '' : formatReset(window.resetsAt, t)}\n </span>\n </div>\n {window.usedPercent !== undefined && (\n <div className=\"dsh-subscription-meter\">\n <span\n style={{ width: `${String(Math.min(100, Math.max(0, window.usedPercent)))}%` }}\n data-exhausted={window.usedPercent >= 90 ? 'true' : 'false'}\n />\n </div>\n )}\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n}\n\n/** The pending-login block: a device code, or a link to the sign-in page. */\nfunction PendingLogin(props: {\n prompt: LoginPrompt\n t: SubscriptionsSectionInjected['t']\n copied: boolean\n onCopy(code: string): void\n}) {\n const { prompt, t, copied, onCopy } = props\n return (\n <div style={styles.device}>\n <span style={styles.deviceTitle}>\n {prompt.mode === 'device-code' ? t('deviceTitle') : t('openLogin')}\n </span>\n <span style={styles.statusLine}>\n {prompt.mode === 'device-code' ? t('devicePrompt') : t('openLoginHint')}\n </span>\n {prompt.userCode !== undefined && <span style={styles.deviceCode}>{prompt.userCode}</span>}\n <span style={styles.statusLine}>{t('deviceExpires', { time: formatTime(prompt.expiresAt) })}</span>\n <div style={styles.actions}>\n <button\n type=\"button\"\n \n onClick={() => { window.open(prompt.url, '_blank', 'noopener') }}\n >\n {prompt.mode === 'device-code' ? t('deviceOpen') : t('openLogin')}\n </button>\n {prompt.userCode !== undefined && (\n <button type=\"button\" onClick={() => { onCopy(prompt.userCode as string) }}>\n {copied ? t('deviceCopied') : t('deviceCopy')}\n </button>\n )}\n </div>\n </div>\n )\n}\n\n/** One provider's card. */\nfunction ProviderCard(props: {\n status: ProviderStatus\n t: SubscriptionsSectionInjected['t']\n busyAction: boolean\n message?: CardMessage\n onLogin(): void\n onCancel(): void\n onLogout(account: string): void\n onSetDefault(account: string): void\n onRefreshToken(account: string): void\n onRefreshModels(): void\n onSetModelVisible(id: string, visible: boolean): void\n /** Persist the chain order the user dropped into place. */\n onSetModelOrder(ids: readonly string[]): void\n /** Publish a model id the catalog does not know yet. */\n onAddModel(id: string): void\n /** Forget a hand-added model id. */\n onRemoveModel(id: string): void\n onRefreshUsage(): void\n onReload(): void\n /** Whether the `auto` provider is offered; flips it. Only the auto card sets these. */\n autoOn?: boolean\n onSetAutoOn?(enabled: boolean): void\n}) {\n const { status, t, busyAction, message } = props\n const [copied, setCopied] = useState(false)\n // Both disclosures open folded, on every visit: the choice is a gesture, not a\n // stored setting. See the note beside the interval constants.\n const [modelsOpen, setModelsOpen] = useState(false)\n /** Whether this provider's whole card is unfolded. */\n const [cardOpen, setCardOpen] = useState(false)\n const [manageOpen, setManageOpen] = useState(false)\n const mounted = useRef(true)\n const dialog = useRef<HTMLDialogElement>(null)\n\n useEffect(() => {\n mounted.current = true\n return () => { mounted.current = false }\n }, [])\n\n // The dialog is a native modal, so the browser owns focus trapping and Esc.\n useEffect(() => {\n const element = dialog.current\n if (element === null || typeof element.showModal !== 'function') return\n if (manageOpen && !element.open) element.showModal()\n if (!manageOpen && element.open) element.close()\n }, [manageOpen])\n\n const copyCode = useCallback(async (code: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(code)\n if (mounted.current) {\n setCopied(true)\n setTimeout(() => { if (mounted.current) setCopied(false) }, 2000)\n }\n } catch {\n // A clipboard refusal is not worth an error banner: the code is on screen\n // and selectable, which is the fallback the user already has.\n }\n }, [])\n\n const toggleModelsOpen = useCallback((): void => {\n setModelsOpen((open) => !open)\n }, [])\n\n /** Fold or unfold this provider's card. */\n const toggleCardOpen = useCallback((): void => {\n setCardOpen((open) => !open)\n }, [])\n\n const accounts: readonly AccountStatus[] = status.accounts\n const models = status.models\n /** Whether this route signs in at all; the auto provider does not. */\n const connectable = status.connectable !== false\n const hiddenCount = models.filter((model) => !model.visible).length\n /** Whether the add-a-model-id field is open, and what has been typed. */\n const [adding, setAdding] = useState(false)\n const [draft, setDraft] = useState('')\n /** The row being dragged, so it can be dimmed while it moves. */\n const [dragFrom, setDragFrom] = useState<number | undefined>(undefined)\n /**\n * The dragged row, mirrored in a ref.\n *\n * The drop is handled by an ancestor whose handler was attached in an earlier\n * render, so it must not depend on state that was set during the drag.\n */\n const dragFromRef = useRef<number | undefined>(undefined)\n /** The row the pointer is currently over, so a drop lands where the user aims. */\n const dropTargetRef = useRef<number | undefined>(undefined)\n /** The same row in state, purely so it can be highlighted while dragging. */\n const [dropTarget, setDropTarget] = useState<number | undefined>(undefined)\n\n /**\n * Commit one dropped arrangement.\n *\n * The node half takes the full chain in its new order and appends whatever the\n * page left out, so sending just these ids is enough — and a model hidden in\n * another window is never dropped from the stored rank by a stale page.\n */\n const commitOrder = useCallback((ids: readonly string[]): void => {\n props.onSetModelOrder(ids)\n }, [props])\n\n /** Drag-and-drop plus its keyboard equivalent on the drag handle. */\n const drag = {\n start: (event: React.DragEvent, index: number): void => {\n setDragFrom(index)\n dragFromRef.current = index\n event.dataTransfer.effectAllowed = 'move'\n // Firefox refuses to start a drag without payload.\n event.dataTransfer.setData('text/plain', String(index))\n },\n over: (event: React.DragEvent, _index: number): void => {\n // A drop is only possible on a target that cancels the default.\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n const container = event.currentTarget as HTMLElement\n const to = indexAt(container, event.clientY)\n dropTargetRef.current = to\n if (to !== undefined && to !== dropTarget) setDropTarget(to)\n },\n drop: (event: React.DragEvent, index: number): void => {\n event.preventDefault()\n // The target comes from the last `dragover`, not from this event's own\n // coordinates: `drop` is not guaranteed to carry the pointer position, and a\n // zero there silently collapsed every drop onto the first row.\n const to = dropTargetRef.current ?? indexAt(event.currentTarget as HTMLElement, event.clientY) ?? index\n move(dragFromRef.current, to)\n dragFromRef.current = undefined\n dropTargetRef.current = undefined\n setDropTarget(undefined)\n setDragFrom(undefined)\n },\n /** A drop anywhere in the list lands on the row under the pointer. */\n dropAt: (event: React.DragEvent): void => {\n event.preventDefault()\n const container = event.currentTarget as HTMLElement\n const to = dropTargetRef.current ?? indexAt(container, event.clientY)\n if (to !== undefined) move(dragFromRef.current, to)\n dragFromRef.current = undefined\n dropTargetRef.current = undefined\n setDropTarget(undefined)\n setDragFrom(undefined)\n },\n end: (): void => {\n dragFromRef.current = undefined\n dropTargetRef.current = undefined\n setDropTarget(undefined)\n setDragFrom(undefined)\n },\n key: (event: React.KeyboardEvent, index: number): void => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(index, index + (event.key === 'ArrowUp' ? -1 : 1))\n },\n }\n\n /** Move one model row to another position and persist the result. */\n function move(from: number | undefined, to: number): void {\n if (from === undefined || from === to) return\n if (from < 0 || to < 0 || from >= models.length || to >= models.length) return\n const ids = models.map((model) => model.id)\n const [moved] = ids.splice(from, 1)\n if (moved === undefined) return\n ids.splice(to, 0, moved)\n commitOrder(ids)\n }\n\n /**\n * The row index a drop at this pointer position lands on.\n *\n * The drop is handled on the whole list rather than per row: rows are tall and\n * separated by gaps, so a drop between two of them hits no target at all and\n * silently does nothing. Comparing the pointer against each row's midpoint also\n * makes the gesture match what the user sees.\n */\n function indexAt(container: HTMLElement, clientY: number): number | undefined {\n const rows = Array.from(container.querySelectorAll<HTMLElement>('[data-model-index]'))\n if (rows.length === 0) return undefined\n let closest = 0\n let best = Number.POSITIVE_INFINITY\n for (const row of rows) {\n const box = row.getBoundingClientRect()\n const distance = Math.abs(clientY - (box.top + box.height / 2))\n if (distance >= best) continue\n best = distance\n closest = Number(row.dataset.modelIndex)\n }\n return closest\n }\n const busy = status.busy\n const liveCatalog = (status.discovered?.length ?? 0) > 0\n /**\n * What a folded card says about itself.\n *\n * The connection is the reason to open a card, so the folded row carries it;\n * `auto` has no account and counts the models it will actually try instead.\n */\n const summaryLine = !connectable\n ? t('statusAuto', { count: models.filter((model) => model.visible).length })\n : busy\n ? t('statusBusy')\n : status.loggedIn\n ? t('statusConnected', { count: accounts.length })\n : t('statusSignedOut')\n\n return (\n <div style={cardOpen ? { ...styles.card, ...styles.cardOpen } : styles.card}>\n {/*\n The header is the disclosure: folded, the page is a list of providers, and\n the whole card is one row with its state and an edit affordance. The\n status dot stays visible either way, so a folded card still reports\n whether the route is signed in.\n */}\n <div\n style={styles.cardHeader}\n role=\"button\"\n tabIndex={0}\n aria-expanded={cardOpen}\n onClick={toggleCardOpen}\n onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggleCardOpen() } }}\n >\n <span style={{ ...styles.dot, ...dotStyle(status) }} aria-hidden=\"true\" />\n <span style={styles.name}>{status.displayName}</span>\n {status.loggedIn && <span style={styles.badgeDefault}>{t('badgeLive')}</span>}\n {/*\n The state stays in the header whether the card is open or folded. The\n body used to repeat the same sentence as its first line, so expanding a\n card made that text appear to jump down a line — the one thing the reader\n is watching.\n */}\n <span style={styles.cardSummary}>{summaryLine}</span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n aria-expanded={cardOpen}\n aria-controls={`dsh-subscription-card-${status.provider}`}\n onClick={(event) => { event.stopPropagation(); toggleCardOpen() }}\n >\n {cardOpen ? t('collapse') : t('edit')}\n </button>\n </div>\n <div\n id={`dsh-subscription-card-${status.provider}`}\n style={cardOpen ? styles.cardBody : { ...styles.cardBody, display: 'none' }}\n >\n {connectable && !status.loggedIn && !busy && <p style={styles.statusLine}>{t('statusSignIn')}</p>}\n {status.lastError !== undefined && (\n <p className=\"dsh-subscription-error\">{t('lastError', { message: status.lastError })}</p>\n )}\n {message?.tone === 'error' && <p className=\"dsh-subscription-error\">{message.text}</p>}\n {message?.tone === 'success' && <p className=\"dsh-subscription-success\">{message.text}</p>}\n\n {/* The auto provider's master switch: off keeps it out of the picker entirely. */}\n {status.provider === 'auto' && props.onSetAutoOn !== undefined && (\n <label style={styles.autoSwitchRow}>\n <span style={styles.modelName}>{t('autoSwitchLabel')}</span>\n <input\n type=\"checkbox\"\n className=\"dsh-subscription-switch\"\n checked={props.autoOn ?? true}\n disabled={busyAction}\n onChange={(event) => { props.onSetAutoOn?.(event.target.checked) }}\n />\n </label>\n )}\n\n {accounts.map((account) => {\n const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account)\n return (\n <div key={account.account} style={styles.accountRow}>\n <div style={styles.accountHeader}>\n <button\n type=\"button\"\n style={styles.starButton}\n title={account.isDefault ? t('defaultBadge') : t('makeDefault')}\n aria-label={account.isDefault ? t('defaultBadge') : t('makeDefault')}\n disabled={busyAction || account.isDefault}\n onClick={() => { props.onSetDefault(account.account) }}\n >\n {account.isDefault ? '★' : '☆'}\n </button>\n <span style={styles.accountName}>{account.label}</span>\n {account.isDefault && <span style={styles.badgeDefault}>{t('defaultBadge')}</span>}\n {/* The provider refused this grant: only a new sign-in helps. */}\n {account.needsLogin === true && (\n <span style={styles.badgeWarn} title={t('accountNeedsLoginHint')}>{t('accountNeedsLogin')}</span>\n )}\n {usageEntry?.plan !== undefined && (\n <span style={styles.badge}>{t('usagePlan', { plan: usageEntry.plan })}</span>\n )}\n <span style={styles.statusLine}>\n {account.expired\n ? t('accountExpired')\n : t('accountExpires', { time: formatTime(account.expiresAt) })}\n </span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n disabled={busyAction}\n onClick={() => { props.onLogout(account.account) }}\n >\n {t('logout')}\n </button>\n </div>\n <AccountUsage entry={usageEntry} t={t} />\n </div>\n )\n })}\n\n {/* A signed-out route has nothing to configure here, and listing models it\n cannot call would only invite picker entries that fail. */}\n {status.loggedIn && (\n <div style={styles.catalog}>\n <div style={styles.catalogHeader}>\n <button\n type=\"button\"\n className=\"dsh-subscription-disclosure\"\n style={styles.disclosure}\n aria-expanded={modelsOpen}\n aria-controls={`dsh-subscription-models-${status.provider}`}\n onClick={toggleModelsOpen}\n >\n <span style={styles.chevron} aria-hidden=\"true\">{modelsOpen ? '▾' : '▸'}</span>\n <span style={styles.catalogTitle}>{t('modelsTitle')}</span>\n </button>\n <span style={styles.catalogMeta}>\n {t('modelsCount', { count: models.length })}\n {' · '}\n {liveCatalog ? t('modelsLive') : t('modelsBuiltIn')}\n {hiddenCount === 0 ? '' : ` ${t('modelsHiddenCount', { count: hiddenCount })}`}\n </span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n disabled={busyAction}\n onClick={props.onRefreshModels}\n >\n {busyAction ? t('refreshing') : t('refresh')}\n </button>\n {/* A model the endpoint serves but this build has never heard of: the\n id is published as a plain route, so a new release is usable at once. */}\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n disabled={busyAction || !connectable}\n title={t('modelsAddHint')}\n onClick={() => { setAdding((open) => !open) }}\n >\n {t('modelsAdd')}\n </button>\n </div>\n {adding && (\n <form\n style={styles.addRow}\n onSubmit={(event) => {\n event.preventDefault()\n const id = draft.trim()\n if (id.length === 0) return\n props.onAddModel(id)\n setDraft('')\n setAdding(false)\n }}\n >\n <input\n type=\"text\"\n className=\"dsh-subscription-input\"\n style={styles.addInput}\n value={draft}\n autoFocus\n spellCheck={false}\n placeholder={t('modelsAddPlaceholder')}\n aria-label={t('modelsAdd')}\n disabled={busyAction}\n onChange={(event) => { setDraft(event.target.value) }}\n />\n <button type=\"submit\" disabled={busyAction || draft.trim().length === 0}>{t('modelsAddConfirm')}</button>\n <button type=\"button\" className=\"dsh-subscription-quiet\" onClick={() => { setAdding(false); setDraft('') }}>\n {t('cancel')}\n </button>\n </form>\n )}\n {modelsOpen && (\n <div\n id={`dsh-subscription-models-${status.provider}`}\n style={styles.modelList}\n // The whole list is the drop target, so a release between two rows still\n // lands somewhere instead of being cancelled unnoticed.\n onDragOver={(event) => { drag.over(event, 0) }}\n onDrop={drag.dropAt}\n >\n {status.provider === 'auto' && <span style={styles.chainTitle}>{t('modelsAutoChain')}</span>}\n {/* Every provider lists its models the same way: a switch in front,\n the model's own metadata, and a grip to drag it in the order both\n the picker list and Auto's fallback follow. */}\n {models.map((model, index) => (\n <div\n key={model.id}\n data-model-index={index}\n draggable={!busyAction}\n style={{\n ...styles.modelRowWrap,\n ...dragFrom === index ? { opacity: 0.5 } : {},\n // The row a drop would land on, so the gesture is visible while it\n // happens rather than only in its result.\n ...dropTarget === index && dragFrom !== undefined && dragFrom !== index\n ? { boxShadow: 'inset 0 2px 0 0 var(--dsw-alias-brand-primary)' }\n : {},\n }}\n onDragStart={(event) => { drag.start(event, index) }}\n onDragEnd={drag.end}\n >\n {/* The grip is the affordance and the keyboard path; the drag\n itself starts on the row, because a <button> does not start an\n HTML5 drag reliably in Chromium. */}\n <button\n type=\"button\"\n className=\"dsh-subscription-handle\"\n disabled={busyAction}\n title={t('modelsDrag')}\n aria-label={`${t('modelsDrag')} · ${model.name}`}\n onKeyDown={(event) => { drag.key(event, index) }}\n >\n ⠿\n </button>\n <label\n style={model.visible\n ? { ...styles.modelRowGrow, cursor: 'pointer' }\n : { ...styles.modelRowGrow, cursor: 'pointer', color: 'var(--dsw-alias-label-dimmed)' }}\n title={model.visible ? t('modelsHidden') : t('modelsShow')}\n >\n <input\n type=\"checkbox\"\n className=\"dsh-subscription-switch\"\n role=\"switch\"\n checked={model.visible}\n disabled={busyAction}\n aria-label={`${t('modelsShow')} · ${model.name}`}\n onChange={(event) => { props.onSetModelVisible(model.id, event.target.checked) }}\n />\n <span style={styles.modelBody}>\n <span style={styles.modelName}>\n {model.name}\n {model.source === undefined ? null : <span style={styles.badge}>{model.source}</span>}\n </span>\n <span style={styles.modelMeta}>\n {model.id}\n {' · '}\n {t('modelsContext', { tokens: formatTokens(model.contextWindow) })}\n {model.defaultEffort === undefined ? '' : ` · ${model.defaultEffort}`}\n {model.inputModalities === undefined || model.inputModalities.length === 0\n ? ''\n : ` · ${model.inputModalities.join('/')}`}\n </span>\n </span>\n </label>\n {/* A hand-added id can be removed again, so a typo does not linger. */}\n {model.userAdded === true && (\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.removeButton}\n disabled={busyAction}\n title={t('modelsRemove')}\n aria-label={`${t('modelsRemove')} · ${model.name}`}\n onClick={() => { props.onRemoveModel(model.id) }}\n >\n ×\n </button>\n )}\n </div>\n ))}\n {models.length === 0 && (\n <p style={styles.statusLine}>\n {status.provider === 'auto' ? t('modelsAutoChainEmpty') : t('modelsEmpty')}\n </p>\n )}\n </div>\n )}\n </div>\n )}\n\n {/* `auto` owns no account, so it offers no connect, cancel or manage. */}\n {connectable && (\n <div style={styles.actions}>\n {!busy && (\n <button type=\"button\" disabled={busyAction} onClick={props.onLogin}>\n {accounts.length === 0 ? t('connect') : t('addAccount')}\n </button>\n )}\n {!busy && (\n <button type=\"button\" disabled={busyAction} onClick={() => { setManageOpen(true) }}>\n {t('manage')}\n </button>\n )}\n {busy && (\n <button type=\"button\" disabled={busyAction} onClick={props.onCancel}>\n {t('cancel')}\n </button>\n )}\n </div>\n )}\n {connectable && accounts.length > 0 && <p style={styles.statusLine}>{t('hint')}</p>}\n\n {busy && status.pending !== undefined && (\n <PendingLogin prompt={status.pending} t={t} copied={copied} onCopy={(code) => { void copyCode(code) }} />\n )}\n </div>\n {/* The dialog stays outside the collapsible body: a hidden ancestor would\n keep it out of the top layer even after `showModal()`. */}\n <dialog\n ref={dialog}\n className=\"dsh-subscription-manager\"\n aria-labelledby={`dsh-subscription-manage-${status.provider}`}\n // A closed `<dialog>` is hidden by the user-agent stylesheet, and an\n // inline `display` would override that rule — so the open state has to\n // be restated here rather than left to `showModal()` alone.\n style={manageOpen ? styles.modal : { ...styles.modal, display: 'none' }}\n onClose={() => { setManageOpen(false) }}\n onCancel={() => { setManageOpen(false) }}\n >\n <div style={styles.modalHeader}>\n <span style={styles.modalTitle} id={`dsh-subscription-manage-${status.provider}`}>\n {t('manageTitle', { provider: status.displayName })}\n </span>\n </div>\n\n <span style={styles.statusLine}>{t('manageAccounts')}</span>\n {accounts.map((account) => (\n <div key={account.account} style={styles.accountRow}>\n <div style={styles.accountHeader}>\n <span style={styles.accountName}>{account.label}</span>\n {account.isDefault && <span style={styles.badge}>{t('defaultBadge')}</span>}\n </div>\n <div style={styles.actions}>\n <button\n type=\"button\"\n \n disabled={busyAction}\n onClick={() => { props.onRefreshToken(account.account) }}\n >\n {t('refreshToken')}\n </button>\n {!account.isDefault && (\n <button\n type=\"button\"\n \n disabled={busyAction}\n onClick={() => { props.onSetDefault(account.account) }}\n >\n {t('makeDefault')}\n </button>\n )}\n <button\n type=\"button\"\n \n disabled={busyAction}\n onClick={() => { props.onLogout(account.account) }}\n >\n {t('logout')}\n </button>\n </div>\n </div>\n ))}\n {accounts.length === 0 && <p style={styles.statusLine}>{t('statusSignedOut')}</p>}\n\n <div style={styles.divider} />\n <div style={styles.actions}>\n <button type=\"button\" disabled={busyAction} onClick={props.onReload}>\n {t('reload')}\n </button>\n </div>\n <p style={styles.statusLine}>{t('reloadHint')}</p>\n\n <div style={styles.divider} />\n <div style={styles.details}>\n {status.details.map((row) => (\n <div key={row.key} style={styles.detailRow}>\n <span>{detailLabel(t, row)}</span>\n <span style={styles.detailValue}>{row.value}</span>\n </div>\n ))}\n {status.discovered !== undefined && status.discovered.length > 0 && (\n <div style={styles.detailRow}>\n <span>{t('modelsLive')}</span>\n <span style={styles.detailValue}>{status.discovered.join(', ')}</span>\n </div>\n )}\n </div>\n\n <div className=\"dsh-subscription-dialog-actions\" style={styles.modalActions}>\n <button type=\"button\" onClick={() => { setManageOpen(false) }}>{t('close')}</button>\n </div>\n </dialog>\n </div>\n )\n}\n\n/**\n * The Subscriptions settings section.\n * @param props - section props (inject face plus the shell's `close`).\n * @returns the section content.\n */\nexport function SubscriptionsSection(props: SubscriptionsSectionProps) {\n const { call } = props\n const t = props.t ?? fallbackTranslate\n const [status, setStatus] = useState<SubscriptionStatus | undefined>(undefined)\n const [error, setError] = useState<string | undefined>(undefined)\n const [messages, setMessages] = useState<Record<string, CardMessage>>({})\n const [busyAction, setBusyAction] = useState(false)\n const [autoOn, setAutoOn] = useState(true)\n const mounted = useRef(true)\n const timer = useRef<ReturnType<typeof setInterval> | undefined>(undefined)\n\n /** Read whether the `auto` provider is offered, so its card can render the switch. */\n useEffect(() => {\n if (call === undefined) return\n let live = true\n void call('auto-state', undefined)\n .then((result) => { if (live) setAutoOn(result.enabled) })\n .catch(() => { /* the switch defaults to on */ })\n return () => { live = false }\n }, [call])\n\n /** Fetch the current state; a failure is shown rather than swallowed. */\n const refresh = useCallback(async (): Promise<void> => {\n if (call === undefined) return\n try {\n const next = await call('status', undefined)\n if (!mounted.current) return\n setStatus(next)\n setError(undefined)\n } catch (caught: unknown) {\n if (mounted.current) setError(t('actionFailed', { message: messageOf(caught) }))\n }\n }, [call, t])\n\n /** Flip the `auto` provider on or off. */\n const setAuto = useCallback(async (enabled: boolean): Promise<void> => {\n if (call === undefined) return\n setBusyAction(true)\n try {\n const result = await call('setAutoOn', { enabled })\n if (!mounted.current) return\n setAutoOn(result.enabled)\n await refresh()\n } catch (caught: unknown) {\n if (mounted.current) setError(t('actionFailed', { message: messageOf(caught) }))\n } finally {\n if (mounted.current) setBusyAction(false)\n }\n }, [call, refresh, t])\n\n const stopPolling = useCallback((): void => {\n if (timer.current !== undefined) {\n clearInterval(timer.current)\n timer.current = undefined\n }\n }, [])\n\n const startPolling = useCallback((): void => {\n if (timer.current !== undefined) return\n // The node half clears `busy` once the attempt settles; the effect below\n // observes that and stops this interval.\n timer.current = setInterval(() => { void refresh() }, POLL_INTERVAL_MS)\n }, [refresh])\n\n useEffect(() => {\n mounted.current = true\n void refresh()\n return () => {\n mounted.current = false\n stopPolling()\n }\n }, [refresh, stopPolling])\n\n /** Every route worth reading, as of the latest render, for the usage poll. */\n const signedInRef = useRef<readonly string[]>([])\n useEffect(() => {\n signedInRef.current = usageTargets(status?.providers ?? [])\n })\n\n /**\n * Read each named route's plan and allowance, applying every answer.\n *\n * A read is not forced: the node half reuses a figure only for a few seconds and\n * never reuses a substitute, so a steady interval refreshes the figures while\n * collapsing bursts into one request per route.\n */\n const readUsage = useCallback(async (providers: readonly string[]): Promise<void> => {\n if (call === undefined) return\n for (const provider of providers) {\n try {\n const result = await call('usage', { provider })\n if (!mounted.current) return\n setStatus(result.status)\n } catch {\n // An unreadable quota is already surfaced inside the card by the status it\n // carries; the page itself stays usable.\n }\n }\n }, [call])\n\n // The figures are read as soon as the first status lands...\n const usageRequested = useRef(false)\n useEffect(() => {\n if (call === undefined || status === undefined || usageRequested.current) return\n const signedIn = usageTargets(status.providers)\n if (signedIn.length === 0) return\n usageRequested.current = true\n void readUsage(signedIn)\n }, [call, status, readUsage])\n\n // ...and then kept current while the page is open. Allowances move as the account\n // is used, so a figure that is only read on mount reports the state of a page that\n // may have been open for hours.\n useEffect(() => {\n if (call === undefined) return\n const tick = (): void => {\n // Nothing is refreshed for a page nobody is looking at.\n if (globalThis.document?.visibilityState === 'hidden') return\n void readUsage(signedInRef.current)\n }\n const interval = setInterval(tick, USAGE_POLL_INTERVAL_MS)\n return () => { clearInterval(interval) }\n }, [call, readUsage])\n\n // Poll only while some provider is waiting for browser approval.\n const anyBusy = status?.providers.some((provider) => provider.busy) === true\n useEffect(() => {\n if (anyBusy) startPolling()\n else stopPolling()\n }, [anyBusy, startPolling, stopPolling])\n\n /**\n * Signed-in providers read first: a route with a live account is the one a\n * reader most likely wants, and `auto` (which owns no account) leads the rest.\n * The sort is stable, so equal ranks keep the host's configured order.\n */\n const orderedProviders = useMemo(() => {\n const providers = status?.providers ?? []\n const rank = (provider: ProviderStatus): number => {\n if (provider.provider === 'auto') return 0\n if (provider.loggedIn) return 1\n return 2\n }\n return [...providers].sort((left, right) => rank(left) - rank(right))\n }, [status?.providers])\n\n /** Run one action for one provider, reporting its own failure. */\n const act = useCallback(async (provider: string, run: () => Promise<void>): Promise<void> => {\n setBusyAction(true)\n setMessages((current) => {\n const next = { ...current }\n delete next[provider]\n return next\n })\n try {\n await run()\n } catch (caught: unknown) {\n if (mounted.current) {\n setMessages((current) => ({ ...current, [provider]: { tone: 'error', text: t('actionFailed', { message: messageOf(caught) }) } }))\n }\n } finally {\n if (mounted.current) setBusyAction(false)\n }\n }, [t])\n\n const login = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const started = await call('login', { provider })\n if (!mounted.current) return\n setStatus(started.status)\n // A loopback flow wants the browser opened; a blocked popup still leaves\n // the link in the card, which is the fallback the user already has.\n if (started.prompt.mode === 'open-url') {\n window.open(started.prompt.url, '_blank', 'noopener')\n }\n })\n }, [act, call])\n\n const cancel = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('cancel', { provider })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const logout = useCallback((provider: string, account: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('logout', { provider, account })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const setDefault = useCallback((provider: string, account: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('setDefault', { provider, account })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const refreshToken = useCallback((provider: string, account: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const result = await call('refresh', { provider, account })\n if (!mounted.current) return\n setStatus(result.status)\n setMessages((current) => ({\n ...current,\n [provider]: { tone: 'success', text: t('tokenRefreshed', { time: formatTime(result.expiresAt) }) },\n }))\n })\n }, [act, call, t])\n\n const refreshModels = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('discover', { provider, force: true })\n if (!mounted.current) return\n setStatus(next)\n setMessages((current) => ({ ...current, [provider]: { tone: 'success', text: t('modelsRefreshed') } }))\n })\n }, [act, call, t])\n\n const setModelVisible = useCallback((provider: string, id: string, visible: boolean): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('setModelVisible', { provider, id, visible })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const refreshUsage = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const result = await call('usage', { provider, force: true })\n if (mounted.current) setStatus(result.status)\n })\n }, [act, call])\n\n const setModelOrder = useCallback((provider: string, ids: readonly string[]): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('setModelOrder', { provider, ids: [...ids] })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const addModel = useCallback((provider: string, id: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('addModel', { provider, id })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const removeModel = useCallback((provider: string, id: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('removeModel', { provider, id })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const reload = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('reload', undefined)\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n if (call === undefined) return <p style={styles.intro}>{t('unavailable')}</p>\n\n return (\n <div className=\"dsh-subscriptions-settings\" style={styles.section}>\n <style>{subscriptionsCss}</style>\n <div style={styles.sectionHeader}>\n <h2 style={styles.heading}>{t('nav')}</h2>\n <p style={styles.intro}>{t('intro')}</p>\n </div>\n {error !== undefined && <p className=\"dsh-subscription-error\">{error}</p>}\n {status === undefined && <p style={styles.statusLine}>{t('loading')}</p>}\n {orderedProviders.map((provider) => (\n <ProviderCard\n key={provider.provider}\n status={provider}\n t={t}\n busyAction={busyAction}\n {...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] as CardMessage }}\n onLogin={() => { login(provider.provider) }}\n onCancel={() => { cancel(provider.provider) }}\n onLogout={(account) => { logout(provider.provider, account) }}\n onSetDefault={(account) => { setDefault(provider.provider, account) }}\n onRefreshToken={(account) => { refreshToken(provider.provider, account) }}\n onRefreshModels={() => { refreshModels(provider.provider) }}\n onSetModelVisible={(id, visible) => { setModelVisible(provider.provider, id, visible) }}\n onSetModelOrder={(ids) => { setModelOrder(provider.provider, ids) }}\n onAddModel={(id) => { addModel(provider.provider, id) }}\n onRemoveModel={(id) => { removeModel(provider.provider, id) }}\n onRefreshUsage={() => { refreshUsage(provider.provider) }}\n onReload={() => { reload(provider.provider) }}\n {...provider.provider !== 'auto' ? {} : { autoOn, onSetAutoOn: (enabled: boolean) => { void setAuto(enabled) } }}\n />\n ))}\n </div>\n )\n}\n","/**\n * The usage chip that rides the composer's bottom bar.\n *\n * The bar itself is a slot the shell owns. `conversation.composer.dock` is a\n * session-scoped `list`, and the shell's own stats pills (`tok/s`, cache hit) and its\n * context meter register into it; this chip joins as the last entry, so it reads to the\n * right of the context meter. It is deliberately compact — an abbreviated route name\n * and the percentage — because the bar is a status line, not a dashboard; the full\n * figure, its reset and the meter appear when the chip is clicked.\n *\n * Two silences are deliberate: a provider this plugin does not own shows nothing rather\n * than a fabricated zero, and switching to a foreign provider keeps the last own reading\n * on screen instead of blanking the bar — the shell's own pills already speak for the\n * foreign one.\n *\n * @module dsh-plugin-subscription-oauth/client/UsageChip\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { SessionStandardProps } from '@deepseek-ai/dsh-client-ui-slots'\n\nimport type { SubscriptionsCall, AccountBalanceCall, CcSwitchBalanceCall } from './subscriptions-rpc.js'\nimport { usageChipCss } from './styles.js'\nimport type { ProviderUsage, UsageWindow } from '../protocol.js'\n\n/** How often a visible chip refreshes its figures. */\nconst REFRESH_INTERVAL_MS = 60_000\n\n/**\n * Abbreviations for the routes this plugin serves.\n *\n * The bar is a status line under the composer, where a full product name crowds out the\n * shell's own pills; the full name is one click away. The fallback shortens an unknown\n * route's display name rather than dropping it.\n */\nconst SHORT_LABELS: Record<string, string> = {\n kimi: 'Kimi',\n grok: 'Grok',\n antigravity: 'Anti',\n glm: 'GLM',\n ark: 'Ark',\n}\n\n/**\n * Abbreviations for the window names the providers use.\n *\n * The bar has room for a token or two, so `weekly` becomes `7d` — the same figure in a\n * form that reads at a glance. An unrecognised window keeps its own name, shortened\n * only if it is long.\n */\nconst SHORT_WINDOWS: Record<string, string> = {\n 'weekly': '7d',\n '7d': '7d',\n '5h': '5h',\n 'monthly': 'mo',\n 'daily': '1d',\n 'credits': 'cr',\n 'free': 'free',\n 'session': 's',\n}\n\n/** Injected props the chip is registered with. */\nexport interface UsageChipInjected {\n /** The plugin's own RPC caller. */\n call: SubscriptionsCall\n /** Copy for the active locale. */\n t: (key: 'usageChipTitle' | 'usageChipFull', values?: Record<string, string | number>) => string\n /** Whether the provider is one this plugin serves. */\n isOwnProvider: (provider: string) => boolean\n /**\n * The DeepSeek account's balance, when the session uses it.\n *\n * The host owns that account's grant and the platform endpoint, so the chip asks the\n * host rather than re-deriving either. It resolves to `undefined` when the account is\n * signed out or the read fails, in which case the chip stays silent.\n */\n accountBalance: AccountBalanceCall\n /**\n * A non-subscription provider's balance, by cc-switch's query.\n *\n * A provider that is not a subscription this plugin serves has no usage windows to\n * meter, so the chip shows its balance instead. The caller names the provider; the\n * server maps it to the credential reference the provider's profile names (`apiKeyEnv`),\n * resolves that to the API key, and asks cc-switch's balance endpoint. It resolves to\n * `undefined` when the provider has no balance source or did not answer.\n */\n ccSwitchBalance: CcSwitchBalanceCall\n}\n\n/** One model choice, as the host records it. */\nexport interface ModelSelection {\n /** Provider route key. */\n readonly provider: string\n /** Model id. */\n readonly model: string\n /** Reasoning effort, when one was chosen. */\n readonly reasoningEffort?: string\n}\n\n/**\n * The session's model-selection projection, as the host wires it.\n *\n * `next` is a selection the user has made but not yet used; `lastUsed` is the one a\n * request actually ran with. Reading `next` first is what makes the chip follow the\n * picker immediately rather than only after the next message.\n */\nexport interface ModelSelectionProjection {\n /** The last selection a request ran with. */\n readonly lastUsed: ModelSelection | null\n /** The selection in force now. */\n readonly next: ModelSelection | null\n}\n\n/** The session's provider, from its model-selection projection. */\nexport function providerOf(projection: ModelSelectionProjection | null | undefined): string | undefined {\n return (projection?.next ?? projection?.lastUsed)?.provider\n}\n\n/** What one route's allowance reduces to for a single line. */\nexport interface ChipReading {\n /** Route the figures belong to. */\n provider: string\n /** Name to show, as the route states it. */\n label: string\n /** Every window that states a figure, in the order the route stated them. */\n windows: readonly UsageWindow[]\n /** The window worth showing in the compact line: the one closest to its limit. */\n window: UsageWindow\n /** Plan name, when the provider stated one. */\n plan?: string\n}\n\n/** One currency's balance, as the host's account controller reports it. */\nexport interface ChipBalance {\n /** The wallet's currency. */\n currency: string\n /** Remaining balance, as a decimal string. */\n balance: string\n /** Whether this is a bonus / promotional wallet rather than topped-up credit. */\n bonus: boolean\n}\n\n/**\n * Reduce the node half's balance payload to the wallets the chip shows.\n *\n * Bonus wallets are shown after the topped-up ones, so a reader sees their own credit\n * first and any promotional credit as a secondary figure. A bonus wallet that holds\n * nothing is dropped rather than shown as `+¥0`: the platform reports one for every\n * currency whether or not there is a bonus, and an empty one is noise, not information.\n *\n * @param value - the node half's balance, or undefined when there is none.\n * @returns the wallets, topped-up first then bonus, or undefined when nothing is usable.\n */\nexport function balanceOf(value: { wallets?: readonly { currency: string, balance: string }[], bonusWallets?: readonly { currency: string, balance: string }[] } | null | undefined): readonly ChipBalance[] | undefined {\n if (value === null || value === undefined) return undefined\n const wallets: ChipBalance[] = [\n ...(value.wallets ?? []).map((wallet) => ({ currency: wallet.currency, balance: wallet.balance, bonus: false })),\n // Only a bonus wallet that actually holds something is worth a line.\n ...(value.bonusWallets ?? [])\n .filter((wallet) => Number.parseFloat(wallet.balance) > 0)\n .map((wallet) => ({ currency: wallet.currency, balance: wallet.balance, bonus: true })),\n ]\n return wallets.length === 0 ? undefined : wallets\n}\n\n/**\n * Format one wallet for the chip's compact line.\n *\n * A balance is money, not a percentage, so it renders as `¥6.89` rather than a meter;\n * the currency prefix keeps two currencies from being mistaken for one sum. The amount\n * is rounded to two decimal places (a balance reads to the cent, and a figure like\n * `1.1` for `1.10` would mislead) and always carries both places.\n *\n * @param wallet - the wallet to show.\n * @returns the short form, e.g. `¥6.89` or `+$2.00` for a bonus wallet.\n */\nexport function formatBalance(wallet: ChipBalance): string {\n const amount = Number.parseFloat(wallet.balance)\n const trimmed = Number.isFinite(amount) ? amount.toFixed(2) : wallet.balance\n const symbol = wallet.currency === 'CNY' ? '¥' : wallet.currency === 'USD' ? '$' : `${wallet.currency} `\n return `${wallet.bonus ? '+' : ''}${symbol}${trimmed}`\n}\n\n/**\n * The short form of a route's name.\n *\n * @param provider - the route id.\n * @param label - the route's own display name.\n * @returns an abbreviation that still identifies the route.\n */\nexport function shortLabel(provider: string, label: string): string {\n const known = SHORT_LABELS[provider]\n if (known !== undefined) return known\n // An unknown route keeps its first word, so the chip names something recognisable\n // rather than a generic placeholder.\n const [first] = label.trim().split(/\\s+/)\n return first === undefined || first === '' ? provider : first\n}\n\n/**\n * The short form of a window's name.\n *\n * @param label - the window's own name, e.g. `weekly`.\n * @returns an abbreviation, or the label itself when it is already short.\n */\nexport function shortWindow(label: string): string {\n const known = SHORT_WINDOWS[label.toLowerCase()]\n if (known !== undefined) return known\n return label.length <= 4 ? label : label.slice(0, 4)\n}\n\n/**\n * Pick the window a single line should show.\n *\n * A plan can meter several windows at once (a rolling five-hour one and a weekly one).\n * The most constrained is the one that will stop work soonest, so it earns the space;\n * ties fall to the earlier window, which is the order the providers state them in.\n *\n * @param windows - the route's metered windows.\n * @returns the window to show, or undefined when none states a figure.\n */\nexport function pickWindow(windows: readonly UsageWindow[]): UsageWindow | undefined {\n let best: UsageWindow | undefined\n for (const window of windows) {\n if (window.usedPercent === undefined) continue\n if (best?.usedPercent === undefined || window.usedPercent > best.usedPercent) best = window\n }\n return best\n}\n\n/**\n * Reduce one route's usage to what the chip renders.\n *\n * @param provider - the route the figures came from.\n * @param label - the route's display name.\n * @param usage - the accounts the route answered for.\n * @returns the reading, or undefined when the route stated no figure.\n */\n/** What one account's allowance reduces to for the chip. */\nexport interface AccountReading {\n /** Which account these figures belong to. */\n account: string\n /** Plan name, when the provider stated one. */\n plan?: string\n /** Every metered window, in the order the route stated them. */\n windows: readonly UsageWindow[]\n}\n\n/**\n * Reduce a route's per-account usage to one reading per account.\n *\n * A provider can hold several accounts (two Antigravity sign-ins, say), and the chip\n * shows each one's allowance on its own line, the way the settings cards do — a merged\n * figure would hide that one account is exhausted while the other is free.\n *\n * @param usage - every account the route answered for.\n * @returns one reading per account that states a figure, in the route's order.\n */\n/**\n * Reduce a route's per-account usage to one reading per account.\n *\n * A provider can hold several accounts (two Antigravity sign-ins, say). Every account's\n * windows are kept as the route stated them — a percentage-metered quota and a credit\n * balance (`credits`) alike — so the per-account panel shows the real figures, and the\n * compact line's merge decides which to integrate.\n *\n * @param usage - every account the route answered for.\n * @returns one reading per account that states any window, in the route's order.\n */\nexport function accountReadingsOf(usage: readonly ProviderUsage[]): readonly AccountReading[] {\n const out: AccountReading[] = []\n for (const entry of usage) {\n if (entry.windows.length === 0) continue\n out.push({ account: entry.account, windows: entry.windows, ...entry.plan === undefined ? {} : { plan: entry.plan } })\n }\n return out\n}\n\n/**\n * Merge every account's percentage-metered windows into one figure per label.\n *\n * The chip's compact line integrates the whole pool: two accounts at 50 percent read as\n * one window at 50 percent (the same allowance spent twice as wide), not two lines. A\n * credit balance (`credits`) is money, not a share of a shared allowance, so it is not\n * merged — it stays on the per-account panel, where it reads correctly. Only windows\n * that state a `usedPercent` (a share of an allowance) merge.\n *\n * @param readings - one per account, from {@link accountReadingsOf}.\n * @returns one merged window per percentage-metered label, most-spent first.\n */\nexport function mergeAccountWindows(readings: readonly AccountReading[]): readonly UsageWindow[] {\n const byLabel = new Map<string, UsageWindow[]>()\n for (const reading of readings) {\n for (const window of reading.windows) {\n // Only a percentage-metered window merges. A credit balance (a bare used/limit\n // pair) is money, not a share of a shared allowance, and averaging it across\n // accounts would misread money as a quota.\n if (window.usedPercent === undefined) continue\n const list = byLabel.get(window.label) ?? []\n list.push(window)\n byLabel.set(window.label, list)\n }\n }\n const merged: UsageWindow[] = []\n for (const [label, list] of byLabel) {\n // Percentages average across accounts: the pool's spent share is the mean of each\n // account's spent share (accounts share an allowance, they do not add to it).\n const withPercent = list.filter((window) => window.usedPercent !== undefined)\n if (withPercent.length === 0) continue\n const usedPercent = withPercent.reduce((sum, window) => sum + (window.usedPercent ?? 0), 0) / withPercent.length\n // A credit window carries the raw figures too; keep them so the compact line can read\n // money as money (0/1500) rather than as a spent share (0%), which misreads an empty\n // balance as nothing to meter.\n const used = list.reduce((sum, window) => sum + (window.used ?? 0), 0)\n const limit = list.reduce((sum, window) => sum + (window.limit ?? 0), 0)\n // The pool's reset is the soonest account reset: the window refills when the first\n // account's does.\n const resets = list.map((window) => window.resetsAt).filter((at): at is number => typeof at === 'number')\n merged.push({\n label,\n usedPercent: Math.max(0, Math.min(100, usedPercent)),\n ...limit > 0 ? { used, limit } : {},\n ...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) },\n })\n }\n // Order by window period, shortest first: the rolling `5h` window reads before the\n // `weekly` one, the way the providers state them and the way a reader scans (what\n // resets soonest is the nearer limit). Unrecognised labels keep their input order.\n const order = (label: string): number => {\n const short = shortWindow(label)\n if (short === '5h' || short === 's') return 0\n if (short === '1d' || short === 'mo') return 1\n if (short === '7d') return 2\n return 3\n }\n return merged.sort((left, right) => order(left.label) - order(right.label))\n}\n\n/**\n * Reduce one route's usage to the single line the compact chip renders.\n *\n * Kept for the balance-free providers that still show one merged line.\n *\n * @param provider - the route id.\n * @param label - the route's display name.\n * @param usage - the accounts the route answered for.\n * @returns the first account's reading, or undefined when none states a figure.\n */\nexport function readingOf(\n provider: string,\n label: string,\n usage: readonly ProviderUsage[],\n): ChipReading | undefined {\n for (const entry of usage) {\n const window = pickWindow(entry.windows)\n if (window === undefined) continue\n const windows = entry.windows.filter((candidate) => candidate.usedPercent !== undefined)\n return {\n provider,\n label,\n windows,\n window,\n ...entry.plan === undefined ? {} : { plan: entry.plan },\n }\n }\n return undefined\n}\n\n/**\n * Format a window's reset the way the settings cards do.\n *\n * The rule is shared with the cards on purpose: a figure reading `6d20h` here and\n * something else there would be two answers to one question.\n *\n * @param at - epoch milliseconds, or undefined when the provider states none.\n * @param now - the instant to measure from.\n * @returns a short phrase, or undefined when there is no reset to show.\n */\nexport function formatReset(at: number | undefined, now: number): string | undefined {\n if (at === undefined) return undefined\n const remaining = at - now\n if (remaining <= 0) return 'now'\n const hours = remaining / 3_600_000\n if (hours < 1) return `${String(Math.max(1, Math.round(remaining / 60_000)))}m`\n if (hours < 24) return `${String(Math.round(hours))}h`\n const days = Math.floor(hours / 24)\n return `${String(days)}d${String(Math.round(hours - days * 24))}h`\n}\n\n/**\n * The composer's usage chip.\n *\n * @param props - injected RPC caller, copy, and the session's provider reader.\n * @returns the chip, or null when there is nothing true to say.\n */\nexport function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, useProjection }: UsageChipInjected & SessionStandardProps): ReactNode {\n const [reading, setReading] = useState<ChipReading | undefined>(undefined)\n const [accountReadings, setAccountReadings] = useState<readonly AccountReading[] | undefined>(undefined)\n const [balance, setBalance] = useState<readonly ChipBalance[] | undefined>(undefined)\n const [ccBalance, setCcBalance] = useState<{ currency: string, total: string } | undefined>(undefined)\n const [now, setNow] = useState(() => Date.now())\n const [open, setOpen] = useState(false)\n // The last route that answered, so a switch to a foreign provider leaves the last own\n // reading up rather than blanking the bar.\n const lastOwn = useRef<string | undefined>(undefined)\n\n // A projection read, so the chip follows the picker: changing the model re-renders it\n // with the new provider instead of waiting for the next unrelated render.\n const selection = useProjection('modelSelection', (value) => (value as ModelSelectionProjection))\n const selected = providerOf(selection)\n\n // A non-subscription provider has no usage windows to meter, so the chip shows its\n // balance instead, read by cc-switch's query. The host's account (deepseek-account)\n // has its own balance source and takes that over cc-switch; a subscription this plugin\n // serves keeps its usage meter.\n const isDeepSeek = selected === 'deepseek-account'\n const isBalanceProvider = selected !== undefined && !isOwnProvider(selected) && !isDeepSeek\n\n // Read a non-subscription provider's balance while it is in force, on the same cadence\n // as the other reads. It resolves to undefined when the provider has no balance source\n // or did not answer, which leaves the chip silent.\n useEffect(() => {\n if (!isBalanceProvider || selected === undefined) {\n setCcBalance(undefined)\n return\n }\n let live = true\n const target = selected\n const refresh = (): void => {\n void ccSwitchBalance(target)\n .then((value) => { if (live) setCcBalance(value === undefined ? undefined : { currency: value.currency, total: value.total }) })\n .catch(() => { /* a failed balance read stays silent */ })\n }\n refresh()\n const timer = setInterval(refresh, REFRESH_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [isBalanceProvider, selected, ccSwitchBalance])\n\n const load = useCallback(async (target: string): Promise<readonly AccountReading[] | undefined> => {\n try {\n const result = await call('usage', { provider: target })\n return accountReadingsOf(result.usage)\n } catch {\n // A failed read is not worth a chip of its own: the settings page is where a user\n // goes to find out why a route cannot report.\n return undefined\n }\n }, [call])\n\n // Read the DeepSeek account's balance while that provider is in force, on the same\n // cadence as a usage read. It resolves to undefined when the account is signed out or\n // the read fails, which leaves the chip silent.\n useEffect(() => {\n if (!isDeepSeek) {\n setBalance(undefined)\n return\n }\n let live = true\n const refresh = (): void => {\n void accountBalance()\n .then((value) => { if (live) setBalance(balanceOf(value)) })\n .catch(() => { /* a failed balance read stays silent */ })\n }\n refresh()\n const timer = setInterval(refresh, REFRESH_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [isDeepSeek, accountBalance])\n\n useEffect(() => {\n const target = selected !== undefined && isOwnProvider(selected)\n ? selected\n // Not one of ours (or none selected): keep showing the last own reading.\n : lastOwn.current\n if (target === undefined) {\n setAccountReadings(undefined)\n return\n }\n let live = true\n const refresh = (): void => {\n setNow(Date.now())\n void load(target).then((next) => {\n if (!live || next === undefined) return\n lastOwn.current = target\n setAccountReadings(next)\n })\n }\n refresh()\n const timer = setInterval(refresh, REFRESH_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [selected, isOwnProvider, load])\n\n // A click anywhere else puts the detail away, which is what a status chip should do.\n useEffect(() => {\n if (!open) return\n const close = (): void => { setOpen(false) }\n document.addEventListener('click', close)\n return () => { document.removeEventListener('click', close) }\n }, [open])\n\n // A non-subscription provider reads as money, through cc-switch's query. It shows the\n // one currency's total the provider answered with, nothing when there is no usable\n // balance.\n if (isBalanceProvider) {\n if (ccBalance === undefined) return null\n const short = shortLabel(selected ?? '', selected ?? '')\n return (\n <span className=\"dsh-subscription-usage-chip\">\n <style>{usageChipCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-usage-chip-trigger\"\n aria-expanded={open}\n title={t('usageChipTitle', { provider: selected ?? '' })}\n onClick={(event) => {\n event.stopPropagation()\n setOpen((was) => !was)\n }}\n >\n <span className=\"dsh-subscription-usage-chip-label\">{short}</span>\n <span className=\"dsh-subscription-usage-chip-cell\">\n <span className=\"dsh-subscription-usage-chip-value\">\n {`${ccBalance.currency === 'CNY' ? '¥' : ccBalance.currency === 'USD' ? '$' : `${ccBalance.currency} `}${ccBalance.total}`}\n </span>\n </span>\n </button>\n {open && (\n <span className=\"dsh-subscription-usage-chip-panel\" role=\"status\">\n <span className=\"dsh-subscription-usage-chip-full\">\n {t('usageChipFull', { provider: selected ?? '' })}\n </span>\n <span className=\"dsh-subscription-usage-chip-meter\">\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{ccBalance.currency}</span>\n <span className=\"dsh-subscription-usage-chip-detail\">{ccBalance.total}</span>\n </span>\n </span>\n </span>\n )}\n </span>\n )\n }\n\n // The DeepSeek account reads as money, not as a percentage, so it takes its own line:\n // the route abbreviated, then each wallet. An unknown selection leaves the chip silent.\n if (isDeepSeek) {\n if (balance === undefined) return null\n const short = shortLabel('deepseek-account', 'DeepSeek Account')\n return (\n <span className=\"dsh-subscription-usage-chip\">\n <style>{usageChipCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-usage-chip-trigger\"\n aria-expanded={open}\n title={t('usageChipTitle', { provider: 'DeepSeek Account' })}\n onClick={(event) => {\n event.stopPropagation()\n setOpen((was) => !was)\n }}\n >\n <span className=\"dsh-subscription-usage-chip-label\">{short}</span>\n {balance.map((wallet) => (\n <span className=\"dsh-subscription-usage-chip-cell\" key={`${wallet.currency}${wallet.bonus ? ':bonus' : ''}`}>\n <span className=\"dsh-subscription-usage-chip-value\">{formatBalance(wallet)}</span>\n </span>\n ))}\n </button>\n {open && (\n <span className=\"dsh-subscription-usage-chip-panel\" role=\"status\">\n <span className=\"dsh-subscription-usage-chip-full\">\n {t('usageChipFull', { provider: 'DeepSeek Account' })}\n </span>\n {balance.map((wallet) => (\n <span className=\"dsh-subscription-usage-chip-meter\" key={`${wallet.currency}${wallet.bonus ? ':bonus' : ''}`}>\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{`${wallet.bonus ? 'Bonus · ' : ''}${wallet.currency}`}</span>\n <span className=\"dsh-subscription-usage-chip-detail\">{formatBalance(wallet)}</span>\n </span>\n </span>\n ))}\n </span>\n )}\n </span>\n )\n }\n\n if (accountReadings === undefined || accountReadings.length === 0) return null\n const short = shortLabel(selected ?? '', selected ?? '')\n // The compact line integrates the whole pool; the panel shows each account's own\n // figures, so a merged glance hides nothing a per-account look cannot recover.\n const merged = mergeAccountWindows(accountReadings)\n\n return (\n <span className=\"dsh-subscription-usage-chip\">\n {/* An element, so unmounting the chip takes its rules with it. */}\n <style>{usageChipCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-usage-chip-trigger\"\n aria-expanded={open}\n title={t('usageChipTitle', { provider: selected ?? '' })}\n onClick={(event) => {\n // The document listener would otherwise close what this click just opened.\n event.stopPropagation()\n setOpen((was) => !was)\n }}\n >\n <span className=\"dsh-subscription-usage-chip-label\">{short}</span>\n {merged.map((window) => (\n <span className=\"dsh-subscription-usage-chip-cell\" key={window.label}>\n <span className=\"dsh-subscription-usage-chip-window\">{shortWindow(window.label)}</span>\n <span className=\"dsh-subscription-usage-chip-value\">\n {window.used !== undefined && window.limit !== undefined\n ? `${String(window.used)}/${String(window.limit)}`\n : `${String(Math.round(window.usedPercent ?? 0))}%`}\n </span>\n </span>\n ))}\n </button>\n {open && (\n <span className=\"dsh-subscription-usage-chip-panel\" role=\"status\">\n <span className=\"dsh-subscription-usage-chip-full\">\n {t('usageChipFull', { provider: selected ?? '' })}\n </span>\n {accountReadings.map((account) => (\n <span key={account.account} className=\"dsh-subscription-usage-chip-account\">\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{account.plan ?? account.account}</span>\n </span>\n {account.windows.map((window) => {\n const windowReset = formatReset(window.resetsAt, now)\n // A credit balance reads as money (`used`/`limit`), a quota as a percent.\n const amount = window.usedPercent === undefined && window.used !== undefined && window.limit !== undefined\n ? `${String(window.used)}/${String(window.limit)}`\n : `${String(Math.round(window.usedPercent ?? 0))}%`\n return (\n <span className=\"dsh-subscription-usage-chip-meter\" key={window.label}>\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{window.label}</span>\n <span className=\"dsh-subscription-usage-chip-detail\">\n {`${amount}${windowReset === undefined ? '' : ` · ${windowReset}`}`}\n </span>\n </span>\n {window.usedPercent !== undefined && (\n <span className=\"dsh-subscription-usage-chip-track\" aria-hidden=\"true\">\n <span\n className=\"dsh-subscription-usage-chip-fill\"\n style={{ width: `${String(Math.max(0, Math.min(100, window.usedPercent ?? 0)))}%` }}\n />\n </span>\n )}\n </span>\n )\n })}\n </span>\n ))}\n </span>\n )}\n </span>\n )\n}\n","/**\n * The composer's quick-mode button.\n *\n * The route's own fast/lightspeed variant is not a row in the model picker: it shares its\n * base model's visibility and is reached from here. The composer renders\n * `conversation.input.right` immediately before its model seat, so this button sits at\n * the model selector's left — the control a reader looks for when they want the same\n * model, faster.\n *\n * Off, the selection is the base model and pressing the button selects the fast variant.\n * On, the selection *is* the fast variant, so the button reports active and pressing it\n * returns to the base — which is why `model-variants` answers from both sides of the pair\n * rather than only from the base id.\n *\n * The button renders nothing when the model has no fast variant, when the route is not\n * one this plugin serves, or when the host exposes no way to change the selection: an\n * absent control is honest, a disabled one is noise.\n *\n * @module dsh-plugin-subscription-oauth/client/FastModeButton\n */\n\nimport { useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { SessionStandardProps } from '@deepseek-ai/dsh-client-ui-slots'\n\nimport type { SubscriptionsCall } from './subscriptions-rpc.js'\nimport type { ModelSelectionProjection } from './UsageChip.js'\nimport { providerOf } from './UsageChip.js'\nimport { fastModeCss } from './styles.js'\n\n/** Injected props the button is registered with. */\nexport interface FastModeButtonInjected {\n /** The plugin's own RPC caller, for the one `model-variants` read. */\n call: SubscriptionsCall\n /** Whether the provider is one this plugin serves. */\n isOwnProvider: (provider: string) => boolean\n /**\n * The session's model directory, when the host exposes one.\n *\n * Written when the button switches the model's `fast` rung. Absent on a host without\n * it, in which case the button renders nothing.\n */\n modelDirectory?: {\n /** Select one model and its reasoning rung; resolves when the picker accepted it. */\n select?: (selection: { provider: string, model: string, reasoningEffort?: string }) => Promise<unknown>\n } | undefined\n}\n\n/**\n * The quick-mode control, for the composer's row of standard controls.\n *\n * @param props - injected RPC caller, directory, and the session standard kit.\n * @returns the button, or null when this model has no fast variant.\n */\nexport function FastModeButton({ call, isOwnProvider, modelDirectory, useProjection }: FastModeButtonInjected & SessionStandardProps): ReactNode {\n const [pair, setPair] = useState<{ rung: string } | undefined>(undefined)\n // A switch that the host refuses must not look like a dead button: the reason is kept\n // and shown, because the only alternative is a control that silently does nothing.\n const [failure, setFailure] = useState<{ code?: string, message: string } | undefined>(undefined)\n const [pending, setPending] = useState(false)\n const selection = useProjection('modelSelection', (value) => (value as ModelSelectionProjection))\n const provider = providerOf(selection)\n const model = selection?.next?.model ?? selection?.lastUsed?.model\n\n useEffect(() => {\n if (provider === undefined || model === undefined || !isOwnProvider(provider)) {\n setPair(undefined)\n return\n }\n let live = true\n void call('model-variants', { provider, id: model })\n .then((result) => {\n if (!live) return\n // The rung is the provider's own word (xAI `lightspeed`, Kimi `highspeed`), so\n // the picker reads a name the model's own docs use, not a generic \"Fast\".\n setPair(result?.fast === undefined ? undefined : { rung: result.fastRung ?? 'fast' })\n })\n .catch(() => { if (live) setPair(undefined) })\n return () => { live = false }\n }, [provider, model, isOwnProvider, call])\n\n // A switch that landed clears the reason, so the marker follows the truth on screen.\n useEffect(() => { setFailure(undefined) }, [model])\n\n if (pair === undefined || provider === undefined || model === undefined || modelDirectory?.select === undefined) return null\n // Fast mode is the row's fast reasoning rung, so the model id never changes and the\n // picker always shows the row the user can switch back to.\n const active = (selection?.next ?? selection?.lastUsed)?.reasoningEffort === pair.rung\n const target = model\n\n const base = active\n ? 'Fast mode is on; press to go back to the standard model'\n : 'Fast mode: switch to this model’s fast variant'\n const title = failure === undefined\n ? base\n : `${base}\\nThe switch was refused — ${`${failure.code ?? ''} ${failure.message}`.trim()}`\n\n return (\n <>\n <style>{fastModeCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-fast-mode\"\n data-active={active}\n data-pending={pending}\n data-failed={failure !== undefined}\n aria-pressed={active}\n aria-busy={pending}\n title={title}\n aria-label={title}\n onClick={(event) => {\n event.stopPropagation()\n setPending(true)\n setFailure(undefined)\n // Turning it off drops the rung, which the host then reads as the model's own\n // default. The model id is never swapped, so this cannot strand the selection.\n const next = active ? { provider, model: target } : { provider, model: target, reasoningEffort: pair.rung }\n void Promise.resolve(modelDirectory.select?.(next))\n .then((result) => {\n // The directory reports its own outcome: a refused selection carries why.\n const outcome = result as { ok?: boolean, error?: { code?: string, message?: string } } | undefined\n if (outcome?.ok !== false) return\n const code = outcome.error?.code\n const message = outcome.error?.message ?? code ?? 'the host refused the selection'\n setFailure({ ...code === undefined ? {} : { code }, message })\n console.warn(`[subscription-oauth] fast mode ${provider}/${target} refused:`, outcome.error)\n })\n .catch((error: unknown) => {\n setFailure({ message: error instanceof Error ? error.message : String(error) })\n console.warn(`[subscription-oauth] fast mode ${provider}/${target} failed:`, error)\n })\n .finally(() => { setPending(false) })\n }}\n >\n <svg className=\"dsh-subscription-fast-mode-bolt\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" aria-hidden=\"true\" fill=\"none\">\n <path d=\"M13 2 3.6 13.2a1.2 1.2 0 0 0 .92 1.98H11l-.86 6.42a1.2 1.2 0 0 0 2.2.7L21.4 10.6a1.2 1.2 0 0 0-.92-1.98H13l.86-6.42A1.2 1.2 0 0 0 13 2z\" fill=\"currentColor\" stroke=\"currentColor\" strokeWidth=\"1.2\" strokeLinejoin=\"round\" />\n </svg>\n {failure !== undefined && (\n <span className=\"dsh-subscription-fast-mode-reason\">{failure.code ?? failure.message}</span>\n )}\n </button>\n </>\n )\n}\n","/**\n * Subscriptions settings page, browser half.\n *\n * Registers one `settings.section` entry titled 订阅 / Subscriptions. Every\n * connection fact arrives through the node half's `subscription-oauth` RPC\n * endpoints, so this half holds no credential state of its own, and the copy\n * rides the client locale service under the `settings.subscriptions` namespace.\n *\n * @module dsh-plugin-subscription-oauth/client\n */\n\n// Type-only: pulls the `ctx.slots` Context merge and the `settings.section`\n// SlotMap entry. These imports are erased; no runtime binding is taken.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\n\nimport { SubscriptionsSection } from './SubscriptionsSection.js'\nimport type { SubscriptionsSectionInjected } from './SubscriptionsSection.js'\nimport { FastModeButton } from './FastModeButton.js'\nimport type { FastModeButtonInjected } from './FastModeButton.js'\nimport { UsageChip } from './UsageChip.js'\nimport type { UsageChipInjected } from './UsageChip.js'\nimport { createSubscriptionsCall, createAccountBalanceCall, createCcSwitchBalanceCall } from './subscriptions-rpc.js'\nimport type { AccountBalanceCall } from './subscriptions-rpc.js'\nimport { en, zh } from './locales.js'\nimport type { SubscriptionsKey } from './locales.js'\n\nexport type { SubscriptionsSectionInjected, SubscriptionsSectionProps } from './SubscriptionsSection.js'\nexport type { FastModeButtonInjected } from './FastModeButton.js'\nexport type { UsageChipInjected } from './UsageChip.js'\nexport type { SubscriptionsKey } from './locales.js'\nexport { createSubscriptionsCall, SubscriptionsRpcError } from './subscriptions-rpc.js'\nexport type { SubscriptionsCall, AccountBalanceCall } from './subscriptions-rpc.js'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The Subscriptions settings page copy. */\n 'settings.subscriptions': SubscriptionsKey\n }\n interface SlotMap {\n /**\n * The composer's bottom bar.\n *\n * Declared here as the shell declares it — a session-scoped list — because the\n * owning package ships no type declarations to a plugin that only consumes it.\n * The shell's own stats pills are one entry in this list; this plugin's usage chip\n * is another.\n */\n 'conversation.composer.dock': { kind: 'list', scope: 'session' }\n /**\n * The composer's row of compact controls, rendered immediately before its model seat.\n *\n * The shell declares it as a session-scoped list; this plugin's quick-mode button is\n * one entry, which is what puts it at the model selector's left.\n */\n 'conversation.input.right': { kind: 'list', scope: 'session' }\n }\n\n /**\n * The session standard kit, of which this plugin reads one member.\n *\n * `ui-session` merges the session identity and the projection reader into every\n * session-scoped component's props, and it ships no declarations a plugin can see, so\n * the member is declared here. It reads one session projection, optionally narrowed\n * by a selector, and re-renders the component when that projection changes — which is\n * what lets the usage chip follow the model picker.\n */\n interface SessionStandardProps {\n useProjection<T>(key: string, selector: (value: unknown) => T): T\n }\n}\n\n/**\n * Routes this plugin serves.\n *\n * The chip only reports for these; a provider somebody else registered gets no chip,\n * because this plugin has no way to read its allowance.\n */\nconst OWN_PROVIDER_IDS = new Set(['kimi', 'grok', 'antigravity', 'glm', 'ark'])\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'settings.subscriptions'\n\n/** Settings nav position: after the shipped sections, before plugin tooling. */\nconst SECTION_ORDER = 92\n\n/**\n * Composer-dock position: after the shell's own stats pills, which sit at 0.\n *\n * The shell owns that bar and registers `tok/s` and the cache-hit pill into it, so the\n * chip follows them rather than displacing them.\n */\nconst CHIP_ORDER = 10\n\n/**\n * Composer-control position: ahead of the shell's own entries in that row, if it ever\n * adds any, so the quick-mode button stays adjacent to the model selector it belongs to.\n */\nconst FAST_MODE_ORDER = 10\n\n/** The host's per-session model directory resolver, as far as this plugin reads it. */\ninterface ModelDirectories {\n directoryFor?: (sessionId: string) => {\n select?: (selection: { provider: string, model: string }) => Promise<unknown>\n } | undefined\n}\n\n/**\n * Required services: `slots` carries the registration seat, `connection` the\n * RPC caller, `locale` the copy dictionaries, and `modelDirectories` the picker's\n * per-session model directory (written when the quick-mode button routes to a fast\n * variant).\n */\nexport const inject = ['slots', 'connection', 'locale', 'modelDirectories']\n\n/**\n * Register the Subscriptions settings section.\n *\n * Registration goes through `slots.inject`, so the shell's own apply order\n * relative to this one is unconstrained.\n *\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-plugin-subscription-oauth: copy dictionaries')\n // The shell's Context merge types `connection` as the host handle; in the\n // browser the same key holds the full client ConnectionHandle.\n const connection = ctx.get('connection') as unknown as ConnectionHandle\n const t = ctx.locale.bind(NS) as SubscriptionsSectionInjected['t']\n const call = createSubscriptionsCall(connection.rpc)\n // Read once: the resolver is a service, and the per-session directory is resolved at\n // registration time (see `modelDirectoryFor`).\n const modelDirectories = ctx.get('modelDirectories') as ModelDirectories | undefined\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'subscriptions',\n order: SECTION_ORDER,\n // A thunk re-evaluated per read, so the nav label follows the active locale.\n label: () => t('nav'),\n inject: (): SubscriptionsSectionInjected => ({ call, t }),\n }, SubscriptionsSection))\n\n // The composer's bottom bar is a session-scoped list the shell owns; its own stats\n // pills sit at order 0. This chip rides beside them and shows the allowance of the\n // provider the session is using. The scope hands `inject` the session id, but the chip\n // reads the selection from its own projection prop, so nothing here needs it.\n ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register({\n name: 'conversation.composer.dock',\n id: 'subscription-usage',\n order: CHIP_ORDER,\n inject: (): UsageChipInjected => ({\n call,\n t: t as unknown as UsageChipInjected['t'],\n isOwnProvider: (provider: string) => OWN_PROVIDER_IDS.has(provider),\n accountBalance: createAccountBalanceCall(connection.rpc),\n ccSwitchBalance: createCcSwitchBalanceCall(connection.rpc),\n }),\n }, UsageChip))\n\n // The composer renders this seat immediately before its model selector, so the\n // quick-mode button lands at the picker's left. It renders nothing unless the current\n // model really has a fast variant, so a route without one shows no control at all.\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register({\n name: 'conversation.input.right',\n id: 'subscription-fast-mode',\n order: FAST_MODE_ORDER,\n inject: (sessionId?: string): FastModeButtonInjected => ({\n call,\n isOwnProvider: (provider: string) => OWN_PROVIDER_IDS.has(provider),\n modelDirectory: modelDirectoryFor(modelDirectories, sessionId),\n }),\n }, FastModeButton))\n}\n\n/**\n * The session's model directory, in the shape the quick-mode button writes through.\n *\n * Absent when the host exposes no directory for the session, or exposes one that cannot\n * select — in which case the button renders nothing rather than a dead control.\n *\n * @param modelDirectories - the host's directory resolver, when it has one.\n * @param sessionId - the session whose directory is wanted.\n * @returns the reader/writer pair, or undefined.\n */\nfunction modelDirectoryFor(\n modelDirectories: ModelDirectories | undefined,\n sessionId: string | undefined,\n): FastModeButtonInjected['modelDirectory'] {\n if (sessionId === undefined || modelDirectories?.directoryFor === undefined) return undefined\n const directory = modelDirectories.directoryFor(sessionId)\n if (directory?.select === undefined) return undefined\n return {\n select: (selection: { provider: string, model: string }) => directory.select?.(selection) ?? Promise.resolve(),\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["code: string","messageOf","result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>","error: unknown","template: string","formatReset","styles: Record<string, CSSProperties>","window","accounts: readonly AccountStatus[]","caught: unknown","SHORT_LABELS: Record<string, string>","SHORT_WINDOWS: Record<string, string>","wallets: ChipBalance[]","out: AccountReading[]","window","merged: UsageWindow[]","short"],"sources":["../src/client/locales.ts","../src/client/styles.ts","../src/protocol.ts","../src/client/subscriptions-rpc.ts","../src/client/SubscriptionsSection.tsx","../src/client/UsageChip.tsx","../src/client/FastModeButton.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Copy dictionaries for the Subscriptions settings page.\n *\n * English is the key-set source of truth; the Chinese dictionary is typed\n * against it, so a key added to one and forgotten in the other is a compile\n * error rather than a silent fallback.\n */\n\n/** English strings. */\nexport const en = {\n nav: 'Subscriptions',\n intro: 'Sign in with the AI subscriptions you already pay for — no API key. Pick a provider below, connect once, and its models join the model picker with live plan usage. Tokens stay on this machine.',\n loading: 'Checking subscriptions…',\n unavailable: 'The subscriptions page needs the web connection service, which this composition does not provide.',\n statusConnected: 'Connected · {count} account(s)',\n statusSignedOut: 'Not signed in',\n statusBusy: 'Waiting for browser approval',\n statusSignIn: 'Sign in to add this provider\\'s models to the model picker.',\n statusAuto: 'Follows {count} enabled model(s) from every provider',\n connect: 'Connect',\n edit: 'Edit',\n collapse: 'Collapse',\n addAccount: 'Add account',\n manage: 'Manage',\n cancel: 'Cancel',\n close: 'Close',\n logout: 'Sign out',\n makeDefault: 'Use by default',\n defaultBadge: 'Default',\n badgeLive: 'Live',\n accountNeedsLogin: 'Sign in again',\n accountNeedsLoginHint: 'The provider refused this account\\'s grant, so its token cannot be renewed',\n accountExpired: 'token expired',\n accountExpires: 'expires {time}',\n deviceTitle: 'Approve this device',\n devicePrompt: 'Open the verification page and confirm the code below. This page updates by itself once the provider approves.',\n deviceOpen: 'Open verification page',\n deviceCopy: 'Copy code',\n deviceCopied: 'Copied',\n deviceExpires: 'The code expires at {time}.',\n openLogin: 'Open sign-in page',\n openLoginHint: 'Finish signing in in the browser tab that opened. This page updates by itself once the provider redirects back.',\n modelsTitle: 'Models',\n modelsCount: '{count} available',\n modelsLive: 'live',\n modelsBuiltIn: 'built-in',\n modelsShow: 'Show in picker',\n modelsHiddenCount: '· {count} hidden',\n modelsHidden: 'Hidden from the model picker',\n modelsEmpty: 'No models reported yet.',\n modelsAdd: 'Add model',\n modelsAddHint: 'Publish a model id this build does not know yet',\n modelsAddPlaceholder: 'model id, e.g. k3-next',\n modelsAddConfirm: 'Add',\n modelsRemove: 'Remove this hand-added model',\n modelsContext: '{tokens} context',\n modelsAuto: 'Auto',\n modelsAutoHint: 'Pinned first: moves to the next model when one is unavailable',\n modelsAutoOff: 'Off — the picker offers the models directly instead',\n modelsAutoChain: 'Auto tries these, in this order — drag to re-order',\n modelsDrag: 'Drag to re-order (or press ↑ / ↓ here)',\n modelsAutoChainOff: 'Take out of Auto',\n modelsAutoChainEmpty: 'No model is enabled, so Auto has nothing to try.',\n autoSwitchLabel: 'Offer Auto in the model picker',\n modelsPinned: 'pinned',\n usageTitle: 'Plan and usage',\n usagePlan: 'Plan: {plan}',\n usageRenews: 'renews {time}',\n usageReset: 'resets {time}',\n usageResetsInMinutes: 'resets in {minutes}m',\n usageResetsInHours: 'resets in {hours}h',\n usageResetDue: 'resets shortly',\n usageRefresh: 'Refresh usage',\n usageFetched: 'read {time}',\n usageNone: 'This provider reports no plan quota.',\n usageError: 'Could not read usage: {message}',\n usage_5h: '5-hour window',\n usage_weekly: 'Weekly window',\n usage_credits: 'Included credit',\n usage_gemini: 'Gemini allowance (rolling)',\n usage_gemini_weekly: 'Gemini allowance (weekly)',\n usage_claude: 'Claude allowance (rolling)',\n usage_claude_weekly: 'Claude allowance (weekly)',\n usage_other: 'Allowance',\n refresh: 'Refresh',\n refreshing: 'Refreshing…',\n modelsRefreshed: 'Model list refreshed.',\n refreshToken: 'Refresh token',\n tokenRefreshed: 'Token refreshed; valid until {time}.',\n hint: 'Authorization follows the account the browser is signed in with. To add a different account, switch accounts there first, or use a private window.',\n manageTitle: 'Manage {provider}',\n manageAccounts: 'Accounts',\n reload: 'Reload credentials',\n reloadHint: 'Use this after signing in from another DeepSeek Harness window.',\n detail_oauthHost: 'Authorization server',\n detail_apiBase: 'API base',\n detail_credentialFile: 'Credential file',\n detail_project: 'Cloud project',\n lastError: 'Last login attempt failed: {message}',\n loginFailed: 'Could not start the sign-in: {message}',\n actionFailed: 'Action failed: {message}',\n usageChipTitle: '{provider} subscription usage',\n usageChipFull: '{provider} allowance',\n} as const\n\n/** The Subscriptions namespace key union. */\nexport type SubscriptionsKey = keyof typeof en\n\n/** Chinese strings. */\nexport const zh = {\n nav: '订阅',\n intro: '使用你已付费的 AI 订阅 —— 无需 API Key。在下方选择服务,连接一次,其模型即进入模型选择器并显示实时额度。令牌仅保存在本机。',\n loading: '正在检查订阅…',\n unavailable: '订阅页面需要 Web 连接服务,当前组合未提供该服务。',\n statusConnected: '已连接 {count} 个账号',\n statusSignedOut: '未登录',\n statusBusy: '等待浏览器确认',\n statusSignIn: '登录后即可在模型选择器中使用该服务的模型。',\n statusAuto: '依次使用来自各服务的 {count} 个已启用模型',\n connect: '连接',\n edit: '编辑',\n collapse: '收起',\n addAccount: '添加账号',\n manage: '管理',\n cancel: '取消',\n close: '关闭',\n logout: '退出登录',\n makeDefault: '设为默认',\n defaultBadge: '默认',\n badgeLive: '已连接',\n accountNeedsLogin: '需重新登录',\n accountNeedsLoginHint: '服务方已拒绝该账号的授权,令牌无法自动续期',\n accountExpired: '令牌已过期',\n accountExpires: '过期时间 {time}',\n deviceTitle: '确认此设备',\n devicePrompt: '打开验证页面并确认下方代码,服务确认后本页面会自动更新。',\n deviceOpen: '打开验证页面',\n deviceCopy: '复制代码',\n deviceCopied: '已复制',\n deviceExpires: '代码将于 {time} 过期。',\n openLogin: '打开登录页面',\n openLoginHint: '请在打开的浏览器标签页中完成登录;服务回调后本页面会自动更新。',\n modelsTitle: '模型',\n modelsCount: '共 {count} 个',\n modelsLive: '实时',\n modelsBuiltIn: '内置',\n modelsShow: '在选择器中显示',\n modelsHiddenCount: '· 已隐藏 {count} 个',\n modelsHidden: '已从模型选择器隐藏',\n modelsEmpty: '暂未获取到模型。',\n modelsAdd: '添加模型',\n modelsAddHint: '手动添加一个本版本还不认识的模型 ID',\n modelsAddPlaceholder: '模型 ID,例如 k3-next',\n modelsAddConfirm: '添加',\n modelsRemove: '移除这个手动添加的模型',\n modelsContext: '{tokens} 上下文',\n modelsAuto: '自动',\n modelsAutoHint: '固定在首位:某个模型不可用时自动改用下一个',\n modelsAutoOff: '已关闭 —— 选择器将直接列出各个模型',\n modelsAutoChain: '自动会按这个顺序依次尝试(拖动可调整)',\n modelsDrag: '拖动调整顺序(也可在此按 ↑ / ↓)',\n modelsAutoChainOff: '从自动中移除',\n modelsAutoChainEmpty: '没有启用任何模型,自动没有可尝试的模型。',\n autoSwitchLabel: '在模型选择器中提供 Auto',\n modelsPinned: '固定',\n usageTitle: '套餐与额度',\n usagePlan: '套餐:{plan}',\n usageRenews: '续费时间 {time}',\n usageReset: '重置时间 {time}',\n usageResetsInMinutes: '{minutes} 分钟后重置',\n usageResetsInHours: '{hours} 小时后重置',\n usageResetDue: '即将重置',\n usageRefresh: '刷新额度',\n usageFetched: '读取于 {time}',\n usageNone: '该服务未提供套餐额度信息。',\n usageError: '额度读取失败:{message}',\n usage_5h: '5 小时窗口',\n usage_weekly: '每周窗口',\n usage_credits: '包含额度',\n usage_gemini: 'Gemini 模型额度(滚动)',\n usage_gemini_weekly: 'Gemini 模型额度(每周)',\n usage_claude: 'Claude 模型额度(滚动)',\n usage_claude_weekly: 'Claude 模型额度(每周)',\n usage_other: '额度',\n refresh: '刷新',\n refreshing: '正在刷新…',\n modelsRefreshed: '模型列表已刷新。',\n refreshToken: '刷新令牌',\n tokenRefreshed: '令牌已刷新,有效至 {time}。',\n hint: '授权以浏览器当前登录的账号为准;要添加不同账号,请先在浏览器里切换账号,或使用无痕窗口。',\n manageTitle: '管理 {provider}',\n manageAccounts: '账号',\n reload: '重新读取凭据',\n reloadHint: '在另一个 DeepSeek Harness 窗口中登录后,请使用此操作。',\n detail_oauthHost: '授权服务器',\n detail_apiBase: 'API 地址',\n detail_credentialFile: '凭据文件',\n detail_project: '云项目',\n lastError: '上次登录失败:{message}',\n loginFailed: '无法开始登录:{message}',\n actionFailed: '操作失败:{message}',\n usageChipTitle: '{provider} 订阅用量',\n usageChipFull: '{provider} 额度',\n} satisfies Record<SubscriptionsKey, string>\n","/**\n * Scoped stylesheet for the Subscriptions page and its manage dialog.\n *\n * The host loads plugin markup without its own control classes, and a bare\n * `<button>` therefore renders with the browser's default raised chrome — which\n * reads as a stray highlight in a dark theme. Rather than invent a look, this\n * follows the control vocabulary of the sibling `dsh-plugin-subscriptions`\n * plugin (same author-facing design as the shell's settings forms): a 6 px\n * radius small control, a transparent fill, a hairline border from the theme's\n * alias tokens, and the theme's hover/active/focus tokens for state.\n *\n * The tokens all carry fallbacks, so the page still renders correctly on a host\n * build that does not define them.\n *\n * @module dsh-plugin-subscription-oauth/client/styles\n */\n\n/**\n * Everything is scoped to this plugin's own roots, so no other surface in the\n * shell can be affected by a rule here.\n */\nconst scope = ':is(.dsh-subscriptions-settings, .dsh-subscription-manager)'\n\n/** The page's and dialog's stylesheet. */\nexport const subscriptionsCss = `\n.dsh-subscriptions-settings,\n.dsh-subscription-manager {\n font-family: inherit;\n font-size: 13px;\n line-height: 1.5;\n /* Native controls (checkbox, dialog) follow the host theme, not the OS. */\n color-scheme: light;\n}\nbody[data-ds-dark-theme] ${scope} {\n color-scheme: dark;\n}\n.dsh-subscription-manager {\n box-sizing: border-box;\n padding: 0;\n border: 0;\n border-radius: var(--dsw-radius-panel, 16px);\n background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n color: var(--dsw-alias-label-primary);\n box-shadow: var(--dsw-elevation-prominent, 0 20px 70px #0004);\n}\n.dsh-subscription-manager::backdrop {\n background: var(--dsw-alias-bg-mask-1, #0006);\n backdrop-filter: var(--dsw-mask-blur, none);\n}\n${scope} h2 {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n}\n${scope} strong,\n${scope} .dsh-subscription-label {\n font-size: 13px;\n line-height: 1.5;\n font-weight: 500;\n}\n${scope} small,\n${scope} .dsh-subscription-hint,\n${scope} [role=\"status\"] {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n${scope} .dsh-subscription-error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-state-error-primary, #b42318);\n}\n${scope} .dsh-subscription-success {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-state-success-primary, #067647);\n}\n${scope} label:has(> input[type=\"checkbox\"]) {\n cursor: pointer;\n}\n${scope} input[type=\"checkbox\"] {\n accent-color: var(--dsw-alias-brand-primary);\n}\n/*\n * A model's on/off control is a switch, not a checkbox: it takes effect\n * immediately and its state is what matters, not a tick. Drawn from the theme\n * tokens with appearance: none, so it follows the host palette in both themes\n * instead of the platform's checkbox chrome.\n */\n${scope} input[type=\"checkbox\"].dsh-subscription-switch {\n appearance: none;\n -webkit-appearance: none;\n position: relative;\n box-sizing: border-box;\n flex-shrink: 0;\n width: 34px;\n height: 20px;\n margin: 0;\n border: 0.5px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-2));\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch::after {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary-foreground, #fff);\n box-shadow: 0 1px 2px #0003;\n transition: transform 120ms ease;\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch:checked {\n border-color: transparent;\n background: var(--dsw-alias-state-success-primary, var(--dsw-alias-brand-primary, #067647));\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch:checked::after {\n transform: translateX(14px);\n}\n${scope} input[type=\"checkbox\"].dsh-subscription-switch:disabled {\n opacity: 0.4;\n cursor: default;\n}\n${scope} button {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n min-height: 28px;\n padding: 0 10px;\n border: 0.5px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n${scope} button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-alias-bg-layer-3));\n}\n${scope} button:active:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active, var(--dsw-alias-bg-layer-3));\n}\n${scope} button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n/* A control that is present but should not read as a bordered button. */\n${scope} button.dsh-subscription-quiet {\n min-height: 22px;\n padding: 0 4px;\n border-color: transparent;\n color: var(--dsw-alias-label-secondary, var(--dsw-alias-label-primary));\n}\n/* The free-text field for a hand-added model id. */\n${scope} input.dsh-subscription-input {\n box-sizing: border-box;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 28px;\n padding: 0 10px;\n border: 0.5px solid var(--dsw-alias-border-l3, var(--dsw-alias-border-l2));\n border-radius: var(--dsw-radius-sm, 6px);\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n}\n${scope} input.dsh-subscription-input::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n${scope} input.dsh-subscription-input:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n/* A drag handle: no border or fill, so it reads as a grip rather than a button. */\n${scope} button.dsh-subscription-handle {\n min-height: 18px;\n height: 18px;\n padding: 0 2px;\n border-color: transparent;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-tertiary, var(--dsw-alias-label-secondary));\n font-size: 12px;\n line-height: 18px;\n cursor: grab;\n}\n${scope} button.dsh-subscription-handle:hover:not(:disabled) {\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n${scope} button.dsh-subscription-handle:active:not(:disabled) { cursor: grabbing; }\n/* Controls inside the manage dialog are one step larger, like a form footer. */\n${scope} .dsh-subscription-dialog-actions button {\n min-height: 34px;\n padding: 0 14px;\n border-radius: var(--dsw-radius-md, 8px);\n font-size: 13px;\n line-height: 1.5;\n}\n${scope} button.dsh-subscription-primary {\n border-color: transparent;\n background: var(--dsw-alias-button-primary-fill, var(--dsw-alias-label-primary));\n color: var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-layer-1));\n font-weight: 500;\n}\n${scope} button.dsh-subscription-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover, var(--dsw-alias-label-secondary));\n}\n${scope} button:focus-visible,\n${scope} input[type=\"checkbox\"]:focus-visible,\n${scope} .dsh-subscription-disclosure:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: 2px;\n}\n/* The catalog heading is a summary-style disclosure, not a bordered button. */\n${scope} .dsh-subscription-disclosure {\n border-color: transparent;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n padding: 0 4px;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary, var(--dsw-alias-label-primary));\n}\n${scope} .dsh-subscription-disclosure:hover:not(:disabled) {\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n/* A usage meter: the track, with the fill sized by the caller. */\n${scope} .dsh-subscription-meter {\n height: 6px;\n border-radius: 999px;\n /* The track must stay visible when nothing is spent — a 0 % window otherwise reads as\n \"no meter at all\", which is the opposite of \"nothing used\". The layer token falls\n back to the card's own colour on a host that does not define it, so the neutral is\n explicit and translucent, and the inset ring keeps the empty track visible on a\n light theme as well as a dark one. */\n background: #8a8f9826;\n box-shadow: inset 0 0 0 1px #8a8f9833;\n overflow: hidden;\n}\n${scope} .dsh-subscription-meter > span {\n display: block;\n height: 100%;\n background: var(--dsw-alias-state-success-primary, #067647);\n}\n${scope} .dsh-subscription-meter > span[data-exhausted=\"true\"] {\n background: var(--dsw-alias-state-error-primary, #b42318);\n}\n`\n\n/**\n * The composer usage chip's stylesheet.\n *\n * Separate from the page's, because the chip renders in the composer's own bar rather\n * than inside either page container — the page stylesheet is scoped to those\n * containers on purpose, so nothing here may leak into the shell.\n *\n * The chip inherits the bar's type and only draws a meter, so it sits beside the shell's\n * own stats pills without looking like a second design. The trigger is a bare control:\n * the host gives plugin markup no control classes, and a default `<button>` would\n * otherwise read as a raised chip. Every token carries a fallback for hosts that do not\n * define it.\n */\nexport const usageChipCss = `\n.dsh-subscription-usage-chip {\n position: relative;\n display: inline-flex;\n align-items: center;\n /* The bar is a centred flex row whose children are this slot's entries followed by the\n shell's context meter, and a list slot renders its entries as bare children (no\n wrapper). So the chip is itself a flex item: a higher order places it after the\n context meter, and no auto margin is set, so it sits immediately beside it rather\n than being pushed to the edge of the bar. */\n order: 1;\n min-width: 0;\n font-family: inherit;\n font-size: 12px;\n line-height: 1;\n}\n.dsh-subscription-usage-chip-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 2px 6px;\n border: 0;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font: inherit;\n cursor: pointer;\n}\n.dsh-subscription-usage-chip-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n}\n.dsh-subscription-usage-chip-trigger:focus-visible {\n outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);\n outline-offset: 1px;\n}\n.dsh-subscription-usage-chip-label {\n overflow: hidden;\n max-width: 6ch;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-subscription-usage-chip-cell {\n display: inline-flex;\n align-items: baseline;\n gap: 2px;\n}\n.dsh-subscription-usage-chip-window {\n opacity: 0.75;\n}\n.dsh-subscription-usage-chip-value {\n color: var(--dsw-alias-label-primary, #e8e8e8);\n font-variant-numeric: tabular-nums;\n}\n.dsh-subscription-usage-chip-account {\n display: flex;\n flex-direction: column;\n gap: 6px;\n /* A hairline between accounts, so two sign-ins read as two groups not one run. */\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-secondary, #ffffff14);\n}\n.dsh-subscription-usage-chip-account:first-of-type {\n padding-top: 0;\n border-top: 0;\n}\n.dsh-subscription-usage-chip-panel {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 180px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-secondary, #ffffff1f);\n border-radius: var(--dsw-radius-md, 10px);\n background: var(--dsw-alias-bg-layer-2, #26262a);\n box-shadow: var(--dsw-elevation-prominent, 0 12px 32px #0007);\n color: var(--dsw-alias-label-primary, #e8e8e8);\n white-space: nowrap;\n}\n.dsh-subscription-usage-chip-full {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n}\n.dsh-subscription-usage-chip-meter {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.dsh-subscription-usage-chip-meterHead {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n.dsh-subscription-usage-chip-detail {\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font-variant-numeric: tabular-nums;\n}\n.dsh-subscription-usage-chip-track {\n display: block;\n width: 100%;\n height: 5px;\n border-radius: 999px;\n /* As on the settings meter: an unspent window has to show its track, so the neutral and\n its ring are explicit rather than a layer token that may match the panel. */\n background: #8a8f9826;\n box-shadow: inset 0 0 0 1px #8a8f9833;\n overflow: hidden;\n}\n.dsh-subscription-usage-chip-fill {\n display: block;\n height: 100%;\n background: var(--dsw-alias-state-success-primary, #067647);\n}\n`\n\n/**\n * The composer's quick-mode button stylesheet.\n *\n * It renders in the composer's own control row — the same row as the model selector —\n * so it borrows that row's geometry: a compact, borderless control that only colours on\n * hover and when the mode is on. Tokens carry fallbacks for hosts that define none.\n */\nexport const fastModeCss = `\n.dsh-subscription-fast-mode {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 3px;\n border: 0;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n}\n.dsh-subscription-fast-mode:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n}\n/* A click has to look like one, even before the selection lands. */\n.dsh-subscription-fast-mode:active {\n background: var(--dsw-alias-interactive-bg-pressed, #ffffff2e);\n}\n.dsh-subscription-fast-mode[data-pending=\"true\"] {\n opacity: 0.7;\n cursor: progress;\n}\n/* A switch the host refused is marked rather than left looking merely off, so a failure\n is visible where it happened; the reason rides the title and the console. */\n.dsh-subscription-fast-mode[data-failed=\"true\"] {\n color: var(--dsw-alias-state-error-primary, #b42318);\n}\n.dsh-subscription-fast-mode:focus-visible {\n outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);\n outline-offset: 1px;\n}\n/* Off: the bolt is dim, so an available mode reads as an invitation, not a state. */\n.dsh-subscription-fast-mode[data-active=\"false\"] .dsh-subscription-fast-mode-bolt {\n opacity: 0.55;\n}\n/* On: the bolt takes the accent and glows, which is the only thing distinguishing the\n fast variant from its base — both answer with the same model name. */\n.dsh-subscription-fast-mode[data-active=\"true\"] .dsh-subscription-fast-mode-bolt {\n color: var(--dsw-alias-state-warning-primary, #f5a524);\n filter: drop-shadow(0 0 6px var(--dsw-alias-state-warning-primary, #f5a52455));\n}\n.dsh-subscription-fast-mode-reason {\n max-width: 14ch;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n}\n`\n","/**\n * Wire contract shared by the plugin's node half (the RPC routes) and its\n * browser half (the Settings page).\n *\n * Only this module crosses the boundary: the client bundle imports the types\n * and the channel name, and every call travels as one JSON envelope over a\n * `POST /api/<prefix><endpoint>` route. One status document describes every\n * provider route the plugin serves, so the page renders one card per entry.\n *\n * @module dsh-plugin-subscription-oauth/protocol\n */\n\n/** Prefix of every RPC method this plugin serves. */\nexport const RPC_PREFIX = 'subscription-oauth.'\n\n/** Endpoints the Settings page may call. */\nexport const RPC_ENDPOINTS = [\n 'status',\n 'login',\n 'cancel',\n 'logout',\n 'setDefault',\n 'refresh',\n 'discover',\n 'setModelVisible',\n 'setModelOrder',\n 'addModel',\n 'removeModel',\n 'usage',\n 'reload',\n 'auto-state',\n 'setAutoOn',\n 'deepseek-balance',\n 'ccswitch-balance',\n 'model-variants',\n] as const\n\n/** One endpoint name. */\nexport type RpcEndpoint = typeof RPC_ENDPOINTS[number]\n\n/** Client → host envelope. */\nexport interface RpcRequest {\n type: 'client-request'\n rpcId: string\n method: string\n payload: unknown\n}\n\n/** Host → client envelope, carrying either the value or the failure. */\nexport interface RpcResponse {\n type: 'server-response'\n rpcId: string\n result: RpcResult\n}\n\n/** Discriminated result inside {@link RpcResponse}. */\nexport type RpcResult =\n | { ok: true, value: unknown }\n | { ok: false, error: RpcError }\n\n/** Failure detail, with the provider-neutral code when one applies. */\nexport interface RpcError {\n code: string\n message: string\n /**\n * Carrier-owned structured detail. The Connection transport models a failure\n * as `{code, message, details}`, so an empty object travels here rather than\n * an absent field the client would have to repair.\n */\n details: Record<string, unknown>\n}\n\n/**\n * One provider-owned connection fact the manage dialog shows.\n *\n * The key is a stable identifier (`oauthHost`, `apiBase`, …) that the browser\n * half localizes; the host never sends user-facing prose.\n */\nexport interface DetailRow {\n key: string\n value: string\n}\n\n/** How the browser should present one pending login. */\nexport type LoginMode =\n /** Open the URL in a new tab; the redirect completes the login. */\n | 'open-url'\n /** Show the code (and a link) while the host polls the token endpoint. */\n | 'device-code'\n\n/** What the Settings page has to show to finish a login. */\nexport interface LoginPrompt {\n /** URL the user opens (or that was opened for them). */\n readonly url: string\n /** Code the user confirms in the browser; device flows only. */\n readonly userCode?: string\n /** Epoch milliseconds after which the attempt lapses. */\n readonly expiresAt: number\n readonly mode: LoginMode\n}\n\n/** One signed-in account as the page displays it. */\nexport interface AccountStatus {\n /** Account key the store is keyed by. */\n account: string\n /** Display label (email, user id, or the key's digest). */\n label: string\n /** Epoch milliseconds when the access token lapses. */\n expiresAt: number\n /** Whether the access token is already past its expiry. */\n expired: boolean\n /** Whether this account serves requests by default. */\n isDefault: boolean\n /**\n * Whether the provider refused this account's grant outright.\n *\n * A refresh answered with `invalid_grant` means the authorization is gone — the\n * stored token can never be renewed — so the page asks for a new sign-in rather\n * than reporting the account as connected.\n */\n needsLogin?: boolean\n}\n\n/** One selectable model, as the page lists it. */\nexport interface ModelSummary {\n id: string\n name: string\n description?: string\n contextWindow: number\n inputModalities?: readonly string[]\n defaultEffort?: string\n /** Whether the session model picker offers this model. */\n visible: boolean\n /**\n * Route that owns this model, when the listing spans routes.\n *\n * The \u0007uto provider mixes models from every route, so its rows name the\n * owning route; a single-route catalog leaves it out.\n */\n source?: string\n /** Whether the user added this id by hand, so it can be removed again. */\n userAdded?: boolean\n /**\n * Whether this entry is fixed at the head of the list.\n *\n * The synthetic `auto` entry follows the user's own order, so it cannot be\n * hidden or moved; the page renders it without those controls.\n */\n pinned?: boolean\n /** Fast/lightspeed variant wire id, when the provider reported one for this base. */\n fastVariant?: string\n}\n\n/** One metered allowance of a subscription plan. */\nexport interface UsageWindow {\n /** What the window meters, e.g. `weekly`, `5h`, `credits`. */\n label: string\n /** How much of the allowance is spent, when the provider states it. */\n usedPercent?: number\n /** Amount already spent, when the provider states an absolute figure. */\n used?: number\n /** Total allowance, when the provider states one. */\n limit?: number\n /** Epoch milliseconds when the window resets, when the provider states it. */\n resetsAt?: number\n}\n\n/** One account's plan and metered allowances. */\nexport interface ProviderUsage {\n /** Account the figures belong to. */\n account: string\n /** Plan name or tier, when the provider states one. */\n plan?: string\n /** Whether the plan renews on a date the provider states. */\n renewsAt?: number\n /** Metered allowances, most relevant first. */\n windows: readonly UsageWindow[]\n /** Epoch milliseconds these figures were read. */\n fetchedAt: number\n /**\n * Whether these figures are the route's substitute rather than its own accounting.\n *\n * A substitute is shown when the primary source cannot answer, and it is never\n * reused as if it were a reading: the next request asks again.\n */\n substitute?: boolean\n}\n\n/** Result of reading one route's usage. */\nexport interface UsageResult {\n provider: string\n /** Usage per account the endpoint could answer for. */\n usage: readonly ProviderUsage[]\n /** Why an account's usage is missing, when the read failed. */\n error?: string\n status: SubscriptionStatus\n}\n\n/** The full state of one provider route. */\nexport interface ProviderStatus {\n /** Provider route key, e.g. `kimi`. */\n provider: string\n displayName: string\n /** Where a user manages the underlying subscription. */\n codeUrl?: string\n /** Whether at least one account is stored. */\n loggedIn: boolean\n /**\n * Whether this route signs in at all.\n *\n * \u0007uto has no account of its own — it delegates to routes that do — so the\n * page hides the connect and manage controls for it.\n */\n connectable?: boolean\n /** Whether a login attempt is being polled. */\n busy: boolean\n /** The pending attempt, when `busy`. */\n pending?: LoginPrompt\n accounts: AccountStatus[]\n /** Account key serving requests, when one does. */\n activeAccount?: string\n /** Absolute path of the credential file, for support requests. */\n credentialFile: string\n /** Whether the live model list is consulted. */\n discovery: boolean\n /** Live catalog ids, when discovery has succeeded this session. */\n discovered?: readonly string[]\n models: readonly ModelSummary[]\n /** Model the picker preselects. */\n defaultModel?: string\n /** Last usage figures read for this route, when they were requested. */\n usage?: readonly ProviderUsage[]\n /** Why the last usage read failed, when it did. */\n usageError?: string\n /** Provider-owned connection facts shown in the manage dialog. */\n details: readonly DetailRow[]\n /** Last background failure (a login that could not complete). */\n lastError?: string\n}\n\n/** Every provider route's state, in display order. */\nexport interface SubscriptionStatus {\n providers: ProviderStatus[]\n}\n\n/** Result of starting a login. */\nexport interface LoginStart {\n prompt: LoginPrompt\n status: SubscriptionStatus\n}\n\n/** Result of a token refresh. */\nexport interface RefreshResult {\n account: string\n expiresAt: number\n status: SubscriptionStatus\n}\n\n/** Payload fields naming one provider route and optionally one account. */\nexport interface AccountRequest {\n provider: string\n account?: string\n}\n\n/** Payload of `setDefault`. */\nexport interface SetDefaultRequest {\n provider: string\n account: string\n}\n\n/** Payload of `discover`. */\nexport interface DiscoverRequest {\n provider: string\n /** Ask the endpoint for its live model list even when one is cached. */\n force?: boolean\n}\n\n/** Payload of `setModelVisible`. */\nexport interface SetModelVisibleRequest {\n provider: string\n /** Model id as the catalog reports it. */\n id: string\n /** Whether the session model picker should offer it. */\n visible: boolean\n}\n\n/** Payload of `addModel` and `removeModel`. */\nexport interface ModelIdRequest {\n provider: string\n /** Model id as the endpoint accepts it. */\n id: string\n}\n\n/** Payload of `setModelOrder`. */\nexport interface SetModelOrderRequest {\n provider: string\n /** Every catalog model id, in the order the user arranged them. */\n ids: readonly string[]\n}\n\n/** Payload naming only a provider route. */\nexport interface ProviderRequest {\n provider: string\n}\n\n/** Map an endpoint to its payload and value types. */\nexport interface RpcMap {\n status: { payload: undefined, value: SubscriptionStatus }\n login: { payload: AccountRequest, value: LoginStart }\n cancel: { payload: ProviderRequest, value: SubscriptionStatus }\n logout: { payload: AccountRequest, value: SubscriptionStatus }\n setDefault: { payload: SetDefaultRequest, value: SubscriptionStatus }\n refresh: { payload: AccountRequest, value: RefreshResult }\n discover: { payload: DiscoverRequest, value: SubscriptionStatus }\n setModelVisible: { payload: SetModelVisibleRequest, value: SubscriptionStatus }\n setModelOrder: { payload: SetModelOrderRequest, value: SubscriptionStatus }\n addModel: { payload: ModelIdRequest, value: SubscriptionStatus }\n removeModel: { payload: ModelIdRequest, value: SubscriptionStatus }\n usage: { payload: DiscoverRequest, value: UsageResult }\n reload: { payload: undefined, value: SubscriptionStatus }\n 'auto-state': { payload: undefined, value: AutoStateResult }\n setAutoOn: { payload: SetAutoOnRequest, value: AutoStateResult }\n 'deepseek-balance': { payload: DeepSeekBalanceRequest, value: DeepSeekBalanceResult }\n 'ccswitch-balance': { payload: CcSwitchBalanceRequest, value: CcSwitchBalanceResult }\n 'model-variants': { payload: ModelVariantsRequest, value: ModelVariantsResult }\n}\n\n/** One currency's wallet balance, as the platform reports it. */\nexport interface WalletBalance {\n /** ISO currency the balance is denominated in. */\n currency: 'CNY' | 'USD' | string\n /** Remaining balance, as a decimal string. */\n balance: string\n}\n\n/** The DeepSeek account's wallet balances, normalised from the host's account service. */\nexport interface AccountBalance {\n /** Normal (topped-up) wallets, one per currency. */\n wallets: readonly WalletBalance[]\n /** Bonus / promotional wallets, one per currency. */\n bonusWallets: readonly WalletBalance[]\n}\n\n/** Result of reading the DeepSeek account's balance through the host. */\nexport type BalanceResult =\n /** The account answered with its balances. */\n | { ok: true, balance: AccountBalance }\n /** The account is signed out, or the read failed. */\n | { ok: false }\n\n/** One model's fast and image variants, as discovery reported them. */\nexport interface ModelVariants {\n /** Fast/lightspeed variant wire id, when the provider reported one. */\n fast?: string\n /**\n * The base model the fast variant belongs to.\n *\n * Sent with `fast` so the picker button can switch back off when the selection is\n * already the fast variant, which is not itself a listed row.\n */\n fastBase?: string\n /**\n * The reasoning rung that selects the fast variant.\n *\n * Each provider names its own variant (xAI `lightspeed`, Kimi `highspeed`), so the\n * rung is the provider's word — the picker reads it, and the stream maps it back to\n * the variant's wire id. Defaults to `fast`.\n */\n fastRung?: string\n /** Image-generation model wire id, when the provider reported one. */\n image?: string\n}\n\n/** Result of `model-variants`: the variants for one model id. */\nexport type ModelVariantsResult = ModelVariants | undefined\n\n/** Payload of `model-variants`. */\nexport interface ModelVariantsRequest {\n provider: string\n id: string\n}\n\n/** Payload of `setAutoOn`: whether the `auto` provider is offered at all. */\nexport interface SetAutoOnRequest {\n enabled: boolean\n}\n\n/** Result of `setAutoOn`: the `auto` provider's live enabled state. */\nexport interface AutoStateResult {\n enabled: boolean\n}\n\n/** Payload of `deepseek-balance`. */\nexport interface DeepSeekBalanceRequest {\n /** Client metadata the host's account service expects. */\n version?: string\n locale?: string\n timezoneOffsetSeconds?: number\n}\n\n/** One currency's wallet, as the host's account service reports it. */\nexport interface DeepSeekWallet {\n /** ISO currency the balance is denominated in. */\n currency: string\n /** Remaining balance, as a decimal string. */\n balance: string\n}\n\n/**\n * Result of `deepseek-balance`.\n *\n * `undefined` means the account is signed out or the read failed, so a chip stays\n * silent rather than showing a wrong figure.\n */\nexport type DeepSeekBalanceResult =\n | {\n /** Normal (topped-up) wallets, one per currency. */\n wallets: readonly DeepSeekWallet[]\n /** Bonus / promotional wallets, one per currency. */\n bonusWallets: readonly DeepSeekWallet[]\n }\n | undefined\n\n/** Payload of `ccswitch-balance`. */\nexport interface CcSwitchBalanceRequest {\n /** The provider whose balance to read; the server maps it to its credential reference. */\n provider: string\n}\n\n/**\n * Result of `ccswitch-balance`.\n *\n * `undefined` means the provider did not answer with a balance — no key, a key it\n * rejects, or a body that is not the balance shape. A chip shows nothing rather than a\n * wrong figure, so this is deliberately not an error.\n */\nexport type CcSwitchBalanceResult =\n | {\n /** The currency the balance is denominated in. */\n currency: string\n /** The remaining balance, as a decimal string. */\n total: string\n /** Whether the account reports itself as usable. */\n available: boolean\n }\n | undefined\n","/**\n * Browser caller for the plugin's own RPC channel.\n *\n * The node half mounts one exact `POST /api/subscription-oauth.<endpoint>` route\n * per endpoint, so every call travels on the shared `/api` channel with a\n * prefixed endpoint name — the same wire the rest of the shell uses.\n *\n * @module dsh-plugin-subscription-oauth/client/subscriptions-rpc\n */\n\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\n\nimport { RPC_PREFIX } from '../protocol.js'\nimport type { CcSwitchBalanceResult, RpcEndpoint, RpcMap } from '../protocol.js'\n\n/** The shared channel the node half's routes are mounted on. */\nconst CHANNEL = '/api'\n\n/** A failed call: either the transport rejected or the endpoint answered `ok: false`. */\nexport class SubscriptionsRpcError extends Error {\n constructor(message: string, readonly code: string) {\n super(message)\n this.name = 'SubscriptionsRpcError'\n }\n}\n\n/** Human-readable message for any thrown value. */\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Call one endpoint, unwrapping the transport result. */\nexport type SubscriptionsCall = <K extends RpcEndpoint>(\n endpoint: K,\n payload: RpcMap[K]['payload'],\n) => Promise<RpcMap[K]['value']>\n\n/** A caller for one non-subscription provider's cc-switch balance. */\nexport interface CcSwitchBalanceCall {\n (provider: string): Promise<CcSwitchBalanceResult>\n}\n\n/**\n * Bind a caller to one connection's RPC transport.\n * @param rpc - the browser connection's RPC caller.\n * @returns a typed caller for this plugin's endpoints.\n */\nexport function createSubscriptionsCall(rpc: ConnectionHandle['rpc']): SubscriptionsCall {\n return async function call(endpoint, payload) {\n let result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>\n try {\n result = await rpc.call(CHANNEL, `${RPC_PREFIX}${endpoint}`, payload)\n } catch (error: unknown) {\n throw new SubscriptionsRpcError(messageOf(error), 'RPC_TRANSPORT')\n }\n if (!result.ok) throw new SubscriptionsRpcError(result.error.message, result.error.code)\n return result.value as never\n }\n}\n\n/**\n * The DeepSeek account's balance, as the plugin's node half reports it.\n *\n * The node half reads the host's `deepseekAccount` service (which owns the OAuth grant\n * and the platform endpoint) and serves the wallets here. It answers `undefined` when\n * the account is signed out or the read failed, so a chip simply does not render.\n */\nexport interface AccountBalance {\n /** Normal (topped-up) wallets, one per currency. */\n wallets: readonly { currency: string, balance: string }[]\n /** Bonus / promotional wallets, one per currency. */\n bonusWallets: readonly { currency: string, balance: string }[]\n}\n\n/** A narrow caller for the DeepSeek account's balance. */\nexport interface AccountBalanceCall {\n (): Promise<AccountBalance | undefined>\n}\n\n/**\n * Read the DeepSeek account's balance through the plugin's own RPC.\n *\n * The browser cannot call the host's account service directly — it sits behind the\n * host's Remote controller, not a `/api` fetch route — so the node half reads it and\n * serves it on the plugin's channel, the same one the rest of the chip's data travels.\n *\n * @param rpc - the browser connection's RPC caller.\n * @returns the balance, or undefined when there is nothing to show.\n */\nexport function createAccountBalanceCall(rpc: ConnectionHandle['rpc']): AccountBalanceCall {\n return async function call() {\n let result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>\n try {\n result = await rpc.call(CHANNEL, `${RPC_PREFIX}deepseek-balance`, {\n version: '1',\n locale: Intl.DateTimeFormat().resolvedOptions().locale ?? 'en-US',\n // East-positive whole seconds, as the host's account service expects (the same\n // value DSH's own account card sends).\n timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,\n })\n } catch (error: unknown) {\n throw new SubscriptionsRpcError(messageOf(error), 'RPC_TRANSPORT')\n }\n if (!result.ok) throw new SubscriptionsRpcError(result.error.message, result.error.code)\n return result.value as AccountBalance | undefined\n }\n}\n\n/**\n * Read a manually added provider's balance through the plugin's cc-switch endpoint.\n *\n * The caller names the credential reference the provider's profile uses (its\n * Read a non-subscription provider's balance through the plugin's cc-switch endpoint.\n *\n * The caller names the provider; the server maps it to the credential reference the\n * provider's profile names (`apiKeyEnv`) and resolves that to the API key. The result\n * is `undefined` when there is no key or the provider did not answer — never a wrong\n * figure.\n *\n * @param rpc - the browser connection's RPC caller.\n * @returns a narrow caller taking the provider id.\n */\nexport function createCcSwitchBalanceCall(rpc: ConnectionHandle['rpc']): CcSwitchBalanceCall {\n return async function call(provider: string) {\n let result: Awaited<ReturnType<ConnectionHandle['rpc']['call']>>\n try {\n result = await rpc.call(CHANNEL, `${RPC_PREFIX}ccswitch-balance`, { provider })\n } catch (error: unknown) {\n throw new SubscriptionsRpcError(messageOf(error), 'RPC_TRANSPORT')\n }\n if (!result.ok) throw new SubscriptionsRpcError(result.error.message, result.error.code)\n return result.value as CcSwitchBalanceResult\n }\n}\n","/**\n * Subscriptions settings page.\n *\n * One card per provider route — Kimi Code, Grok (X Premium), Google Antigravity\n * — describing that route's connection, accounts, and model catalog. Every fact\n * arrives from the node half's `subscription-oauth` RPC endpoints, so this\n * component holds no credential state of its own. The status poll runs only\n * while a login is pending, so an idle page performs no requests.\n *\n * Colors resolve through `--dsw-alias-*` design tokens, so the page follows the\n * active theme; every user-visible string goes through the locale-bound `t`.\n *\n * @module dsh-plugin-subscription-oauth/client/SubscriptionsSection\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { CSSProperties } from 'react'\n\nimport { en } from './locales.js'\nimport type { SubscriptionsKey } from './locales.js'\nimport { subscriptionsCss } from './styles.js'\nimport { SubscriptionsRpcError } from './subscriptions-rpc.js'\nimport type { SubscriptionsCall } from './subscriptions-rpc.js'\nimport type {\n AccountStatus,\n DetailRow,\n LoginPrompt,\n ProviderStatus,\n ProviderUsage,\n SubscriptionStatus,\n UsageWindow,\n} from '../protocol.js'\n/** Poll cadence while a login attempt is pending. */\nconst POLL_INTERVAL_MS = 2000\n\n/**\n * How often an open page re-reads every signed-in route's allowance.\n *\n * Slow on purpose: it is fast enough that a figure shown on screen is current, and\n * slow enough that leaving the page open costs one small request a minute per\n * signed-in provider.\n */\nconst USAGE_POLL_INTERVAL_MS = 60_000\n\n/**\n * Disclosures start folded, every time the page is opened.\n *\n * Nothing is remembered between visits on purpose: the section is a list of\n * providers first, and a card left open last time should not decide what this\n * visit looks like. A fold is a gesture within one visit, not a stored setting.\n */\n\n/** Horizontal room the page takes inside the settings panel. */\nconst SECTION_WIDTH = 560\n\n/** What the node half injects into this section. */\nexport interface SubscriptionsSectionInjected {\n /** Typed caller for the plugin's RPC endpoints. */\n call: SubscriptionsCall\n /** Translate a key of this plugin's settings namespace. */\n t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string\n}\n\n/** Component props: the shell supplies `close`, the plugin supplies the rest. */\nexport type SubscriptionsSectionProps = Partial<SubscriptionsSectionInjected> & {\n /** Close the settings panel (owned by the settings shell). */\n close?: () => void\n}\n\n/** One card's transient message. */\ninterface CardMessage {\n tone: 'error' | 'success'\n text: string\n}\n\n/** Minimal translator used when the locale seat is absent (standalone render). */\nfunction fallbackTranslate(key: SubscriptionsKey, params?: Record<string, unknown>): string {\n const template: string = en[key]\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n}\n\n/** Human-readable message for any thrown value. */\nfunction messageOf(error: unknown): string {\n if (error instanceof SubscriptionsRpcError) return error.message\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Local time for one epoch-millisecond instant. */\nfunction formatTime(at: number): string {\n try {\n return new Date(at).toLocaleString()\n } catch {\n return String(at)\n }\n}\n\n/** How far ahead a reset is still stated as hours rather than a date. */\nconst RESET_RELATIVE_MS = 24 * 60 * 60 * 1000\n\n/**\n * One allowance reset, in the form a reader acts on.\n *\n * How far off it is decides how it is said: minutes inside the hour, hours inside\n * the day, and the date beyond that — \"137 小时后\" would be arithmetic rather than\n * information, and a window that comes back in forty minutes is better described in\n * minutes than as \"1 小时后\". Every route's windows go through here, so all of them\n * read the same way.\n *\n * @param at - epoch milliseconds the window resets.\n * @param t - translator, so the wording follows the page language.\n * @returns the reset text.\n */\nfunction formatReset(\n at: number,\n t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string,\n): string {\n const remaining = at - Date.now()\n if (remaining <= 0) return t('usageResetDue')\n if (remaining >= RESET_RELATIVE_MS) return t('usageReset', { time: formatTime(at) })\n // Rounded, and never below one: \"0 分钟后重置\" would read as a bug rather than as\n // \"any moment now\".\n if (remaining < 60 * 60 * 1000) {\n return t('usageResetsInMinutes', { minutes: Math.max(1, Math.round(remaining / 60_000)) })\n }\n return t('usageResetsInHours', { hours: Math.max(1, Math.round(remaining / 3_600_000)) })\n}\n\n/** Compact token count for a context window. */\nfunction formatTokens(tokens: number): string {\n if (tokens >= 1_000_000) return `${String(Math.round(tokens / 100_000) / 10)}M`\n if (tokens >= 1_000) return `${String(Math.round(tokens / 1_000))}K`\n return String(tokens)\n}\n\n/**\n * Routes worth asking for allowances.\n *\n * A route with no account of its own — `auto` — reports itself as signed in so its\n * card reads as usable, but it holds no allowance to read: asking it every minute\n * spends a request to be told nothing. The routes it stands in for are each read\n * on their own.\n *\n * @param providers - the routes in the current status.\n * @returns the route keys to read usage for.\n */\nfunction usageTargets(providers: readonly ProviderStatus[]): string[] {\n return providers\n .filter((provider) => provider.loggedIn && provider.connectable !== false)\n .map((provider) => provider.provider)\n}\n\nconst styles: Record<string, CSSProperties> = {\n section: {\n display: 'flex', flexDirection: 'column', gap: 12, maxWidth: SECTION_WIDTH,\n color: 'var(--dsw-alias-label-primary)',\n },\n sectionHeader: { display: 'flex', flexDirection: 'column', gap: 4, marginBottom: 2 },\n heading: { margin: 0, fontSize: 18, lineHeight: '26px', fontWeight: 600, color: 'var(--dsw-alias-label-primary)' },\n intro: { margin: 0, color: 'var(--dsw-alias-label-tertiary)', fontSize: 13, lineHeight: '20px', maxWidth: 560 },\n card: {\n border: '0.5px solid var(--dsw-alias-settings-card-stroke, var(--dsw-alias-border-l4))',\n borderRadius: 'var(--dsw-radius-xl, 16px)',\n background: 'var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2))',\n padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 8,\n transition: 'border-color 120ms ease, background 120ms ease',\n },\n cardOpen: {\n borderColor: 'var(--dsw-alias-border-l2, var(--dsw-alias-border-l3))',\n background: 'var(--dsw-alias-settings-card-fill-hover, var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2)))',\n },\n cardHeader: { display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer', userSelect: 'none' },\n /** Everything a folded card hides. */\n cardBody: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 2 },\n /** The one-line state a folded card shows in place of its body. */\n cardSummary: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n /** The status indicator: filled when live, a hollow ring when not. */\n dot: {\n width: 8, height: 8, borderRadius: '50%', flexShrink: 0,\n border: '1.5px solid var(--dsw-alias-label-dimmed)',\n background: 'transparent',\n },\n dotLive: {\n borderColor: 'var(--dsw-alias-state-success-primary, #067647)',\n background: 'var(--dsw-alias-state-success-primary, #067647)',\n },\n dotBusy: {\n borderColor: 'var(--dsw-alias-state-warn-primary, #f5a524)',\n background: 'var(--dsw-alias-state-warn-primary, #f5a524)',\n },\n name: { fontWeight: 600, fontSize: 14, lineHeight: '22px', color: 'var(--dsw-alias-label-primary)' },\n statusLine: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n errorLine: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },\n successLine: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-success-primary)' },\n actions: { display: 'flex', gap: 8, marginTop: 4, alignItems: 'center', flexWrap: 'wrap' },\n accountRow: {\n display: 'flex', flexDirection: 'column', gap: 6,\n border: '0.5px solid var(--dsw-alias-border-l2)', borderRadius: 'var(--dsw-radius-lg, 12px)',\n padding: '10px 12px', background: 'var(--dsw-alias-bg-layer-1)',\n },\n accountHeader: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },\n starButton: {\n border: 'none', padding: 0, width: 20, background: 'transparent',\n font: 'inherit', fontSize: 14, lineHeight: '20px', cursor: 'pointer',\n color: 'var(--dsw-alias-state-warn-label)',\n },\n accountName: { fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)', userSelect: 'all' },\n badge: {\n fontSize: 10, lineHeight: '14px', padding: '1px 7px', borderRadius: 999, fontWeight: 500,\n background: 'var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-1))',\n border: '0.5px solid var(--dsw-alias-border-l2)',\n color: 'var(--dsw-alias-label-secondary)',\n },\n badgeDefault: {\n fontSize: 10, lineHeight: '14px', padding: '1px 7px', borderRadius: 999, fontWeight: 500,\n background: 'var(--dsw-alias-state-success-primary, #067647)',\n color: 'var(--dsw-alias-label-primary-foreground, #fff)',\n },\n badgeWarn: {\n fontSize: 10, lineHeight: '14px', padding: '1px 7px', borderRadius: 999, fontWeight: 500,\n background: 'var(--dsw-alias-state-warn-primary, #f5a524)',\n color: 'var(--dsw-alias-label-primary-foreground, #fff)',\n },\n pushRight: { marginLeft: 'auto', flexShrink: 0 },\n catalog: {\n display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4,\n borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: 8,\n },\n catalogHeader: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },\n catalogTitle: { fontSize: 12, lineHeight: '18px', fontWeight: 500, color: 'var(--dsw-alias-label-secondary)' },\n catalogMeta: { fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n /**\n * Button surface geometry lives in the scoped stylesheet; only the layout a\n * rule cannot express stays inline.\n */\n pill: {},\n pillSmall: {},\n /** The catalog heading: a summary-style disclosure, not a bordered control. */\n disclosure: {\n display: 'inline-flex', alignItems: 'center', gap: 4, margin: 0,\n },\n chevron: { fontSize: 10, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },\n modelList: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 2 },\n /** One model line: a visibility toggle and the model's own metadata. */\n modelRowGrow: { display: 'flex', alignItems: 'flex-start', gap: 8, flex: '1 1 auto', minWidth: 0 },\n /** The pinned `auto` line: listed, switchable, and never part of its own chain. */\n autoRow: {\n display: 'flex', alignItems: 'flex-start', gap: 8, cursor: 'pointer',\n fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)',\n },\n /** The models `auto` will walk, indented under it. */\n chain: {\n display: 'flex', flexDirection: 'column', gap: 3,\n margin: '2px 0 4px 26px', paddingLeft: 8,\n borderLeft: '0.5px solid var(--dsw-alias-border-l2)',\n },\n chainTitle: { fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)' },\n /** The add-a-model-id row: the field, then confirm and cancel. */\n addRow: { display: 'flex', alignItems: 'center', gap: 6, marginTop: 2 },\n addInput: {},\n /** The remove control on a hand-added row: a bare glyph beside the row. */\n removeButton: { flexShrink: 0, alignSelf: 'center', padding: '0 6px' },\n /** One draggable chain row: the grip, then the remove-from-auto switch. */\n chainRowWrap: { display: 'flex', alignItems: 'center', gap: 4 },\n chainRow: {\n display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer', flex: '1 1 auto',\n fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)',\n },\n usage: {\n display: 'flex', flexDirection: 'column', gap: 8, marginTop: 2,\n borderTop: '0.5px solid var(--dsw-alias-border-l2)', paddingTop: 10,\n },\n usageHeader: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' },\n usageRow: { display: 'flex', flexDirection: 'column', gap: 4 },\n usageLine: { display: 'flex', alignItems: 'baseline', gap: 8, justifyContent: 'space-between' },\n usageMeter: {\n height: 6, borderRadius: 999, background: 'var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-2))',\n overflow: 'hidden', boxShadow: 'inset 0 0 0 1px #8a8f9833',\n },\n usageAccount: {\n display: 'flex', flexDirection: 'column', gap: 6,\n padding: '8px 10px', borderRadius: 'var(--dsw-radius-md, 10px)',\n background: 'var(--dsw-alias-bg-layer-1)', border: '0.5px solid var(--dsw-alias-border-l2)',\n },\n /** The metered windows under one account row: no border, just the meters. */\n usageInline: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 },\n /** The auto provider's on/off row: a label and its switch. */\n autoSwitchRow: { display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' },\n modelRow: {\n display: 'flex', alignItems: 'flex-start', gap: 8,\n cursor: 'pointer', fontSize: 13, lineHeight: '20px',\n color: 'var(--dsw-alias-label-primary)',\n },\n /** A hidden model stays listed and editable, but reads as inactive. */\n modelRowHidden: {\n display: 'flex', alignItems: 'flex-start', gap: 8,\n cursor: 'pointer', fontSize: 13, lineHeight: '20px',\n color: 'var(--dsw-alias-label-dimmed)',\n },\n modelBody: { display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0 },\n modelName: { fontSize: 13, lineHeight: '20px' },\n modelMeta: { fontSize: 11, lineHeight: '16px', color: 'var(--dsw-alias-label-tertiary)', userSelect: 'all' },\n device: {\n marginTop: 4, display: 'flex', flexDirection: 'column', gap: 6,\n border: '0.5px solid var(--dsw-alias-border-l2)', borderRadius: 'var(--dsw-radius-lg, 16px)',\n padding: '10px 12px', background: 'var(--dsw-alias-bg-layer-1)',\n },\n deviceTitle: { fontWeight: 500, fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)' },\n deviceCode: {\n fontFamily: 'monospace', fontSize: 18, lineHeight: '24px', letterSpacing: 2,\n color: 'var(--dsw-alias-label-primary)', userSelect: 'all',\n },\n details: {\n display: 'flex', flexDirection: 'column', gap: 4,\n fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)',\n },\n detailRow: { display: 'flex', gap: 8, justifyContent: 'space-between', alignItems: 'baseline' },\n detailValue: { userSelect: 'all', overflowWrap: 'anywhere', textAlign: 'right' },\n /** Dialog layout only: its surface comes from `.dsh-subscription-manager`. */\n modal: {\n width: 440, maxWidth: '100%', maxHeight: '85vh', overflowY: 'auto',\n boxSizing: 'border-box', display: 'flex', flexDirection: 'column', gap: 10,\n padding: '16px 18px', margin: 'auto',\n },\n modalHeader: { display: 'flex', alignItems: 'center', gap: 8 },\n modalTitle: { fontWeight: 500, fontSize: 16, lineHeight: '24px', color: 'var(--dsw-alias-label-primary)' },\n modalActions: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 2 },\n divider: { borderTop: '0.5px solid var(--dsw-alias-border-l2)' },\n}\n\n/** Status dot style for one provider's connection state. */\nfunction dotStyle(status: ProviderStatus): CSSProperties {\n if (status.busy) return styles.dotBusy\n if (status.loggedIn) return styles.dotLive\n return {}\n}\n\n/** Localized label for one provider-owned detail row. */\nfunction detailLabel(t: SubscriptionsSectionInjected['t'], row: DetailRow): string {\n const key = `detail_${row.key}` as SubscriptionsKey\n return key in en ? t(key) : row.key\n}\n\n/** Localized label for one metered window the provider states. */\nfunction windowLabel(t: SubscriptionsSectionInjected['t'], window: UsageWindow): string {\n const key = `usage_${window.label}` as SubscriptionsKey\n return key in en ? t(key) : window.label\n}\n\n/** How much of one window is spent, as a display string. */\nfunction windowAmount(window: UsageWindow): string {\n if (window.usedPercent !== undefined) return `${String(Math.round(window.usedPercent))}%`\n if (window.used !== undefined && window.limit !== undefined) return `${String(window.used)}/${String(window.limit)}`\n return '—'\n}\n\n/** One account's metered windows, rendered under its account row. */\nfunction AccountUsage(props: {\n entry: ProviderUsage | undefined\n t: SubscriptionsSectionInjected['t']\n}) {\n const { entry, t } = props\n if (entry === undefined) return null\n return (\n <div style={styles.usageInline}>\n {entry.windows.map((window) => (\n <div key={`${entry.account}-${window.label}`} style={styles.usageRow}>\n <div style={styles.usageLine}>\n <span style={styles.modelName}>\n {windowLabel(t, window)}\n {' · '}\n {windowAmount(window)}\n </span>\n <span style={styles.modelMeta}>\n {window.resetsAt === undefined ? '' : formatReset(window.resetsAt, t)}\n </span>\n </div>\n {window.usedPercent !== undefined && (\n <div className=\"dsh-subscription-meter\">\n <span\n style={{ width: `${String(Math.min(100, Math.max(0, window.usedPercent)))}%` }}\n data-exhausted={window.usedPercent >= 90 ? 'true' : 'false'}\n />\n </div>\n )}\n </div>\n ))}\n {entry.windows.length === 0 && (\n <p style={styles.statusLine}>{t('usageNone')}</p>\n )}\n </div>\n )\n}\n\n/** The plan-and-usage block for the whole route. */\nfunction UsageBlock(props: {\n usage: readonly ProviderUsage[]\n error?: string\n t: SubscriptionsSectionInjected['t']\n busyAction: boolean\n onRefresh(): void\n}) {\n const { usage, error, t, busyAction, onRefresh } = props\n return (\n <div style={styles.usage}>\n <div style={styles.usageHeader}>\n <span style={styles.catalogTitle}>{t('usageTitle')}</span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n disabled={busyAction}\n onClick={onRefresh}\n >\n {busyAction ? t('refreshing') : t('usageRefresh')}\n </button>\n </div>\n {error !== undefined && <p className=\"dsh-subscription-error\">{t('usageError', { message: error })}</p>}\n {usage.length === 0 && error === undefined && <p style={styles.statusLine}>{t('usageNone')}</p>}\n {usage.map((entry) => (\n <div key={entry.account} style={styles.usageAccount}>\n <div style={styles.usageLine}>\n <span style={styles.modelMeta}>\n {entry.plan === undefined ? entry.account : t('usagePlan', { plan: entry.plan })}\n {entry.renewsAt === undefined ? '' : ` · ${t('usageRenews', { time: formatTime(entry.renewsAt) })}`}\n </span>\n <span style={styles.modelMeta}>{t('usageFetched', { time: formatTime(entry.fetchedAt) })}</span>\n </div>\n {entry.windows.map((window) => (\n <div key={`${entry.account}-${window.label}`} style={styles.usageRow}>\n <div style={styles.usageLine}>\n <span style={styles.modelName}>\n {windowLabel(t, window)}\n {' · '}\n {windowAmount(window)}\n </span>\n <span style={styles.modelMeta}>\n {window.resetsAt === undefined ? '' : formatReset(window.resetsAt, t)}\n </span>\n </div>\n {window.usedPercent !== undefined && (\n <div className=\"dsh-subscription-meter\">\n <span\n style={{ width: `${String(Math.min(100, Math.max(0, window.usedPercent)))}%` }}\n data-exhausted={window.usedPercent >= 90 ? 'true' : 'false'}\n />\n </div>\n )}\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n}\n\n/** The pending-login block: a device code, or a link to the sign-in page. */\nfunction PendingLogin(props: {\n prompt: LoginPrompt\n t: SubscriptionsSectionInjected['t']\n copied: boolean\n onCopy(code: string): void\n}) {\n const { prompt, t, copied, onCopy } = props\n return (\n <div style={styles.device}>\n <span style={styles.deviceTitle}>\n {prompt.mode === 'device-code' ? t('deviceTitle') : t('openLogin')}\n </span>\n <span style={styles.statusLine}>\n {prompt.mode === 'device-code' ? t('devicePrompt') : t('openLoginHint')}\n </span>\n {prompt.userCode !== undefined && <span style={styles.deviceCode}>{prompt.userCode}</span>}\n <span style={styles.statusLine}>{t('deviceExpires', { time: formatTime(prompt.expiresAt) })}</span>\n <div style={styles.actions}>\n <button\n type=\"button\"\n \n onClick={() => { window.open(prompt.url, '_blank', 'noopener') }}\n >\n {prompt.mode === 'device-code' ? t('deviceOpen') : t('openLogin')}\n </button>\n {prompt.userCode !== undefined && (\n <button type=\"button\" onClick={() => { onCopy(prompt.userCode as string) }}>\n {copied ? t('deviceCopied') : t('deviceCopy')}\n </button>\n )}\n </div>\n </div>\n )\n}\n\n/** One provider's card. */\nfunction ProviderCard(props: {\n status: ProviderStatus\n t: SubscriptionsSectionInjected['t']\n busyAction: boolean\n message?: CardMessage\n onLogin(): void\n onCancel(): void\n onLogout(account: string): void\n onSetDefault(account: string): void\n onRefreshToken(account: string): void\n onRefreshModels(): void\n onSetModelVisible(id: string, visible: boolean): void\n /** Persist the chain order the user dropped into place. */\n onSetModelOrder(ids: readonly string[]): void\n /** Publish a model id the catalog does not know yet. */\n onAddModel(id: string): void\n /** Forget a hand-added model id. */\n onRemoveModel(id: string): void\n onRefreshUsage(): void\n onReload(): void\n /** Whether the `auto` provider is offered; flips it. Only the auto card sets these. */\n autoOn?: boolean\n onSetAutoOn?(enabled: boolean): void\n}) {\n const { status, t, busyAction, message } = props\n const [copied, setCopied] = useState(false)\n // Both disclosures open folded, on every visit: the choice is a gesture, not a\n // stored setting. See the note beside the interval constants.\n const [modelsOpen, setModelsOpen] = useState(false)\n /** Whether this provider's whole card is unfolded. */\n const [cardOpen, setCardOpen] = useState(false)\n const [manageOpen, setManageOpen] = useState(false)\n const mounted = useRef(true)\n const dialog = useRef<HTMLDialogElement>(null)\n\n useEffect(() => {\n mounted.current = true\n return () => { mounted.current = false }\n }, [])\n\n // The dialog is a native modal, so the browser owns focus trapping and Esc.\n useEffect(() => {\n const element = dialog.current\n if (element === null || typeof element.showModal !== 'function') return\n if (manageOpen && !element.open) element.showModal()\n if (!manageOpen && element.open) element.close()\n }, [manageOpen])\n\n const copyCode = useCallback(async (code: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(code)\n if (mounted.current) {\n setCopied(true)\n setTimeout(() => { if (mounted.current) setCopied(false) }, 2000)\n }\n } catch {\n // A clipboard refusal is not worth an error banner: the code is on screen\n // and selectable, which is the fallback the user already has.\n }\n }, [])\n\n const toggleModelsOpen = useCallback((): void => {\n setModelsOpen((open) => !open)\n }, [])\n\n /** Fold or unfold this provider's card. */\n const toggleCardOpen = useCallback((): void => {\n setCardOpen((open) => !open)\n }, [])\n\n const accounts: readonly AccountStatus[] = status.accounts\n const models = status.models\n /** Whether this route signs in at all; the auto provider does not. */\n const connectable = status.connectable !== false\n const hiddenCount = models.filter((model) => !model.visible).length\n /** Whether the add-a-model-id field is open, and what has been typed. */\n const [adding, setAdding] = useState(false)\n const [draft, setDraft] = useState('')\n /** The row being dragged, so it can be dimmed while it moves. */\n const [dragFrom, setDragFrom] = useState<number | undefined>(undefined)\n /**\n * The dragged row, mirrored in a ref.\n *\n * The drop is handled by an ancestor whose handler was attached in an earlier\n * render, so it must not depend on state that was set during the drag.\n */\n const dragFromRef = useRef<number | undefined>(undefined)\n /** The row the pointer is currently over, so a drop lands where the user aims. */\n const dropTargetRef = useRef<number | undefined>(undefined)\n /** The same row in state, purely so it can be highlighted while dragging. */\n const [dropTarget, setDropTarget] = useState<number | undefined>(undefined)\n\n /**\n * Commit one dropped arrangement.\n *\n * The node half takes the full chain in its new order and appends whatever the\n * page left out, so sending just these ids is enough — and a model hidden in\n * another window is never dropped from the stored rank by a stale page.\n */\n const commitOrder = useCallback((ids: readonly string[]): void => {\n props.onSetModelOrder(ids)\n }, [props])\n\n /** Drag-and-drop plus its keyboard equivalent on the drag handle. */\n const drag = {\n start: (event: React.DragEvent, index: number): void => {\n setDragFrom(index)\n dragFromRef.current = index\n event.dataTransfer.effectAllowed = 'move'\n // Firefox refuses to start a drag without payload.\n event.dataTransfer.setData('text/plain', String(index))\n },\n over: (event: React.DragEvent, _index: number): void => {\n // A drop is only possible on a target that cancels the default.\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n const container = event.currentTarget as HTMLElement\n const to = indexAt(container, event.clientY)\n dropTargetRef.current = to\n if (to !== undefined && to !== dropTarget) setDropTarget(to)\n },\n drop: (event: React.DragEvent, index: number): void => {\n event.preventDefault()\n // The target comes from the last `dragover`, not from this event's own\n // coordinates: `drop` is not guaranteed to carry the pointer position, and a\n // zero there silently collapsed every drop onto the first row.\n const to = dropTargetRef.current ?? indexAt(event.currentTarget as HTMLElement, event.clientY) ?? index\n move(dragFromRef.current, to)\n dragFromRef.current = undefined\n dropTargetRef.current = undefined\n setDropTarget(undefined)\n setDragFrom(undefined)\n },\n /** A drop anywhere in the list lands on the row under the pointer. */\n dropAt: (event: React.DragEvent): void => {\n event.preventDefault()\n const container = event.currentTarget as HTMLElement\n const to = dropTargetRef.current ?? indexAt(container, event.clientY)\n if (to !== undefined) move(dragFromRef.current, to)\n dragFromRef.current = undefined\n dropTargetRef.current = undefined\n setDropTarget(undefined)\n setDragFrom(undefined)\n },\n end: (): void => {\n dragFromRef.current = undefined\n dropTargetRef.current = undefined\n setDropTarget(undefined)\n setDragFrom(undefined)\n },\n key: (event: React.KeyboardEvent, index: number): void => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n event.preventDefault()\n move(index, index + (event.key === 'ArrowUp' ? -1 : 1))\n },\n }\n\n /** Move one model row to another position and persist the result. */\n function move(from: number | undefined, to: number): void {\n if (from === undefined || from === to) return\n if (from < 0 || to < 0 || from >= models.length || to >= models.length) return\n const ids = models.map((model) => model.id)\n const [moved] = ids.splice(from, 1)\n if (moved === undefined) return\n ids.splice(to, 0, moved)\n commitOrder(ids)\n }\n\n /**\n * The row index a drop at this pointer position lands on.\n *\n * The drop is handled on the whole list rather than per row: rows are tall and\n * separated by gaps, so a drop between two of them hits no target at all and\n * silently does nothing. Comparing the pointer against each row's midpoint also\n * makes the gesture match what the user sees.\n */\n function indexAt(container: HTMLElement, clientY: number): number | undefined {\n const rows = Array.from(container.querySelectorAll<HTMLElement>('[data-model-index]'))\n if (rows.length === 0) return undefined\n let closest = 0\n let best = Number.POSITIVE_INFINITY\n for (const row of rows) {\n const box = row.getBoundingClientRect()\n const distance = Math.abs(clientY - (box.top + box.height / 2))\n if (distance >= best) continue\n best = distance\n closest = Number(row.dataset.modelIndex)\n }\n return closest\n }\n const busy = status.busy\n const liveCatalog = (status.discovered?.length ?? 0) > 0\n /**\n * What a folded card says about itself.\n *\n * The connection is the reason to open a card, so the folded row carries it;\n * `auto` has no account and counts the models it will actually try instead.\n */\n const summaryLine = !connectable\n ? t('statusAuto', { count: models.filter((model) => model.visible).length })\n : busy\n ? t('statusBusy')\n : status.loggedIn\n ? t('statusConnected', { count: accounts.length })\n : t('statusSignedOut')\n\n return (\n <div style={cardOpen ? { ...styles.card, ...styles.cardOpen } : styles.card}>\n {/*\n The header is the disclosure: folded, the page is a list of providers, and\n the whole card is one row with its state and an edit affordance. The\n status dot stays visible either way, so a folded card still reports\n whether the route is signed in.\n */}\n <div\n style={styles.cardHeader}\n role=\"button\"\n tabIndex={0}\n aria-expanded={cardOpen}\n onClick={toggleCardOpen}\n onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggleCardOpen() } }}\n >\n <span style={{ ...styles.dot, ...dotStyle(status) }} aria-hidden=\"true\" />\n <span style={styles.name}>{status.displayName}</span>\n {status.loggedIn && <span style={styles.badgeDefault}>{t('badgeLive')}</span>}\n {/*\n The state stays in the header whether the card is open or folded. The\n body used to repeat the same sentence as its first line, so expanding a\n card made that text appear to jump down a line — the one thing the reader\n is watching.\n */}\n <span style={styles.cardSummary}>{summaryLine}</span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n aria-expanded={cardOpen}\n aria-controls={`dsh-subscription-card-${status.provider}`}\n onClick={(event) => { event.stopPropagation(); toggleCardOpen() }}\n >\n {cardOpen ? t('collapse') : t('edit')}\n </button>\n </div>\n <div\n id={`dsh-subscription-card-${status.provider}`}\n style={cardOpen ? styles.cardBody : { ...styles.cardBody, display: 'none' }}\n >\n {connectable && !status.loggedIn && !busy && <p style={styles.statusLine}>{t('statusSignIn')}</p>}\n {status.lastError !== undefined && (\n <p className=\"dsh-subscription-error\">{t('lastError', { message: status.lastError })}</p>\n )}\n {message?.tone === 'error' && <p className=\"dsh-subscription-error\">{message.text}</p>}\n {message?.tone === 'success' && <p className=\"dsh-subscription-success\">{message.text}</p>}\n\n {/* The auto provider's master switch: off keeps it out of the picker entirely. */}\n {status.provider === 'auto' && props.onSetAutoOn !== undefined && (\n <label style={styles.autoSwitchRow}>\n <span style={styles.modelName}>{t('autoSwitchLabel')}</span>\n <input\n type=\"checkbox\"\n className=\"dsh-subscription-switch\"\n checked={props.autoOn ?? true}\n disabled={busyAction}\n onChange={(event) => { props.onSetAutoOn?.(event.target.checked) }}\n />\n </label>\n )}\n\n {accounts.map((account) => {\n const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account)\n return (\n <div key={account.account} style={styles.accountRow}>\n <div style={styles.accountHeader}>\n <button\n type=\"button\"\n style={styles.starButton}\n title={account.isDefault ? t('defaultBadge') : t('makeDefault')}\n aria-label={account.isDefault ? t('defaultBadge') : t('makeDefault')}\n disabled={busyAction || account.isDefault}\n onClick={() => { props.onSetDefault(account.account) }}\n >\n {account.isDefault ? '★' : '☆'}\n </button>\n <span style={styles.accountName}>{account.label}</span>\n {account.isDefault && <span style={styles.badgeDefault}>{t('defaultBadge')}</span>}\n {/* The provider refused this grant: only a new sign-in helps. */}\n {account.needsLogin === true && (\n <span style={styles.badgeWarn} title={t('accountNeedsLoginHint')}>{t('accountNeedsLogin')}</span>\n )}\n {usageEntry?.plan !== undefined && (\n <span style={styles.badge}>{t('usagePlan', { plan: usageEntry.plan })}</span>\n )}\n <span style={styles.statusLine}>\n {account.expired\n ? t('accountExpired')\n : t('accountExpires', { time: formatTime(account.expiresAt) })}\n </span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n disabled={busyAction}\n onClick={() => { props.onLogout(account.account) }}\n >\n {t('logout')}\n </button>\n </div>\n <AccountUsage entry={usageEntry} t={t} />\n </div>\n )\n })}\n\n {/* A signed-out route has nothing to configure here, and listing models it\n cannot call would only invite picker entries that fail. */}\n {status.loggedIn && (\n <div style={styles.catalog}>\n <div style={styles.catalogHeader}>\n <button\n type=\"button\"\n className=\"dsh-subscription-disclosure\"\n style={styles.disclosure}\n aria-expanded={modelsOpen}\n aria-controls={`dsh-subscription-models-${status.provider}`}\n onClick={toggleModelsOpen}\n >\n <span style={styles.chevron} aria-hidden=\"true\">{modelsOpen ? '▾' : '▸'}</span>\n <span style={styles.catalogTitle}>{t('modelsTitle')}</span>\n </button>\n <span style={styles.catalogMeta}>\n {t('modelsCount', { count: models.length })}\n {' · '}\n {liveCatalog ? t('modelsLive') : t('modelsBuiltIn')}\n {hiddenCount === 0 ? '' : ` ${t('modelsHiddenCount', { count: hiddenCount })}`}\n </span>\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.pushRight}\n disabled={busyAction}\n onClick={props.onRefreshModels}\n >\n {busyAction ? t('refreshing') : t('refresh')}\n </button>\n {/* A model the endpoint serves but this build has never heard of: the\n id is published as a plain route, so a new release is usable at once. */}\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n disabled={busyAction || !connectable}\n title={t('modelsAddHint')}\n onClick={() => { setAdding((open) => !open) }}\n >\n {t('modelsAdd')}\n </button>\n </div>\n {adding && (\n <form\n style={styles.addRow}\n onSubmit={(event) => {\n event.preventDefault()\n const id = draft.trim()\n if (id.length === 0) return\n props.onAddModel(id)\n setDraft('')\n setAdding(false)\n }}\n >\n <input\n type=\"text\"\n className=\"dsh-subscription-input\"\n style={styles.addInput}\n value={draft}\n autoFocus\n spellCheck={false}\n placeholder={t('modelsAddPlaceholder')}\n aria-label={t('modelsAdd')}\n disabled={busyAction}\n onChange={(event) => { setDraft(event.target.value) }}\n />\n <button type=\"submit\" disabled={busyAction || draft.trim().length === 0}>{t('modelsAddConfirm')}</button>\n <button type=\"button\" className=\"dsh-subscription-quiet\" onClick={() => { setAdding(false); setDraft('') }}>\n {t('cancel')}\n </button>\n </form>\n )}\n {modelsOpen && (\n <div\n id={`dsh-subscription-models-${status.provider}`}\n style={styles.modelList}\n // The whole list is the drop target, so a release between two rows still\n // lands somewhere instead of being cancelled unnoticed.\n onDragOver={(event) => { drag.over(event, 0) }}\n onDrop={drag.dropAt}\n >\n {status.provider === 'auto' && <span style={styles.chainTitle}>{t('modelsAutoChain')}</span>}\n {/* Every provider lists its models the same way: a switch in front,\n the model's own metadata, and a grip to drag it in the order both\n the picker list and Auto's fallback follow. */}\n {models.map((model, index) => (\n <div\n key={model.id}\n data-model-index={index}\n draggable={!busyAction}\n style={{\n ...styles.modelRowWrap,\n ...dragFrom === index ? { opacity: 0.5 } : {},\n // The row a drop would land on, so the gesture is visible while it\n // happens rather than only in its result.\n ...dropTarget === index && dragFrom !== undefined && dragFrom !== index\n ? { boxShadow: 'inset 0 2px 0 0 var(--dsw-alias-brand-primary)' }\n : {},\n }}\n onDragStart={(event) => { drag.start(event, index) }}\n onDragEnd={drag.end}\n >\n {/* The grip is the affordance and the keyboard path; the drag\n itself starts on the row, because a <button> does not start an\n HTML5 drag reliably in Chromium. */}\n <button\n type=\"button\"\n className=\"dsh-subscription-handle\"\n disabled={busyAction}\n title={t('modelsDrag')}\n aria-label={`${t('modelsDrag')} · ${model.name}`}\n onKeyDown={(event) => { drag.key(event, index) }}\n >\n ⠿\n </button>\n <label\n style={model.visible\n ? { ...styles.modelRowGrow, cursor: 'pointer' }\n : { ...styles.modelRowGrow, cursor: 'pointer', color: 'var(--dsw-alias-label-dimmed)' }}\n title={model.visible ? t('modelsHidden') : t('modelsShow')}\n >\n <input\n type=\"checkbox\"\n className=\"dsh-subscription-switch\"\n role=\"switch\"\n checked={model.visible}\n disabled={busyAction}\n aria-label={`${t('modelsShow')} · ${model.name}`}\n onChange={(event) => { props.onSetModelVisible(model.id, event.target.checked) }}\n />\n <span style={styles.modelBody}>\n <span style={styles.modelName}>\n {model.name}\n {model.source === undefined ? null : <span style={styles.badge}>{model.source}</span>}\n </span>\n <span style={styles.modelMeta}>\n {model.id}\n {' · '}\n {t('modelsContext', { tokens: formatTokens(model.contextWindow) })}\n {model.defaultEffort === undefined ? '' : ` · ${model.defaultEffort}`}\n {model.inputModalities === undefined || model.inputModalities.length === 0\n ? ''\n : ` · ${model.inputModalities.join('/')}`}\n </span>\n </span>\n </label>\n {/* A hand-added id can be removed again, so a typo does not linger. */}\n {model.userAdded === true && (\n <button\n type=\"button\"\n className=\"dsh-subscription-quiet\"\n style={styles.removeButton}\n disabled={busyAction}\n title={t('modelsRemove')}\n aria-label={`${t('modelsRemove')} · ${model.name}`}\n onClick={() => { props.onRemoveModel(model.id) }}\n >\n ×\n </button>\n )}\n </div>\n ))}\n {models.length === 0 && (\n <p style={styles.statusLine}>\n {status.provider === 'auto' ? t('modelsAutoChainEmpty') : t('modelsEmpty')}\n </p>\n )}\n </div>\n )}\n </div>\n )}\n\n {/* `auto` owns no account, so it offers no connect, cancel or manage. */}\n {connectable && (\n <div style={styles.actions}>\n {!busy && (\n <button type=\"button\" disabled={busyAction} onClick={props.onLogin}>\n {accounts.length === 0 ? t('connect') : t('addAccount')}\n </button>\n )}\n {!busy && (\n <button type=\"button\" disabled={busyAction} onClick={() => { setManageOpen(true) }}>\n {t('manage')}\n </button>\n )}\n {busy && (\n <button type=\"button\" disabled={busyAction} onClick={props.onCancel}>\n {t('cancel')}\n </button>\n )}\n </div>\n )}\n {connectable && accounts.length > 0 && <p style={styles.statusLine}>{t('hint')}</p>}\n\n {busy && status.pending !== undefined && (\n <PendingLogin prompt={status.pending} t={t} copied={copied} onCopy={(code) => { void copyCode(code) }} />\n )}\n </div>\n {/* The dialog stays outside the collapsible body: a hidden ancestor would\n keep it out of the top layer even after `showModal()`. */}\n <dialog\n ref={dialog}\n className=\"dsh-subscription-manager\"\n aria-labelledby={`dsh-subscription-manage-${status.provider}`}\n // A closed `<dialog>` is hidden by the user-agent stylesheet, and an\n // inline `display` would override that rule — so the open state has to\n // be restated here rather than left to `showModal()` alone.\n style={manageOpen ? styles.modal : { ...styles.modal, display: 'none' }}\n onClose={() => { setManageOpen(false) }}\n onCancel={() => { setManageOpen(false) }}\n >\n <div style={styles.modalHeader}>\n <span style={styles.modalTitle} id={`dsh-subscription-manage-${status.provider}`}>\n {t('manageTitle', { provider: status.displayName })}\n </span>\n </div>\n\n <span style={styles.statusLine}>{t('manageAccounts')}</span>\n {accounts.map((account) => (\n <div key={account.account} style={styles.accountRow}>\n <div style={styles.accountHeader}>\n <span style={styles.accountName}>{account.label}</span>\n {account.isDefault && <span style={styles.badge}>{t('defaultBadge')}</span>}\n </div>\n <div style={styles.actions}>\n <button\n type=\"button\"\n \n disabled={busyAction}\n onClick={() => { props.onRefreshToken(account.account) }}\n >\n {t('refreshToken')}\n </button>\n {!account.isDefault && (\n <button\n type=\"button\"\n \n disabled={busyAction}\n onClick={() => { props.onSetDefault(account.account) }}\n >\n {t('makeDefault')}\n </button>\n )}\n <button\n type=\"button\"\n \n disabled={busyAction}\n onClick={() => { props.onLogout(account.account) }}\n >\n {t('logout')}\n </button>\n </div>\n </div>\n ))}\n {accounts.length === 0 && <p style={styles.statusLine}>{t('statusSignedOut')}</p>}\n\n <div style={styles.divider} />\n <div style={styles.actions}>\n <button type=\"button\" disabled={busyAction} onClick={props.onReload}>\n {t('reload')}\n </button>\n </div>\n <p style={styles.statusLine}>{t('reloadHint')}</p>\n\n <div style={styles.divider} />\n <div style={styles.details}>\n {status.details.map((row) => (\n <div key={row.key} style={styles.detailRow}>\n <span>{detailLabel(t, row)}</span>\n <span style={styles.detailValue}>{row.value}</span>\n </div>\n ))}\n {status.discovered !== undefined && status.discovered.length > 0 && (\n <div style={styles.detailRow}>\n <span>{t('modelsLive')}</span>\n <span style={styles.detailValue}>{status.discovered.join(', ')}</span>\n </div>\n )}\n </div>\n\n <div className=\"dsh-subscription-dialog-actions\" style={styles.modalActions}>\n <button type=\"button\" onClick={() => { setManageOpen(false) }}>{t('close')}</button>\n </div>\n </dialog>\n </div>\n )\n}\n\n/**\n * The Subscriptions settings section.\n * @param props - section props (inject face plus the shell's `close`).\n * @returns the section content.\n */\nexport function SubscriptionsSection(props: SubscriptionsSectionProps) {\n const { call } = props\n const t = props.t ?? fallbackTranslate\n const [status, setStatus] = useState<SubscriptionStatus | undefined>(undefined)\n const [error, setError] = useState<string | undefined>(undefined)\n const [messages, setMessages] = useState<Record<string, CardMessage>>({})\n const [busyAction, setBusyAction] = useState(false)\n const [autoOn, setAutoOn] = useState(true)\n const mounted = useRef(true)\n const timer = useRef<ReturnType<typeof setInterval> | undefined>(undefined)\n\n /** Read whether the `auto` provider is offered, so its card can render the switch. */\n useEffect(() => {\n if (call === undefined) return\n let live = true\n void call('auto-state', undefined)\n .then((result) => { if (live) setAutoOn(result.enabled) })\n .catch(() => { /* the switch defaults to on */ })\n return () => { live = false }\n }, [call])\n\n /** Fetch the current state; a failure is shown rather than swallowed. */\n const refresh = useCallback(async (): Promise<void> => {\n if (call === undefined) return\n try {\n const next = await call('status', undefined)\n if (!mounted.current) return\n setStatus(next)\n setError(undefined)\n } catch (caught: unknown) {\n if (mounted.current) setError(t('actionFailed', { message: messageOf(caught) }))\n }\n }, [call, t])\n\n /** Flip the `auto` provider on or off. */\n const setAuto = useCallback(async (enabled: boolean): Promise<void> => {\n if (call === undefined) return\n setBusyAction(true)\n try {\n const result = await call('setAutoOn', { enabled })\n if (!mounted.current) return\n setAutoOn(result.enabled)\n await refresh()\n } catch (caught: unknown) {\n if (mounted.current) setError(t('actionFailed', { message: messageOf(caught) }))\n } finally {\n if (mounted.current) setBusyAction(false)\n }\n }, [call, refresh, t])\n\n const stopPolling = useCallback((): void => {\n if (timer.current !== undefined) {\n clearInterval(timer.current)\n timer.current = undefined\n }\n }, [])\n\n const startPolling = useCallback((): void => {\n if (timer.current !== undefined) return\n // The node half clears `busy` once the attempt settles; the effect below\n // observes that and stops this interval.\n timer.current = setInterval(() => { void refresh() }, POLL_INTERVAL_MS)\n }, [refresh])\n\n useEffect(() => {\n mounted.current = true\n void refresh()\n return () => {\n mounted.current = false\n stopPolling()\n }\n }, [refresh, stopPolling])\n\n /** Every route worth reading, as of the latest render, for the usage poll. */\n const signedInRef = useRef<readonly string[]>([])\n useEffect(() => {\n signedInRef.current = usageTargets(status?.providers ?? [])\n })\n\n /**\n * Read each named route's plan and allowance, applying every answer.\n *\n * A read is not forced: the node half reuses a figure only for a few seconds and\n * never reuses a substitute, so a steady interval refreshes the figures while\n * collapsing bursts into one request per route.\n */\n const readUsage = useCallback(async (providers: readonly string[]): Promise<void> => {\n if (call === undefined) return\n for (const provider of providers) {\n try {\n const result = await call('usage', { provider })\n if (!mounted.current) return\n setStatus(result.status)\n } catch {\n // An unreadable quota is already surfaced inside the card by the status it\n // carries; the page itself stays usable.\n }\n }\n }, [call])\n\n // The figures are read as soon as the first status lands...\n const usageRequested = useRef(false)\n useEffect(() => {\n if (call === undefined || status === undefined || usageRequested.current) return\n const signedIn = usageTargets(status.providers)\n if (signedIn.length === 0) return\n usageRequested.current = true\n void readUsage(signedIn)\n }, [call, status, readUsage])\n\n // ...and then kept current while the page is open. Allowances move as the account\n // is used, so a figure that is only read on mount reports the state of a page that\n // may have been open for hours.\n useEffect(() => {\n if (call === undefined) return\n const tick = (): void => {\n // Nothing is refreshed for a page nobody is looking at.\n if (globalThis.document?.visibilityState === 'hidden') return\n void readUsage(signedInRef.current)\n }\n const interval = setInterval(tick, USAGE_POLL_INTERVAL_MS)\n return () => { clearInterval(interval) }\n }, [call, readUsage])\n\n // Poll only while some provider is waiting for browser approval.\n const anyBusy = status?.providers.some((provider) => provider.busy) === true\n useEffect(() => {\n if (anyBusy) startPolling()\n else stopPolling()\n }, [anyBusy, startPolling, stopPolling])\n\n /**\n * Signed-in providers read first: a route with a live account is the one a\n * reader most likely wants, and `auto` (which owns no account) leads the rest.\n * The sort is stable, so equal ranks keep the host's configured order.\n */\n const orderedProviders = useMemo(() => {\n const providers = status?.providers ?? []\n const rank = (provider: ProviderStatus): number => {\n if (provider.provider === 'auto') return 0\n if (provider.loggedIn) return 1\n return 2\n }\n return [...providers].sort((left, right) => rank(left) - rank(right))\n }, [status?.providers])\n\n /** Run one action for one provider, reporting its own failure. */\n const act = useCallback(async (provider: string, run: () => Promise<void>): Promise<void> => {\n setBusyAction(true)\n setMessages((current) => {\n const next = { ...current }\n delete next[provider]\n return next\n })\n try {\n await run()\n } catch (caught: unknown) {\n if (mounted.current) {\n setMessages((current) => ({ ...current, [provider]: { tone: 'error', text: t('actionFailed', { message: messageOf(caught) }) } }))\n }\n } finally {\n if (mounted.current) setBusyAction(false)\n }\n }, [t])\n\n const login = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const started = await call('login', { provider })\n if (!mounted.current) return\n setStatus(started.status)\n // A loopback flow wants the browser opened; a blocked popup still leaves\n // the link in the card, which is the fallback the user already has.\n if (started.prompt.mode === 'open-url') {\n window.open(started.prompt.url, '_blank', 'noopener')\n }\n })\n }, [act, call])\n\n const cancel = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('cancel', { provider })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const logout = useCallback((provider: string, account: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('logout', { provider, account })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const setDefault = useCallback((provider: string, account: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('setDefault', { provider, account })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const refreshToken = useCallback((provider: string, account: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const result = await call('refresh', { provider, account })\n if (!mounted.current) return\n setStatus(result.status)\n setMessages((current) => ({\n ...current,\n [provider]: { tone: 'success', text: t('tokenRefreshed', { time: formatTime(result.expiresAt) }) },\n }))\n })\n }, [act, call, t])\n\n const refreshModels = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('discover', { provider, force: true })\n if (!mounted.current) return\n setStatus(next)\n setMessages((current) => ({ ...current, [provider]: { tone: 'success', text: t('modelsRefreshed') } }))\n })\n }, [act, call, t])\n\n const setModelVisible = useCallback((provider: string, id: string, visible: boolean): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('setModelVisible', { provider, id, visible })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const refreshUsage = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const result = await call('usage', { provider, force: true })\n if (mounted.current) setStatus(result.status)\n })\n }, [act, call])\n\n const setModelOrder = useCallback((provider: string, ids: readonly string[]): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('setModelOrder', { provider, ids: [...ids] })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const addModel = useCallback((provider: string, id: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('addModel', { provider, id })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const removeModel = useCallback((provider: string, id: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('removeModel', { provider, id })\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n const reload = useCallback((provider: string): void => {\n if (call === undefined) return\n void act(provider, async () => {\n const next = await call('reload', undefined)\n if (mounted.current) setStatus(next)\n })\n }, [act, call])\n\n if (call === undefined) return <p style={styles.intro}>{t('unavailable')}</p>\n\n return (\n <div className=\"dsh-subscriptions-settings\" style={styles.section}>\n <style>{subscriptionsCss}</style>\n <div style={styles.sectionHeader}>\n <h2 style={styles.heading}>{t('nav')}</h2>\n <p style={styles.intro}>{t('intro')}</p>\n </div>\n {error !== undefined && <p className=\"dsh-subscription-error\">{error}</p>}\n {status === undefined && <p style={styles.statusLine}>{t('loading')}</p>}\n {orderedProviders.map((provider) => (\n <ProviderCard\n key={provider.provider}\n status={provider}\n t={t}\n busyAction={busyAction}\n {...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] as CardMessage }}\n onLogin={() => { login(provider.provider) }}\n onCancel={() => { cancel(provider.provider) }}\n onLogout={(account) => { logout(provider.provider, account) }}\n onSetDefault={(account) => { setDefault(provider.provider, account) }}\n onRefreshToken={(account) => { refreshToken(provider.provider, account) }}\n onRefreshModels={() => { refreshModels(provider.provider) }}\n onSetModelVisible={(id, visible) => { setModelVisible(provider.provider, id, visible) }}\n onSetModelOrder={(ids) => { setModelOrder(provider.provider, ids) }}\n onAddModel={(id) => { addModel(provider.provider, id) }}\n onRemoveModel={(id) => { removeModel(provider.provider, id) }}\n onRefreshUsage={() => { refreshUsage(provider.provider) }}\n onReload={() => { reload(provider.provider) }}\n {...provider.provider !== 'auto' ? {} : { autoOn, onSetAutoOn: (enabled: boolean) => { void setAuto(enabled) } }}\n />\n ))}\n </div>\n )\n}\n","/**\n * The usage chip that rides the composer's bottom bar.\n *\n * The bar itself is a slot the shell owns. `conversation.composer.dock` is a\n * session-scoped `list`, and the shell's own stats pills (`tok/s`, cache hit) and its\n * context meter register into it; this chip joins as the last entry, so it reads to the\n * right of the context meter. It is deliberately compact — an abbreviated route name\n * and the percentage — because the bar is a status line, not a dashboard; the full\n * figure, its reset and the meter appear when the chip is clicked.\n *\n * Two silences are deliberate: a provider this plugin does not own shows nothing rather\n * than a fabricated zero, and switching to a foreign provider keeps the last own reading\n * on screen instead of blanking the bar — the shell's own pills already speak for the\n * foreign one.\n *\n * @module dsh-plugin-subscription-oauth/client/UsageChip\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { SessionStandardProps } from '@deepseek-ai/dsh-client-ui-slots'\n\nimport type { SubscriptionsCall, AccountBalanceCall, CcSwitchBalanceCall } from './subscriptions-rpc.js'\nimport { usageChipCss } from './styles.js'\nimport type { ProviderUsage, UsageWindow } from '../protocol.js'\n\n/** How often a visible chip refreshes its figures. */\nconst REFRESH_INTERVAL_MS = 60_000\n\n/**\n * Abbreviations for the routes this plugin serves.\n *\n * The bar is a status line under the composer, where a full product name crowds out the\n * shell's own pills; the full name is one click away. The fallback shortens an unknown\n * route's display name rather than dropping it.\n */\nconst SHORT_LABELS: Record<string, string> = {\n kimi: 'Kimi',\n grok: 'Grok',\n antigravity: 'Anti',\n glm: 'GLM',\n ark: 'Ark',\n}\n\n/**\n * Abbreviations for the window names the providers use.\n *\n * The bar has room for a token or two, so `weekly` becomes `7d` — the same figure in a\n * form that reads at a glance. An unrecognised window keeps its own name, shortened\n * only if it is long.\n */\nconst SHORT_WINDOWS: Record<string, string> = {\n 'weekly': '7d',\n '7d': '7d',\n '5h': '5h',\n 'monthly': 'mo',\n 'daily': '1d',\n 'credits': 'cr',\n 'free': 'free',\n 'session': 's',\n}\n\n/** Injected props the chip is registered with. */\nexport interface UsageChipInjected {\n /** The plugin's own RPC caller. */\n call: SubscriptionsCall\n /** Copy for the active locale. */\n t: (key: 'usageChipTitle' | 'usageChipFull', values?: Record<string, string | number>) => string\n /** Whether the provider is one this plugin serves. */\n isOwnProvider: (provider: string) => boolean\n /**\n * The DeepSeek account's balance, when the session uses it.\n *\n * The host owns that account's grant and the platform endpoint, so the chip asks the\n * host rather than re-deriving either. It resolves to `undefined` when the account is\n * signed out or the read fails, in which case the chip stays silent.\n */\n accountBalance: AccountBalanceCall\n /**\n * A non-subscription provider's balance, by cc-switch's query.\n *\n * A provider that is not a subscription this plugin serves has no usage windows to\n * meter, so the chip shows its balance instead. The caller names the provider; the\n * server maps it to the credential reference the provider's profile names (`apiKeyEnv`),\n * resolves that to the API key, and asks cc-switch's balance endpoint. It resolves to\n * `undefined` when the provider has no balance source or did not answer.\n */\n ccSwitchBalance: CcSwitchBalanceCall\n}\n\n/** One model choice, as the host records it. */\nexport interface ModelSelection {\n /** Provider route key. */\n readonly provider: string\n /** Model id. */\n readonly model: string\n /** Reasoning effort, when one was chosen. */\n readonly reasoningEffort?: string\n}\n\n/**\n * The session's model-selection projection, as the host wires it.\n *\n * `next` is a selection the user has made but not yet used; `lastUsed` is the one a\n * request actually ran with. Reading `next` first is what makes the chip follow the\n * picker immediately rather than only after the next message.\n */\nexport interface ModelSelectionProjection {\n /** The last selection a request ran with. */\n readonly lastUsed: ModelSelection | null\n /** The selection in force now. */\n readonly next: ModelSelection | null\n}\n\n/** The session's provider, from its model-selection projection. */\nexport function providerOf(projection: ModelSelectionProjection | null | undefined): string | undefined {\n return (projection?.next ?? projection?.lastUsed)?.provider\n}\n\n/** What one route's allowance reduces to for a single line. */\nexport interface ChipReading {\n /** Route the figures belong to. */\n provider: string\n /** Name to show, as the route states it. */\n label: string\n /** Every window that states a figure, in the order the route stated them. */\n windows: readonly UsageWindow[]\n /** The window worth showing in the compact line: the one closest to its limit. */\n window: UsageWindow\n /** Plan name, when the provider stated one. */\n plan?: string\n}\n\n/** One currency's balance, as the host's account controller reports it. */\nexport interface ChipBalance {\n /** The wallet's currency. */\n currency: string\n /** Remaining balance, as a decimal string. */\n balance: string\n /** Whether this is a bonus / promotional wallet rather than topped-up credit. */\n bonus: boolean\n}\n\n/**\n * Reduce the node half's balance payload to the wallets the chip shows.\n *\n * Bonus wallets are shown after the topped-up ones, so a reader sees their own credit\n * first and any promotional credit as a secondary figure. A bonus wallet that holds\n * nothing is dropped rather than shown as `+¥0`: the platform reports one for every\n * currency whether or not there is a bonus, and an empty one is noise, not information.\n *\n * @param value - the node half's balance, or undefined when there is none.\n * @returns the wallets, topped-up first then bonus, or undefined when nothing is usable.\n */\nexport function balanceOf(value: { wallets?: readonly { currency: string, balance: string }[], bonusWallets?: readonly { currency: string, balance: string }[] } | null | undefined): readonly ChipBalance[] | undefined {\n if (value === null || value === undefined) return undefined\n const wallets: ChipBalance[] = [\n ...(value.wallets ?? []).map((wallet) => ({ currency: wallet.currency, balance: wallet.balance, bonus: false })),\n // Only a bonus wallet that actually holds something is worth a line.\n ...(value.bonusWallets ?? [])\n .filter((wallet) => Number.parseFloat(wallet.balance) > 0)\n .map((wallet) => ({ currency: wallet.currency, balance: wallet.balance, bonus: true })),\n ]\n return wallets.length === 0 ? undefined : wallets\n}\n\n/**\n * Format one wallet for the chip's compact line.\n *\n * A balance is money, not a percentage, so it renders as `¥6.89` rather than a meter;\n * the currency prefix keeps two currencies from being mistaken for one sum. The amount\n * is rounded to two decimal places (a balance reads to the cent, and a figure like\n * `1.1` for `1.10` would mislead) and always carries both places.\n *\n * @param wallet - the wallet to show.\n * @returns the short form, e.g. `¥6.89` or `+$2.00` for a bonus wallet.\n */\nexport function formatBalance(wallet: ChipBalance): string {\n const amount = Number.parseFloat(wallet.balance)\n const trimmed = Number.isFinite(amount) ? amount.toFixed(2) : wallet.balance\n const symbol = wallet.currency === 'CNY' ? '¥' : wallet.currency === 'USD' ? '$' : `${wallet.currency} `\n return `${wallet.bonus ? '+' : ''}${symbol}${trimmed}`\n}\n\n/**\n * The short form of a route's name.\n *\n * @param provider - the route id.\n * @param label - the route's own display name.\n * @returns an abbreviation that still identifies the route.\n */\nexport function shortLabel(provider: string, label: string): string {\n const known = SHORT_LABELS[provider]\n if (known !== undefined) return known\n // An unknown route keeps its first word, so the chip names something recognisable\n // rather than a generic placeholder.\n const [first] = label.trim().split(/\\s+/)\n return first === undefined || first === '' ? provider : first\n}\n\n/**\n * The short form of a window's name.\n *\n * @param label - the window's own name, e.g. `weekly`.\n * @returns an abbreviation, or the label itself when it is already short.\n */\nexport function shortWindow(label: string): string {\n const known = SHORT_WINDOWS[label.toLowerCase()]\n if (known !== undefined) return known\n return label.length <= 4 ? label : label.slice(0, 4)\n}\n\n/**\n * Pick the window a single line should show.\n *\n * A plan can meter several windows at once (a rolling five-hour one and a weekly one).\n * The most constrained is the one that will stop work soonest, so it earns the space;\n * ties fall to the earlier window, which is the order the providers state them in.\n *\n * @param windows - the route's metered windows.\n * @returns the window to show, or undefined when none states a figure.\n */\nexport function pickWindow(windows: readonly UsageWindow[]): UsageWindow | undefined {\n let best: UsageWindow | undefined\n for (const window of windows) {\n if (window.usedPercent === undefined) continue\n if (best?.usedPercent === undefined || window.usedPercent > best.usedPercent) best = window\n }\n return best\n}\n\n/**\n * Reduce one route's usage to what the chip renders.\n *\n * @param provider - the route the figures came from.\n * @param label - the route's display name.\n * @param usage - the accounts the route answered for.\n * @returns the reading, or undefined when the route stated no figure.\n */\n/** What one account's allowance reduces to for the chip. */\nexport interface AccountReading {\n /** Which account these figures belong to. */\n account: string\n /** Plan name, when the provider stated one. */\n plan?: string\n /** Every metered window, in the order the route stated them. */\n windows: readonly UsageWindow[]\n}\n\n/**\n * Reduce a route's per-account usage to one reading per account.\n *\n * A provider can hold several accounts (two Antigravity sign-ins, say), and the chip\n * shows each one's allowance on its own line, the way the settings cards do — a merged\n * figure would hide that one account is exhausted while the other is free.\n *\n * @param usage - every account the route answered for.\n * @returns one reading per account that states a figure, in the route's order.\n */\n/**\n * Reduce a route's per-account usage to one reading per account.\n *\n * A provider can hold several accounts (two Antigravity sign-ins, say). Every account's\n * windows are kept as the route stated them — a percentage-metered quota and a credit\n * balance (`credits`) alike — so the per-account panel shows the real figures, and the\n * compact line's merge decides which to integrate.\n *\n * @param usage - every account the route answered for.\n * @returns one reading per account that states any window, in the route's order.\n */\nexport function accountReadingsOf(usage: readonly ProviderUsage[]): readonly AccountReading[] {\n const out: AccountReading[] = []\n for (const entry of usage) {\n if (entry.windows.length === 0) continue\n out.push({ account: entry.account, windows: entry.windows, ...entry.plan === undefined ? {} : { plan: entry.plan } })\n }\n return out\n}\n\n/**\n * Merge every account's percentage-metered windows into one figure per label.\n *\n * The chip's compact line integrates the whole pool: two accounts at 50 percent read as\n * one window at 50 percent (the same allowance spent twice as wide), not two lines. A\n * credit balance (`credits`) is money, not a share of a shared allowance, so it is not\n * merged — it stays on the per-account panel, where it reads correctly. Only windows\n * that state a `usedPercent` (a share of an allowance) merge.\n *\n * @param readings - one per account, from {@link accountReadingsOf}.\n * @returns one merged window per percentage-metered label, most-spent first.\n */\nexport function mergeAccountWindows(readings: readonly AccountReading[]): readonly UsageWindow[] {\n const byLabel = new Map<string, UsageWindow[]>()\n for (const reading of readings) {\n for (const window of reading.windows) {\n // Only a percentage-metered window merges. A credit balance (a bare used/limit\n // pair) is money, not a share of a shared allowance, and averaging it across\n // accounts would misread money as a quota.\n if (window.usedPercent === undefined) continue\n const list = byLabel.get(window.label) ?? []\n list.push(window)\n byLabel.set(window.label, list)\n }\n }\n const merged: UsageWindow[] = []\n for (const [label, list] of byLabel) {\n // Percentages average across accounts: the pool's spent share is the mean of each\n // account's spent share (accounts share an allowance, they do not add to it).\n const withPercent = list.filter((window) => window.usedPercent !== undefined)\n if (withPercent.length === 0) continue\n const usedPercent = withPercent.reduce((sum, window) => sum + (window.usedPercent ?? 0), 0) / withPercent.length\n // A credit window carries the raw figures too; keep them so the compact line can read\n // money as money (0/1500) rather than as a spent share (0%), which misreads an empty\n // balance as nothing to meter.\n const used = list.reduce((sum, window) => sum + (window.used ?? 0), 0)\n const limit = list.reduce((sum, window) => sum + (window.limit ?? 0), 0)\n // The pool's reset is the soonest account reset: the window refills when the first\n // account's does.\n const resets = list.map((window) => window.resetsAt).filter((at): at is number => typeof at === 'number')\n merged.push({\n label,\n usedPercent: Math.max(0, Math.min(100, usedPercent)),\n ...limit > 0 ? { used, limit } : {},\n ...resets.length === 0 ? {} : { resetsAt: Math.min(...resets) },\n })\n }\n // Order by window period, shortest first: the rolling `5h` window reads before the\n // `weekly` one, the way the providers state them and the way a reader scans (what\n // resets soonest is the nearer limit). Unrecognised labels keep their input order.\n const order = (label: string): number => {\n const short = shortWindow(label)\n if (short === '5h' || short === 's') return 0\n if (short === '1d' || short === 'mo') return 1\n if (short === '7d') return 2\n return 3\n }\n return merged.sort((left, right) => order(left.label) - order(right.label))\n}\n\n/**\n * Reduce one route's usage to the single line the compact chip renders.\n *\n * Kept for the balance-free providers that still show one merged line.\n *\n * @param provider - the route id.\n * @param label - the route's display name.\n * @param usage - the accounts the route answered for.\n * @returns the first account's reading, or undefined when none states a figure.\n */\nexport function readingOf(\n provider: string,\n label: string,\n usage: readonly ProviderUsage[],\n): ChipReading | undefined {\n for (const entry of usage) {\n const window = pickWindow(entry.windows)\n if (window === undefined) continue\n const windows = entry.windows.filter((candidate) => candidate.usedPercent !== undefined)\n return {\n provider,\n label,\n windows,\n window,\n ...entry.plan === undefined ? {} : { plan: entry.plan },\n }\n }\n return undefined\n}\n\n/**\n * Format a window's reset the way the settings cards do.\n *\n * The rule is shared with the cards on purpose: a figure reading `6d20h` here and\n * something else there would be two answers to one question.\n *\n * @param at - epoch milliseconds, or undefined when the provider states none.\n * @param now - the instant to measure from.\n * @returns a short phrase, or undefined when there is no reset to show.\n */\nexport function formatReset(at: number | undefined, now: number): string | undefined {\n if (at === undefined) return undefined\n const remaining = at - now\n if (remaining <= 0) return 'now'\n const hours = remaining / 3_600_000\n if (hours < 1) return `${String(Math.max(1, Math.round(remaining / 60_000)))}m`\n if (hours < 24) return `${String(Math.round(hours))}h`\n const days = Math.floor(hours / 24)\n return `${String(days)}d${String(Math.round(hours - days * 24))}h`\n}\n\n/**\n * The composer's usage chip.\n *\n * @param props - injected RPC caller, copy, and the session's provider reader.\n * @returns the chip, or null when there is nothing true to say.\n */\nexport function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBalance, useProjection }: UsageChipInjected & SessionStandardProps): ReactNode {\n const [reading, setReading] = useState<ChipReading | undefined>(undefined)\n const [accountReadings, setAccountReadings] = useState<readonly AccountReading[] | undefined>(undefined)\n const [balance, setBalance] = useState<readonly ChipBalance[] | undefined>(undefined)\n const [ccBalance, setCcBalance] = useState<{ currency: string, total: string } | undefined>(undefined)\n const [now, setNow] = useState(() => Date.now())\n const [open, setOpen] = useState(false)\n // The last route that answered, so a switch to a foreign provider leaves the last own\n // reading up rather than blanking the bar.\n const lastOwn = useRef<string | undefined>(undefined)\n\n // A projection read, so the chip follows the picker: changing the model re-renders it\n // with the new provider instead of waiting for the next unrelated render.\n const selection = useProjection('modelSelection', (value) => (value as ModelSelectionProjection))\n const selected = providerOf(selection)\n\n // A non-subscription provider has no usage windows to meter, so the chip shows its\n // balance instead, read by cc-switch's query. The host's account (deepseek-account)\n // has its own balance source and takes that over cc-switch; a subscription this plugin\n // serves keeps its usage meter.\n const isDeepSeek = selected === 'deepseek-account'\n const isBalanceProvider = selected !== undefined && !isOwnProvider(selected) && !isDeepSeek\n\n // Read a non-subscription provider's balance while it is in force, on the same cadence\n // as the other reads. It resolves to undefined when the provider has no balance source\n // or did not answer, which leaves the chip silent.\n useEffect(() => {\n if (!isBalanceProvider || selected === undefined) {\n setCcBalance(undefined)\n return\n }\n let live = true\n const target = selected\n const refresh = (): void => {\n void ccSwitchBalance(target)\n .then((value) => { if (live) setCcBalance(value === undefined ? undefined : { currency: value.currency, total: value.total }) })\n .catch(() => { /* a failed balance read stays silent */ })\n }\n refresh()\n const timer = setInterval(refresh, REFRESH_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [isBalanceProvider, selected, ccSwitchBalance])\n\n const load = useCallback(async (target: string): Promise<readonly AccountReading[] | undefined> => {\n try {\n const result = await call('usage', { provider: target })\n return accountReadingsOf(result.usage)\n } catch {\n // A failed read is not worth a chip of its own: the settings page is where a user\n // goes to find out why a route cannot report.\n return undefined\n }\n }, [call])\n\n // Read the DeepSeek account's balance while that provider is in force, on the same\n // cadence as a usage read. It resolves to undefined when the account is signed out or\n // the read fails, which leaves the chip silent.\n useEffect(() => {\n if (!isDeepSeek) {\n setBalance(undefined)\n return\n }\n let live = true\n const refresh = (): void => {\n void accountBalance()\n .then((value) => { if (live) setBalance(balanceOf(value)) })\n .catch(() => { /* a failed balance read stays silent */ })\n }\n refresh()\n const timer = setInterval(refresh, REFRESH_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [isDeepSeek, accountBalance])\n\n useEffect(() => {\n const target = selected !== undefined && isOwnProvider(selected)\n ? selected\n // Not one of ours (or none selected): keep showing the last own reading.\n : lastOwn.current\n if (target === undefined) {\n setAccountReadings(undefined)\n return\n }\n let live = true\n const refresh = (): void => {\n setNow(Date.now())\n void load(target).then((next) => {\n if (!live || next === undefined) return\n lastOwn.current = target\n setAccountReadings(next)\n })\n }\n refresh()\n const timer = setInterval(refresh, REFRESH_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [selected, isOwnProvider, load])\n\n // A click anywhere else puts the detail away, which is what a status chip should do.\n useEffect(() => {\n if (!open) return\n const close = (): void => { setOpen(false) }\n document.addEventListener('click', close)\n return () => { document.removeEventListener('click', close) }\n }, [open])\n\n // A non-subscription provider reads as money, through cc-switch's query. It shows the\n // one currency's total the provider answered with, nothing when there is no usable\n // balance.\n if (isBalanceProvider) {\n if (ccBalance === undefined) return null\n const short = shortLabel(selected ?? '', selected ?? '')\n return (\n <span className=\"dsh-subscription-usage-chip\">\n <style>{usageChipCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-usage-chip-trigger\"\n aria-expanded={open}\n title={t('usageChipTitle', { provider: selected ?? '' })}\n onClick={(event) => {\n event.stopPropagation()\n setOpen((was) => !was)\n }}\n >\n <span className=\"dsh-subscription-usage-chip-label\">{short}</span>\n <span className=\"dsh-subscription-usage-chip-cell\">\n <span className=\"dsh-subscription-usage-chip-value\">\n {`${ccBalance.currency === 'CNY' ? '¥' : ccBalance.currency === 'USD' ? '$' : `${ccBalance.currency} `}${ccBalance.total}`}\n </span>\n </span>\n </button>\n {open && (\n <span className=\"dsh-subscription-usage-chip-panel\" role=\"status\">\n <span className=\"dsh-subscription-usage-chip-full\">\n {t('usageChipFull', { provider: selected ?? '' })}\n </span>\n <span className=\"dsh-subscription-usage-chip-meter\">\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{ccBalance.currency}</span>\n <span className=\"dsh-subscription-usage-chip-detail\">{ccBalance.total}</span>\n </span>\n </span>\n </span>\n )}\n </span>\n )\n }\n\n // The DeepSeek account reads as money, not as a percentage, so it takes its own line:\n // the route abbreviated, then each wallet. An unknown selection leaves the chip silent.\n if (isDeepSeek) {\n if (balance === undefined) return null\n const short = shortLabel('deepseek-account', 'DeepSeek Account')\n return (\n <span className=\"dsh-subscription-usage-chip\">\n <style>{usageChipCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-usage-chip-trigger\"\n aria-expanded={open}\n title={t('usageChipTitle', { provider: 'DeepSeek Account' })}\n onClick={(event) => {\n event.stopPropagation()\n setOpen((was) => !was)\n }}\n >\n <span className=\"dsh-subscription-usage-chip-label\">{short}</span>\n {balance.map((wallet) => (\n <span className=\"dsh-subscription-usage-chip-cell\" key={`${wallet.currency}${wallet.bonus ? ':bonus' : ''}`}>\n <span className=\"dsh-subscription-usage-chip-value\">{formatBalance(wallet)}</span>\n </span>\n ))}\n </button>\n {open && (\n <span className=\"dsh-subscription-usage-chip-panel\" role=\"status\">\n <span className=\"dsh-subscription-usage-chip-full\">\n {t('usageChipFull', { provider: 'DeepSeek Account' })}\n </span>\n {balance.map((wallet) => (\n <span className=\"dsh-subscription-usage-chip-meter\" key={`${wallet.currency}${wallet.bonus ? ':bonus' : ''}`}>\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{`${wallet.bonus ? 'Bonus · ' : ''}${wallet.currency}`}</span>\n <span className=\"dsh-subscription-usage-chip-detail\">{formatBalance(wallet)}</span>\n </span>\n </span>\n ))}\n </span>\n )}\n </span>\n )\n }\n\n if (accountReadings === undefined || accountReadings.length === 0) return null\n const short = shortLabel(selected ?? '', selected ?? '')\n // The compact line integrates the whole pool; the panel shows each account's own\n // figures, so a merged glance hides nothing a per-account look cannot recover.\n const merged = mergeAccountWindows(accountReadings)\n\n return (\n <span className=\"dsh-subscription-usage-chip\">\n {/* An element, so unmounting the chip takes its rules with it. */}\n <style>{usageChipCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-usage-chip-trigger\"\n aria-expanded={open}\n title={t('usageChipTitle', { provider: selected ?? '' })}\n onClick={(event) => {\n // The document listener would otherwise close what this click just opened.\n event.stopPropagation()\n setOpen((was) => !was)\n }}\n >\n <span className=\"dsh-subscription-usage-chip-label\">{short}</span>\n {merged.map((window) => (\n <span className=\"dsh-subscription-usage-chip-cell\" key={window.label}>\n <span className=\"dsh-subscription-usage-chip-window\">{shortWindow(window.label)}</span>\n <span className=\"dsh-subscription-usage-chip-value\">\n {`${String(Math.round(window.usedPercent ?? 0))}%`}\n </span>\n </span>\n ))}\n </button>\n {open && (\n <span className=\"dsh-subscription-usage-chip-panel\" role=\"status\">\n <span className=\"dsh-subscription-usage-chip-full\">\n {t('usageChipFull', { provider: selected ?? '' })}\n </span>\n {accountReadings.map((account) => (\n <span key={account.account} className=\"dsh-subscription-usage-chip-account\">\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{account.plan ?? account.account}</span>\n </span>\n {account.windows.map((window) => {\n const windowReset = formatReset(window.resetsAt, now)\n // A credit balance reads as money (`used`/`limit`), a quota as a percent.\n const amount = window.usedPercent === undefined && window.used !== undefined && window.limit !== undefined\n ? `${String(window.used)}/${String(window.limit)}`\n : `${String(Math.round(window.usedPercent ?? 0))}%`\n return (\n <span className=\"dsh-subscription-usage-chip-meter\" key={window.label}>\n <span className=\"dsh-subscription-usage-chip-meterHead\">\n <span>{window.label}</span>\n <span className=\"dsh-subscription-usage-chip-detail\">\n {`${amount}${windowReset === undefined ? '' : ` · ${windowReset}`}`}\n </span>\n </span>\n {window.usedPercent !== undefined && (\n <span className=\"dsh-subscription-usage-chip-track\" aria-hidden=\"true\">\n <span\n className=\"dsh-subscription-usage-chip-fill\"\n style={{ width: `${String(Math.max(0, Math.min(100, window.usedPercent ?? 0)))}%` }}\n />\n </span>\n )}\n </span>\n )\n })}\n </span>\n ))}\n </span>\n )}\n </span>\n )\n}\n","/**\n * The composer's quick-mode button.\n *\n * The route's own fast/lightspeed variant is not a row in the model picker: it shares its\n * base model's visibility and is reached from here. The composer renders\n * `conversation.input.right` immediately before its model seat, so this button sits at\n * the model selector's left — the control a reader looks for when they want the same\n * model, faster.\n *\n * Off, the selection is the base model and pressing the button selects the fast variant.\n * On, the selection *is* the fast variant, so the button reports active and pressing it\n * returns to the base — which is why `model-variants` answers from both sides of the pair\n * rather than only from the base id.\n *\n * The button renders nothing when the model has no fast variant, when the route is not\n * one this plugin serves, or when the host exposes no way to change the selection: an\n * absent control is honest, a disabled one is noise.\n *\n * @module dsh-plugin-subscription-oauth/client/FastModeButton\n */\n\nimport { useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { SessionStandardProps } from '@deepseek-ai/dsh-client-ui-slots'\n\nimport type { SubscriptionsCall } from './subscriptions-rpc.js'\nimport type { ModelSelectionProjection } from './UsageChip.js'\nimport { providerOf } from './UsageChip.js'\nimport { fastModeCss } from './styles.js'\n\n/** Injected props the button is registered with. */\nexport interface FastModeButtonInjected {\n /** The plugin's own RPC caller, for the one `model-variants` read. */\n call: SubscriptionsCall\n /** Whether the provider is one this plugin serves. */\n isOwnProvider: (provider: string) => boolean\n /**\n * The session's model directory, when the host exposes one.\n *\n * Written when the button switches the model's `fast` rung. Absent on a host without\n * it, in which case the button renders nothing.\n */\n modelDirectory?: {\n /** Select one model and its reasoning rung; resolves when the picker accepted it. */\n select?: (selection: { provider: string, model: string, reasoningEffort?: string }) => Promise<unknown>\n } | undefined\n}\n\n/**\n * The quick-mode control, for the composer's row of standard controls.\n *\n * @param props - injected RPC caller, directory, and the session standard kit.\n * @returns the button, or null when this model has no fast variant.\n */\nexport function FastModeButton({ call, isOwnProvider, modelDirectory, useProjection }: FastModeButtonInjected & SessionStandardProps): ReactNode {\n const [pair, setPair] = useState<{ rung: string } | undefined>(undefined)\n // A switch that the host refuses must not look like a dead button: the reason is kept\n // and shown, because the only alternative is a control that silently does nothing.\n const [failure, setFailure] = useState<{ code?: string, message: string } | undefined>(undefined)\n const [pending, setPending] = useState(false)\n const selection = useProjection('modelSelection', (value) => (value as ModelSelectionProjection))\n const provider = providerOf(selection)\n const model = selection?.next?.model ?? selection?.lastUsed?.model\n\n useEffect(() => {\n if (provider === undefined || model === undefined || !isOwnProvider(provider)) {\n setPair(undefined)\n return\n }\n let live = true\n void call('model-variants', { provider, id: model })\n .then((result) => {\n if (!live) return\n // The rung is the provider's own word (xAI `lightspeed`, Kimi `highspeed`), so\n // the picker reads a name the model's own docs use, not a generic \"Fast\".\n setPair(result?.fast === undefined ? undefined : { rung: result.fastRung ?? 'fast' })\n })\n .catch(() => { if (live) setPair(undefined) })\n return () => { live = false }\n }, [provider, model, isOwnProvider, call])\n\n // A switch that landed clears the reason, so the marker follows the truth on screen.\n useEffect(() => { setFailure(undefined) }, [model])\n\n if (pair === undefined || provider === undefined || model === undefined || modelDirectory?.select === undefined) return null\n // Fast mode is the row's fast reasoning rung, so the model id never changes and the\n // picker always shows the row the user can switch back to.\n const active = (selection?.next ?? selection?.lastUsed)?.reasoningEffort === pair.rung\n const target = model\n\n const base = active\n ? 'Fast mode is on; press to go back to the standard model'\n : 'Fast mode: switch to this model’s fast variant'\n const title = failure === undefined\n ? base\n : `${base}\\nThe switch was refused — ${`${failure.code ?? ''} ${failure.message}`.trim()}`\n\n return (\n <>\n <style>{fastModeCss}</style>\n <button\n type=\"button\"\n className=\"dsh-subscription-fast-mode\"\n data-active={active}\n data-pending={pending}\n data-failed={failure !== undefined}\n aria-pressed={active}\n aria-busy={pending}\n title={title}\n aria-label={title}\n onClick={(event) => {\n event.stopPropagation()\n setPending(true)\n setFailure(undefined)\n // Turning it off drops the rung, which the host then reads as the model's own\n // default. The model id is never swapped, so this cannot strand the selection.\n const next = active ? { provider, model: target } : { provider, model: target, reasoningEffort: pair.rung }\n void Promise.resolve(modelDirectory.select?.(next))\n .then((result) => {\n // The directory reports its own outcome: a refused selection carries why.\n const outcome = result as { ok?: boolean, error?: { code?: string, message?: string } } | undefined\n if (outcome?.ok !== false) return\n const code = outcome.error?.code\n const message = outcome.error?.message ?? code ?? 'the host refused the selection'\n setFailure({ ...code === undefined ? {} : { code }, message })\n console.warn(`[subscription-oauth] fast mode ${provider}/${target} refused:`, outcome.error)\n })\n .catch((error: unknown) => {\n setFailure({ message: error instanceof Error ? error.message : String(error) })\n console.warn(`[subscription-oauth] fast mode ${provider}/${target} failed:`, error)\n })\n .finally(() => { setPending(false) })\n }}\n >\n <svg className=\"dsh-subscription-fast-mode-bolt\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" aria-hidden=\"true\" fill=\"none\">\n <path d=\"M13 2 3.6 13.2a1.2 1.2 0 0 0 .92 1.98H11l-.86 6.42a1.2 1.2 0 0 0 2.2.7L21.4 10.6a1.2 1.2 0 0 0-.92-1.98H13l.86-6.42A1.2 1.2 0 0 0 13 2z\" fill=\"currentColor\" stroke=\"currentColor\" strokeWidth=\"1.2\" strokeLinejoin=\"round\" />\n </svg>\n {failure !== undefined && (\n <span className=\"dsh-subscription-fast-mode-reason\">{failure.code ?? failure.message}</span>\n )}\n </button>\n </>\n )\n}\n","/**\n * Subscriptions settings page, browser half.\n *\n * Registers one `settings.section` entry titled 订阅 / Subscriptions. Every\n * connection fact arrives through the node half's `subscription-oauth` RPC\n * endpoints, so this half holds no credential state of its own, and the copy\n * rides the client locale service under the `settings.subscriptions` namespace.\n *\n * @module dsh-plugin-subscription-oauth/client\n */\n\n// Type-only: pulls the `ctx.slots` Context merge and the `settings.section`\n// SlotMap entry. These imports are erased; no runtime binding is taken.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client'\n\nimport { SubscriptionsSection } from './SubscriptionsSection.js'\nimport type { SubscriptionsSectionInjected } from './SubscriptionsSection.js'\nimport { FastModeButton } from './FastModeButton.js'\nimport type { FastModeButtonInjected } from './FastModeButton.js'\nimport { UsageChip } from './UsageChip.js'\nimport type { UsageChipInjected } from './UsageChip.js'\nimport { createSubscriptionsCall, createAccountBalanceCall, createCcSwitchBalanceCall } from './subscriptions-rpc.js'\nimport type { AccountBalanceCall } from './subscriptions-rpc.js'\nimport { en, zh } from './locales.js'\nimport type { SubscriptionsKey } from './locales.js'\n\nexport type { SubscriptionsSectionInjected, SubscriptionsSectionProps } from './SubscriptionsSection.js'\nexport type { FastModeButtonInjected } from './FastModeButton.js'\nexport type { UsageChipInjected } from './UsageChip.js'\nexport type { SubscriptionsKey } from './locales.js'\nexport { createSubscriptionsCall, SubscriptionsRpcError } from './subscriptions-rpc.js'\nexport type { SubscriptionsCall, AccountBalanceCall } from './subscriptions-rpc.js'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The Subscriptions settings page copy. */\n 'settings.subscriptions': SubscriptionsKey\n }\n interface SlotMap {\n /**\n * The composer's bottom bar.\n *\n * Declared here as the shell declares it — a session-scoped list — because the\n * owning package ships no type declarations to a plugin that only consumes it.\n * The shell's own stats pills are one entry in this list; this plugin's usage chip\n * is another.\n */\n 'conversation.composer.dock': { kind: 'list', scope: 'session' }\n /**\n * The composer's row of compact controls, rendered immediately before its model seat.\n *\n * The shell declares it as a session-scoped list; this plugin's quick-mode button is\n * one entry, which is what puts it at the model selector's left.\n */\n 'conversation.input.right': { kind: 'list', scope: 'session' }\n }\n\n /**\n * The session standard kit, of which this plugin reads one member.\n *\n * `ui-session` merges the session identity and the projection reader into every\n * session-scoped component's props, and it ships no declarations a plugin can see, so\n * the member is declared here. It reads one session projection, optionally narrowed\n * by a selector, and re-renders the component when that projection changes — which is\n * what lets the usage chip follow the model picker.\n */\n interface SessionStandardProps {\n useProjection<T>(key: string, selector: (value: unknown) => T): T\n }\n}\n\n/**\n * Routes this plugin serves.\n *\n * The chip only reports for these; a provider somebody else registered gets no chip,\n * because this plugin has no way to read its allowance.\n */\nconst OWN_PROVIDER_IDS = new Set(['kimi', 'grok', 'antigravity', 'glm', 'ark'])\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'settings.subscriptions'\n\n/** Settings nav position: after the shipped sections, before plugin tooling. */\nconst SECTION_ORDER = 92\n\n/**\n * Composer-dock position: after the shell's own stats pills, which sit at 0.\n *\n * The shell owns that bar and registers `tok/s` and the cache-hit pill into it, so the\n * chip follows them rather than displacing them.\n */\nconst CHIP_ORDER = 10\n\n/**\n * Composer-control position: ahead of the shell's own entries in that row, if it ever\n * adds any, so the quick-mode button stays adjacent to the model selector it belongs to.\n */\nconst FAST_MODE_ORDER = 10\n\n/** The host's per-session model directory resolver, as far as this plugin reads it. */\ninterface ModelDirectories {\n directoryFor?: (sessionId: string) => {\n select?: (selection: { provider: string, model: string }) => Promise<unknown>\n } | undefined\n}\n\n/**\n * Required services: `slots` carries the registration seat, `connection` the\n * RPC caller, `locale` the copy dictionaries, and `modelDirectories` the picker's\n * per-session model directory (written when the quick-mode button routes to a fast\n * variant).\n */\nexport const inject = ['slots', 'connection', 'locale', 'modelDirectories']\n\n/**\n * Register the Subscriptions settings section.\n *\n * Registration goes through `slots.inject`, so the shell's own apply order\n * relative to this one is unconstrained.\n *\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-plugin-subscription-oauth: copy dictionaries')\n // The shell's Context merge types `connection` as the host handle; in the\n // browser the same key holds the full client ConnectionHandle.\n const connection = ctx.get('connection') as unknown as ConnectionHandle\n const t = ctx.locale.bind(NS) as SubscriptionsSectionInjected['t']\n const call = createSubscriptionsCall(connection.rpc)\n // Read once: the resolver is a service, and the per-session directory is resolved at\n // registration time (see `modelDirectoryFor`).\n const modelDirectories = ctx.get('modelDirectories') as ModelDirectories | undefined\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'subscriptions',\n order: SECTION_ORDER,\n // A thunk re-evaluated per read, so the nav label follows the active locale.\n label: () => t('nav'),\n inject: (): SubscriptionsSectionInjected => ({ call, t }),\n }, SubscriptionsSection))\n\n // The composer's bottom bar is a session-scoped list the shell owns; its own stats\n // pills sit at order 0. This chip rides beside them and shows the allowance of the\n // provider the session is using. The scope hands `inject` the session id, but the chip\n // reads the selection from its own projection prop, so nothing here needs it.\n ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register({\n name: 'conversation.composer.dock',\n id: 'subscription-usage',\n order: CHIP_ORDER,\n inject: (): UsageChipInjected => ({\n call,\n t: t as unknown as UsageChipInjected['t'],\n isOwnProvider: (provider: string) => OWN_PROVIDER_IDS.has(provider),\n accountBalance: createAccountBalanceCall(connection.rpc),\n ccSwitchBalance: createCcSwitchBalanceCall(connection.rpc),\n }),\n }, UsageChip))\n\n // The composer renders this seat immediately before its model selector, so the\n // quick-mode button lands at the picker's left. It renders nothing unless the current\n // model really has a fast variant, so a route without one shows no control at all.\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register({\n name: 'conversation.input.right',\n id: 'subscription-fast-mode',\n order: FAST_MODE_ORDER,\n inject: (sessionId?: string): FastModeButtonInjected => ({\n call,\n isOwnProvider: (provider: string) => OWN_PROVIDER_IDS.has(provider),\n modelDirectory: modelDirectoryFor(modelDirectories, sessionId),\n }),\n }, FastModeButton))\n}\n\n/**\n * The session's model directory, in the shape the quick-mode button writes through.\n *\n * Absent when the host exposes no directory for the session, or exposes one that cannot\n * select — in which case the button renders nothing rather than a dead control.\n *\n * @param modelDirectories - the host's directory resolver, when it has one.\n * @param sessionId - the session whose directory is wanted.\n * @returns the reader/writer pair, or undefined.\n */\nfunction modelDirectoryFor(\n modelDirectories: ModelDirectories | undefined,\n sessionId: string | undefined,\n): FastModeButtonInjected['modelDirectory'] {\n if (sessionId === undefined || modelDirectories?.directoryFor === undefined) return undefined\n const directory = modelDirectories.directoryFor(sessionId)\n if (directory?.select === undefined) return undefined\n return {\n select: (selection: { provider: string, model: string }) => directory.select?.(selection) ?? Promise.resolve(),\n }\n}\n"],"mappings":"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"}
@@ -115,7 +115,14 @@ export function createWorkBuddyProvider(options) {
115
115
  }
116
116
  try {
117
117
  const renewed = await refreshWorkBuddyToken(token, fetchFn);
118
- return { accessToken: renewed.accessToken, expiresAt: renewed.expiresAt };
118
+ // WorkBuddy rotates the refresh token: keep the new one so the next restart can
119
+ // still refresh, otherwise the stale token signs the account out.
120
+ return {
121
+ accessToken: renewed.accessToken,
122
+ expiresAt: renewed.expiresAt,
123
+ ...renewed.refreshToken === undefined ? {} : { refreshToken: renewed.refreshToken },
124
+ ...renewed.refreshExpiresAt === undefined ? {} : { refreshExpiresAt: renewed.refreshExpiresAt },
125
+ };
119
126
  }
120
127
  catch (error) {
121
128
  throw new ProviderError(`${WORKBUDDY_PROVIDER_NAME} token refresh failed: ${error instanceof Error ? error.message : String(error)}`, REAUTH_REQUIRED);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-subscription-oauth",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "Use subscription accounts as DeepSeek Harness LLM providers — Kimi Code (OAuth device flow), Grok / X Premium (OIDC + PKCE), Google Antigravity (Google OAuth + Cloud Code Assist) and GLM / BigModel (ZCode's browser sign-in) — with per-model visibility, drag-to-reorder Auto fallback and plan quotas from the web Settings page",
5
5
  "license": "MIT",
6
6
  "author": "EricFL1998",