dsh-subscriptions 0.1.5 → 0.2.2

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.
Files changed (110) hide show
  1. package/README.md +212 -154
  2. package/lib/auth/claude-code-creds.js +31 -9
  3. package/lib/auth/device-flow.js +16 -9
  4. package/lib/auth/oauth-flow.js +13 -10
  5. package/lib/auth/rpc.d.ts +73 -2
  6. package/lib/auth/rpc.js +124 -23
  7. package/lib/auth/store.js +80 -31
  8. package/lib/client/CursorCard.d.ts +1 -1
  9. package/lib/client/CursorCard.js +50 -16
  10. package/lib/client/ExternalUsageCards.d.ts +1 -1
  11. package/lib/client/ExternalUsageCards.js +37 -16
  12. package/lib/client/ImageGallery.d.ts +2 -2
  13. package/lib/client/ImageGallery.js +92 -33
  14. package/lib/client/ImageGenerateToolview.js +50 -14
  15. package/lib/client/OllamaCard.d.ts +13 -0
  16. package/lib/client/OllamaCard.js +94 -0
  17. package/lib/client/OllamaSettingsManager.d.ts +24 -0
  18. package/lib/client/OllamaSettingsManager.js +329 -0
  19. package/lib/client/ProviderAccountManager.d.ts +1 -1
  20. package/lib/client/ProviderAccountManager.js +164 -49
  21. package/lib/client/ProviderModelEditor.js +109 -26
  22. package/lib/client/SpeedSelect.d.ts +62 -14
  23. package/lib/client/SpeedSelect.js +257 -46
  24. package/lib/client/SubscriptionUsageBadge.d.ts +5 -11
  25. package/lib/client/SubscriptionUsageBadge.js +301 -112
  26. package/lib/client/SubscriptionsSection.d.ts +2 -0
  27. package/lib/client/SubscriptionsSection.js +259 -85
  28. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  29. package/lib/client/UsageMeter.d.ts +1 -1
  30. package/lib/client/UsageMeter.js +69 -27
  31. package/lib/client/VideoGenerateToolview.js +69 -17
  32. package/lib/client/account-preferences.d.ts +8 -0
  33. package/lib/client/account-preferences.js +20 -5
  34. package/lib/client/host-contract.d.ts +11 -2
  35. package/lib/client/host-contract.js +20 -3
  36. package/lib/client/index.d.ts +5 -5
  37. package/lib/client/index.js +34 -12
  38. package/lib/client/locales.d.ts +78 -0
  39. package/lib/client/locales.js +121 -34
  40. package/lib/client/ollama-draft.d.ts +229 -0
  41. package/lib/client/ollama-draft.js +255 -0
  42. package/lib/client/reset-action.js +6 -4
  43. package/lib/client/reset-credits-view.d.ts +1 -1
  44. package/lib/client/reset-credits-view.js +66 -17
  45. package/lib/client/subscription-card-styles.js +94 -24
  46. package/lib/client/usage-color-preferences.js +4 -2
  47. package/lib/client/usage-pace.d.ts +7 -0
  48. package/lib/client/usage-pace.js +43 -10
  49. package/lib/client.js +1647 -266
  50. package/lib/client.js.map +1 -1
  51. package/lib/http.js +1 -1
  52. package/lib/index.d.ts +28 -3
  53. package/lib/index.js +9329 -8217
  54. package/lib/model-defaults.js +4 -2
  55. package/lib/provider-settings.d.ts +14 -1
  56. package/lib/provider-settings.js +64 -20
  57. package/lib/providers/account-preferences.js +51 -22
  58. package/lib/providers/accounts.js +15 -9
  59. package/lib/providers/antigravity-oauth-client.js +2 -2
  60. package/lib/providers/antigravity.js +141 -68
  61. package/lib/providers/catalog-store.js +120 -41
  62. package/lib/providers/claude-cli-version.js +3 -3
  63. package/lib/providers/claude.d.ts +23 -3
  64. package/lib/providers/claude.js +167 -92
  65. package/lib/providers/codex-client-version.js +3 -3
  66. package/lib/providers/codex-search.js +40 -13
  67. package/lib/providers/codex.js +196 -112
  68. package/lib/providers/common.d.ts +14 -0
  69. package/lib/providers/common.js +118 -38
  70. package/lib/providers/copilot.js +143 -82
  71. package/lib/providers/cursor-adapter.js +42 -18
  72. package/lib/providers/cursor-auth.d.ts +1 -1
  73. package/lib/providers/cursor-auth.js +69 -24
  74. package/lib/providers/cursor-model-settings.js +14 -5
  75. package/lib/providers/cursor-usage.js +27 -13
  76. package/lib/providers/external-usage-controller.d.ts +9 -2
  77. package/lib/providers/external-usage-controller.js +30 -7
  78. package/lib/providers/external-usage.d.ts +8 -0
  79. package/lib/providers/external-usage.js +140 -20
  80. package/lib/providers/grok.js +152 -89
  81. package/lib/providers/image-pool.js +29 -11
  82. package/lib/providers/minimax-usage.js +31 -11
  83. package/lib/providers/npm-cli-version.js +10 -3
  84. package/lib/providers/ollama-settings.d.ts +90 -0
  85. package/lib/providers/ollama-settings.js +213 -0
  86. package/lib/providers/ollama-web.d.ts +46 -0
  87. package/lib/providers/ollama-web.js +231 -0
  88. package/lib/providers/ollama.d.ts +107 -0
  89. package/lib/providers/ollama.js +608 -0
  90. package/lib/providers/pool-family.js +11 -3
  91. package/lib/providers/pool-health.js +28 -8
  92. package/lib/providers/pool-usage.js +16 -9
  93. package/lib/providers/pool.d.ts +8 -0
  94. package/lib/providers/pool.js +81 -42
  95. package/lib/providers/rate-limit.js +8 -2
  96. package/lib/providers/replay.js +40 -17
  97. package/lib/providers/reset-redemption.js +18 -6
  98. package/lib/tools/image-generate.js +123 -65
  99. package/lib/tools/registration.js +5 -2
  100. package/lib/tools/video-generate.js +51 -31
  101. package/lib/tools/x-search.js +50 -22
  102. package/lib/translate/anthropic.js +85 -37
  103. package/lib/translate/antigravity-schema.js +71 -28
  104. package/lib/translate/antigravity-thinking.js +22 -9
  105. package/lib/translate/antigravity.js +141 -50
  106. package/lib/translate/chat-completions.js +67 -23
  107. package/lib/translate/resolved.js +55 -21
  108. package/lib/translate/responses.js +69 -29
  109. package/lib/translate/sse.js +4 -1
  110. package/package.json +26 -4
@@ -27,13 +27,14 @@ import { useCallback, useEffect, useRef, useState } from 'react';
27
27
  import { subscriptionChromeCss } from './provider-settings-styles.js';
28
28
  import { createPortal } from 'react-dom';
29
29
  import * as primitives from '@deepseek-ai/dsh-client-ui-primitives';
30
- import { useAnchoredPosition, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
30
+ import { useAnchoredPosition, useDismissOnOutsidePointer, } from '@deepseek-ai/dsh-client-ui-primitives';
31
31
  import { callSubscriptionsAuth } from './SubscriptionsSection.js';
32
32
  import { UsageMeter } from './UsageMeter.js';
33
33
  import { displayUsedPercent } from './usage-pace.js';
34
34
  import { en } from './locales.js';
35
35
  import { hostIcon } from './host-icons.js';
36
- import { USAGE_BADGE_REFRESH_EVENT, useUsageBadgeMode } from './usage-badge-preferences.js';
36
+ import { HOST_CONTRACT, markerSelector } from './host-contract.js';
37
+ import { USAGE_BADGE_REFRESH_EVENT, useUsageBadgeMode, } from './usage-badge-preferences.js';
37
38
  /** DSH renamed the data icon in 0.1.7; retain older supported hosts too. */
38
39
  export function usageBadgeIcon(icons) {
39
40
  return hostIcon(icons, 'DataOutline');
@@ -51,7 +52,7 @@ const PANEL_GAP = 8;
51
52
  const PANEL_MARGIN = 12;
52
53
  /** The account the collapsed pill reads: the default one, else the first listed. */
53
54
  export function pillAccountOf(d) {
54
- return d.accounts.find(a => a.isDefault) ?? d.accounts[0];
55
+ return d.accounts.find((a) => a.isDefault) ?? d.accounts[0];
55
56
  }
56
57
  const PROVIDER_NAMES = {
57
58
  codex: 'Codex',
@@ -62,8 +63,9 @@ const PROVIDER_NAMES = {
62
63
  'cursor-subscription': 'Cursor',
63
64
  'opencode-go': 'OpenCode Go',
64
65
  'kimi-coding': 'Kimi Code',
65
- 'minimax': 'MiniMax',
66
+ minimax: 'MiniMax',
66
67
  'minimax-cn': 'MiniMax CN',
68
+ 'ollama-cloud': 'Ollama Cloud',
67
69
  };
68
70
  /** Key of one account's cached windows: `provider:accountKey`. */
69
71
  function usageKeyOf(provider, account) {
@@ -90,13 +92,17 @@ export function groupUsageDisplays(roster, windowsOf, plans) {
90
92
  const row = {
91
93
  key: account.key,
92
94
  isDefault: account.isDefault,
93
- ...account.account === undefined ? {} : { account: account.account },
94
- ...plan === undefined ? {} : { plan },
95
+ ...(account.account === undefined ? {} : { account: account.account }),
96
+ ...(plan === undefined ? {} : { plan }),
95
97
  windows,
96
98
  };
97
99
  const display = byProvider.get(provider);
98
100
  if (display === undefined)
99
- byProvider.set(provider, { provider, name: PROVIDER_NAMES[provider], accounts: [row] });
101
+ byProvider.set(provider, {
102
+ provider,
103
+ name: PROVIDER_NAMES[provider],
104
+ accounts: [row],
105
+ });
100
106
  else
101
107
  display.accounts.push(row);
102
108
  }
@@ -112,7 +118,13 @@ export async function loadBadgeRoster(rpc) {
112
118
  const roster = [];
113
119
  const refreshed = new Set();
114
120
  if (subscriptions.status === 'fulfilled') {
115
- for (const provider of ['codex', 'claude', 'grok', 'copilot', 'antigravity']) {
121
+ for (const provider of [
122
+ 'codex',
123
+ 'claude',
124
+ 'grok',
125
+ 'copilot',
126
+ 'antigravity',
127
+ ]) {
116
128
  refreshed.add(provider);
117
129
  for (const account of accountsOf(subscriptions.value.providers[provider]))
118
130
  roster.push({ provider, account });
@@ -122,17 +134,23 @@ export async function loadBadgeRoster(rpc) {
122
134
  refreshed.add('cursor-subscription');
123
135
  if (cursor.value.authenticated)
124
136
  roster.push({
125
- provider: 'cursor-subscription', account: { key: 'cursor', isDefault: true },
137
+ provider: 'cursor-subscription',
138
+ account: { key: 'cursor', isDefault: true },
126
139
  });
127
140
  }
128
141
  if (external.status === 'fulfilled') {
129
142
  for (const [source, provider] of [
130
- ['opencode-go', 'opencode-go'], ['kimi-code', 'kimi-coding'], ['minimax', 'minimax'], ['minimax-cn', 'minimax-cn'],
143
+ ['opencode-go', 'opencode-go'],
144
+ ['kimi-code', 'kimi-coding'],
145
+ ['minimax', 'minimax'],
146
+ ['minimax-cn', 'minimax-cn'],
147
+ ['ollama-cloud', 'ollama-cloud'],
131
148
  ]) {
132
149
  refreshed.add(provider);
133
150
  if (external.value[source]?.configured)
134
151
  roster.push({
135
- provider, account: { key: source, isDefault: true },
152
+ provider,
153
+ account: { key: source, isDefault: true },
136
154
  });
137
155
  }
138
156
  }
@@ -141,8 +159,14 @@ export async function loadBadgeRoster(rpc) {
141
159
  export async function usageOf(rpc, { provider, account }) {
142
160
  if (provider === 'cursor-subscription')
143
161
  return callSubscriptionsAuth(rpc, 'cursorUsage', {});
144
- if (provider === 'opencode-go' || provider === 'kimi-coding' || provider === 'minimax' || provider === 'minimax-cn') {
145
- return callSubscriptionsAuth(rpc, 'externalUsage', { source: provider === 'kimi-coding' ? 'kimi-code' : provider });
162
+ if (provider === 'opencode-go' ||
163
+ provider === 'kimi-coding' ||
164
+ provider === 'minimax' ||
165
+ provider === 'minimax-cn' ||
166
+ provider === 'ollama-cloud') {
167
+ return callSubscriptionsAuth(rpc, 'externalUsage', {
168
+ source: provider === 'kimi-coding' ? 'kimi-code' : provider,
169
+ });
146
170
  }
147
171
  return callSubscriptionsAuth(rpc, 'usage', { provider, account: account.key });
148
172
  }
@@ -172,9 +196,12 @@ export function windowLabel(w) {
172
196
  if (w.scope !== undefined && w.scope !== '')
173
197
  return w.scope;
174
198
  switch (w.kind) {
175
- case 'session': return '5h';
176
- case 'weekly': return 'Wk';
177
- default: return 'W';
199
+ case 'session':
200
+ return '5h';
201
+ case 'weekly':
202
+ return 'Wk';
203
+ default:
204
+ return 'W';
178
205
  }
179
206
  }
180
207
  const ms = Math.max(0, w.resetsAt - Date.now());
@@ -197,10 +224,12 @@ function windowSegment(label, w) {
197
224
  }
198
225
  /** Keep model quotas separate: matching percentages do not imply a shared pool. */
199
226
  export function prioritizeWindows(windows, model) {
200
- return model === undefined ? [...windows] : [
201
- ...windows.filter(w => w.scope === model),
202
- ...windows.filter(w => w.scope !== model),
203
- ];
227
+ return model === undefined
228
+ ? [...windows]
229
+ : [
230
+ ...windows.filter((w) => w.scope === model),
231
+ ...windows.filter((w) => w.scope !== model),
232
+ ];
204
233
  }
205
234
  /** Small previews keep a live model catalog from taking over the dialog. */
206
235
  export const WINDOW_PREVIEW_LIMIT = 4;
@@ -208,7 +237,9 @@ export function previewWindows(windows, model, provider) {
208
237
  const ordered = prioritizeWindows(windows, model);
209
238
  // Antigravity has many model-specific quotas: preview only the current model.
210
239
  const limit = provider === 'antigravity'
211
- ? Math.min(2, model === undefined ? 0 : windows.filter(w => w.scope === model).length)
240
+ ? Math.min(2, model === undefined
241
+ ? 0
242
+ : windows.filter((w) => w.scope === model).length)
212
243
  : WINDOW_PREVIEW_LIMIT;
213
244
  return { shown: ordered.slice(0, limit), hidden: ordered.slice(limit) };
214
245
  }
@@ -216,27 +247,34 @@ export function previewWindows(windows, model, provider) {
216
247
  export function compactSegment(d, model, t = fallbackTranslate) {
217
248
  const windows = pillAccountOf(d).windows;
218
249
  if (d.provider === 'antigravity') {
219
- const matching = model === undefined ? [] : windows.filter(w => w.scope === model);
250
+ const matching = model === undefined ? [] : windows.filter((w) => w.scope === model);
220
251
  if (matching.length === 0) {
221
- return `${d.name} ${t(model === undefined ? 'usageBadgeModelCount' : 'usageBadgeModelUnavailable', {
222
- count: new Set(windows.map(w => w.scope).filter(Boolean)).size,
252
+ return `${d.name} ${t(model === undefined
253
+ ? 'usageBadgeModelCount'
254
+ : 'usageBadgeModelUnavailable', {
255
+ count: new Set(windows.map((w) => w.scope).filter(Boolean)).size,
223
256
  })}`;
224
257
  }
225
- const parts = matching.slice(0, 2).map(w => windowSegment(w.kind === 'weekly' ? t('usageWeekly') : t('usageWindow'), w));
258
+ const parts = matching
259
+ .slice(0, 2)
260
+ .map((w) => windowSegment(w.kind === 'weekly' ? t('usageWeekly') : t('usageWindow'), w));
226
261
  return `${d.name} ${parts.join(' · ')}`;
227
262
  }
228
- const parts = windows.slice(0, 2).map(w => windowSegment(windowLabel(w), w));
263
+ const parts = windows.slice(0, 2).map((w) => windowSegment(windowLabel(w), w));
229
264
  if (windows.length > 2)
230
265
  parts.push(`+${windows.length - 2}`);
231
266
  return `${d.name} ${parts.join(' · ')}`;
232
267
  }
233
268
  /** Remember only the most recent subscription in this view, including its model scope. */
234
269
  export function retainSubscriptionSelection(previous, current) {
235
- return current !== undefined && Object.hasOwn(PROVIDER_NAMES, current.provider) ? current : previous;
270
+ return current !== undefined &&
271
+ Object.hasOwn(PROVIDER_NAMES, current.provider)
272
+ ? current
273
+ : previous;
236
274
  }
237
275
  /** At most one provider; no known selection or no usage means no badge. */
238
276
  export function collapsedDisplays(displays, current) {
239
- const match = displays.find(d => d.provider === current);
277
+ const match = displays.find((d) => d.provider === current);
240
278
  return match === undefined ? [] : [match];
241
279
  }
242
280
  /**
@@ -254,16 +292,20 @@ export function collapsedDisplays(displays, current) {
254
292
  export function rotatingDisplay(displays, current, step) {
255
293
  if (displays.length === 0)
256
294
  return [];
257
- const match = displays.find(d => d.provider === current);
295
+ const match = displays.find((d) => d.provider === current);
258
296
  if (match !== undefined)
259
297
  return [match];
260
298
  const index = Number.isFinite(step) ? Math.floor(step) : 0;
261
- return [displays[((index % displays.length) + displays.length) % displays.length]];
299
+ return [
300
+ displays[((index % displays.length) + displays.length) % displays.length],
301
+ ];
262
302
  }
263
303
  /** Order for the expanded dialog: the current provider first, the rest in poll order. */
264
304
  export function expandedDisplays(displays, current) {
265
- const match = displays.find(d => d.provider === current);
266
- return match === undefined ? displays : [match, ...displays.filter(d => d !== match)];
305
+ const match = displays.find((d) => d.provider === current);
306
+ return match === undefined
307
+ ? displays
308
+ : [match, ...displays.filter((d) => d !== match)];
267
309
  }
268
310
  /**
269
311
  * A provider's logged-in accounts, the effective default first. When no
@@ -273,9 +315,9 @@ export function expandedDisplays(displays, current) {
273
315
  function accountsOf(status) {
274
316
  if (status === undefined || status.accounts.length === 0)
275
317
  return [];
276
- const fallback = status.accounts.find(a => a.isDefault) ?? status.accounts[0];
318
+ const fallback = status.accounts.find((a) => a.isDefault) ?? status.accounts[0];
277
319
  return status.accounts
278
- .map(a => (a === fallback ? { ...a, isDefault: true } : a))
320
+ .map((a) => (a === fallback ? { ...a, isDefault: true } : a))
279
321
  .sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
280
322
  }
281
323
  /** English-dictionary fallback for a missing inject `t` (standalone renders). */
@@ -286,8 +328,12 @@ function fallbackTranslate(key, params) {
286
328
  function usageWindowLabel(t, window) {
287
329
  const base = window.kind === 'session'
288
330
  ? t('usageSession')
289
- : window.kind === 'weekly' ? t('usageWeekly') : t('usageWindow');
290
- return window.scope !== undefined && window.scope !== '' ? `${base} · ${window.scope}` : base;
331
+ : window.kind === 'weekly'
332
+ ? t('usageWeekly')
333
+ : t('usageWindow');
334
+ return window.scope !== undefined && window.scope !== ''
335
+ ? `${base} · ${window.scope}`
336
+ : base;
291
337
  }
292
338
  /**
293
339
  * The composer subscription-usage badge: a pill reading e.g.
@@ -295,7 +341,9 @@ function usageWindowLabel(t, window) {
295
341
  * every provider's accounts and their windows. Returns null when no data is
296
342
  * available.
297
343
  */
298
- export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
344
+ const STATS_ROW_SELECTOR = markerSelector(HOST_CONTRACT.markers.composerStats.row);
345
+ const STATS_DOCK_SELECTOR = markerSelector(HOST_CONTRACT.markers.composerStats.dock);
346
+ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs, }) {
299
347
  const translate = t ?? fallbackTranslate;
300
348
  const [displays, setDisplays] = useState([]);
301
349
  const [selection, setSelection] = useState(undefined);
@@ -342,7 +390,8 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
342
390
  // real signal, unlike a fetch failure.
343
391
  const live = new Set(roster.map(({ provider, account }) => usageKeyOf(provider, account)));
344
392
  for (const key of lastKnown.keys()) {
345
- if (refreshed.has(key.split(':', 1)[0]) && !live.has(key)) {
393
+ if (refreshed.has(key.split(':', 1)[0]) &&
394
+ !live.has(key)) {
346
395
  lastKnown.delete(key);
347
396
  observationsRef.current.delete(key);
348
397
  }
@@ -355,7 +404,10 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
355
404
  return;
356
405
  for (const { provider, account } of roster) {
357
406
  const key = usageKeyOf(provider, account);
358
- observationsRef.current.set(key, { ...observationsRef.current.get(key), stale: true });
407
+ observationsRef.current.set(key, {
408
+ ...observationsRef.current.get(key),
409
+ stale: true,
410
+ });
359
411
  }
360
412
  const plans = new Map();
361
413
  for (const r of results) {
@@ -371,24 +423,39 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
371
423
  continue;
372
424
  }
373
425
  lastKnown.set(key, usage.windows);
374
- observationsRef.current.set(key, { observedAt: usage.observedAt ?? Date.now(), stale: usage.stale === true });
426
+ observationsRef.current.set(key, {
427
+ observedAt: usage.observedAt ?? Date.now(),
428
+ stale: usage.stale === true,
429
+ });
375
430
  }
376
- setDisplays(previous => [
377
- ...groupUsageDisplays(roster, lastKnown, plans).map(display => ({ ...display,
378
- accounts: display.accounts.map(account => ({ ...account,
431
+ setDisplays((previous) => [
432
+ ...groupUsageDisplays(roster, lastKnown, plans).map((display) => ({
433
+ ...display,
434
+ accounts: display.accounts.map((account) => ({
435
+ ...account,
379
436
  ...observationsRef.current.get(`${display.provider}:${account.key}`),
380
437
  })),
381
438
  })),
382
- ...previous.filter(display => !refreshed.has(display.provider)).map(display => ({ ...display,
383
- accounts: display.accounts.map(account => ({ ...account, stale: true })),
439
+ ...previous
440
+ .filter((display) => !refreshed.has(display.provider))
441
+ .map((display) => ({
442
+ ...display,
443
+ accounts: display.accounts.map((account) => ({
444
+ ...account,
445
+ stale: true,
446
+ })),
384
447
  })),
385
448
  ]);
386
449
  }
387
450
  catch {
388
451
  // Keep cached numbers, but never present failed observations as current.
389
452
  if (mountedRef.current)
390
- setDisplays(previous => previous.map(display => ({ ...display,
391
- accounts: display.accounts.map(account => ({ ...account, stale: true })),
453
+ setDisplays((previous) => previous.map((display) => ({
454
+ ...display,
455
+ accounts: display.accounts.map((account) => ({
456
+ ...account,
457
+ stale: true,
458
+ })),
392
459
  })));
393
460
  }
394
461
  finally {
@@ -397,14 +464,18 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
397
464
  }, [rpc]);
398
465
  useEffect(() => {
399
466
  mountedRef.current = true;
400
- return () => { mountedRef.current = false; };
467
+ return () => {
468
+ mountedRef.current = false;
469
+ };
401
470
  }, []);
402
471
  // Hidden renders nothing, so it polls nothing; showing it again refreshes at once.
403
472
  useEffect(() => {
404
473
  if (displayMode === 'hidden')
405
474
  return;
406
475
  void refresh();
407
- const timer = setInterval(() => { void refresh(); }, USAGE_POLL_INTERVAL_MS);
476
+ const timer = setInterval(() => {
477
+ void refresh();
478
+ }, USAGE_POLL_INTERVAL_MS);
408
479
  window.addEventListener(USAGE_BADGE_REFRESH_EVENT, refresh);
409
480
  return () => {
410
481
  clearInterval(timer);
@@ -421,12 +492,18 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
421
492
  if (read === undefined || inflight)
422
493
  return;
423
494
  inflight = true;
424
- void read().then((model) => {
495
+ void read()
496
+ .then((model) => {
425
497
  if (cancelled)
426
498
  return;
427
- setLastSubscription(previous => retainSubscriptionSelection(previous, model));
499
+ setLastSubscription((previous) => retainSubscriptionSelection(previous, model));
428
500
  setSelection(model);
429
- }, () => { }).finally(() => { inflight = false; });
501
+ }, () => {
502
+ /* keep the last known provider; the next tick retries */
503
+ })
504
+ .finally(() => {
505
+ inflight = false;
506
+ });
430
507
  };
431
508
  reload();
432
509
  const timer = setInterval(reload, MODEL_POLL_INTERVAL_MS);
@@ -435,7 +512,14 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
435
512
  clearInterval(timer);
436
513
  };
437
514
  }, []);
438
- const pos = useAnchoredPosition({ open, anchorRef: rootRef, panelRef, side: 'top', gap: PANEL_GAP, margin: PANEL_MARGIN });
515
+ const pos = useAnchoredPosition({
516
+ open,
517
+ anchorRef: rootRef,
518
+ panelRef,
519
+ side: 'top',
520
+ gap: PANEL_GAP,
521
+ margin: PANEL_MARGIN,
522
+ });
439
523
  useDismissOnOutsidePointer(rootRef, open, setOpen, panelRef);
440
524
  useEffect(() => {
441
525
  if (!open)
@@ -445,15 +529,20 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
445
529
  setOpen(false);
446
530
  };
447
531
  document.addEventListener('keydown', onKeyDown);
448
- return () => { document.removeEventListener('keydown', onKeyDown); };
532
+ return () => {
533
+ document.removeEventListener('keydown', onKeyDown);
534
+ };
449
535
  }, [open]);
450
- // Sit on the host's stats row when there is one. Every dock entry is its
451
- // own row in the composer bar, so a badge rendered in place lands under the
452
- // shipped time/token pills; the host marks its pill row with
453
- // `data-composer-stats`, and rendering into it makes the badge a third pill
454
- // on that line. The marker is watched (the row mounts only once the session
455
- // has steps or tokens, and unmounts with them) and older hosts without it
456
- // keep the in-place row.
536
+ // Sit on the host's stats row when there is one. Hosts through 0.2.0 render
537
+ // their time/token pills in a row apart from the dock, marked
538
+ // `data-composer-stats`; every dock entry is its own row in the composer
539
+ // bar, so a badge rendered in place lands under those pills, and rendering
540
+ // into the row makes it a third pill on their line. The marker is watched
541
+ // (the row mounts only once the session has steps or tokens, and unmounts
542
+ // with them). From 0.2.1 the shipped pills are dock entries like this
543
+ // badge, each anchored `data-composer-stat`, so the in-place seat already
544
+ // shares their row; seeing a docked anchor ends the watch for a row that
545
+ // will not appear.
457
546
  const [statsRow, setStatsRow] = useState(null);
458
547
  useEffect(() => {
459
548
  const seat = seatRef.current;
@@ -462,11 +551,19 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
462
551
  const scope = statsScopeOf(seat);
463
552
  if (scope === null)
464
553
  return;
465
- const find = () => scope.querySelector('[data-composer-stats]');
466
- setStatsRow(find());
467
- const observer = new MutationObserver(() => { setStatsRow(find()); });
554
+ const find = () => scope.querySelector(STATS_ROW_SELECTOR);
555
+ const settle = () => {
556
+ const row = find();
557
+ setStatsRow(row);
558
+ if (row === null && scope.querySelector(STATS_DOCK_SELECTOR) !== null)
559
+ observer.disconnect();
560
+ };
561
+ const observer = new MutationObserver(settle);
468
562
  observer.observe(scope, { childList: true, subtree: true });
469
- return () => { observer.disconnect(); };
563
+ settle();
564
+ return () => {
565
+ observer.disconnect();
566
+ };
470
567
  }, []);
471
568
  useEffect(() => {
472
569
  if (displayMode === 'hidden')
@@ -480,8 +577,12 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
480
577
  const interval = rotationMs !== undefined && Number.isFinite(rotationMs) && rotationMs > 0
481
578
  ? rotationMs
482
579
  : BADGE_ROTATION_INTERVAL_MS;
483
- const timer = setInterval(() => { setRotation(step => step + 1); }, interval);
484
- return () => { clearInterval(timer); };
580
+ const timer = setInterval(() => {
581
+ setRotation((step) => step + 1);
582
+ }, interval);
583
+ return () => {
584
+ clearInterval(timer);
585
+ };
485
586
  }, [displayMode, rotationMs]);
486
587
  const seat = _jsx("span", { ref: seatRef, style: styles.seat, "aria-hidden": true });
487
588
  // `always` follows the live model, not the retained subscription: the
@@ -494,7 +595,9 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
494
595
  // Model scope only while the pill shows the current model's provider;
495
596
  // a rotated provider has no live model, so it reads at account level.
496
597
  const pillModel = displayMode === 'always'
497
- ? (current === collapsed[0]?.provider ? selection?.model : undefined)
598
+ ? current === collapsed[0]?.provider
599
+ ? selection?.model
600
+ : undefined
498
601
  : badgeSelection?.model;
499
602
  const label = compactSegment(collapsed[0], pillModel, translate);
500
603
  // The dialog opens from the badge, so it follows the provider and model the
@@ -503,7 +606,9 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
503
606
  // the provider that was showing when it opened instead of reordering live.
504
607
  const dialogProvider = open && displayMode === 'always' ? dialogLead : badgeSelection?.provider;
505
608
  const dialogModel = open && displayMode === 'always'
506
- ? (dialogLead === current ? selection?.model : undefined)
609
+ ? dialogLead === current
610
+ ? selection?.model
611
+ : undefined
507
612
  : badgeSelection?.model;
508
613
  const expanded = expandedDisplays(displays, dialogProvider);
509
614
  const title = translate('usageBadgeTitle');
@@ -514,18 +619,29 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
514
619
  if (next)
515
620
  void refresh();
516
621
  };
517
- const pill = (_jsxs("span", { ref: rootRef, style: styles.anchor, children: [_jsx("style", { children: subscriptionChromeCss }), _jsxs("button", { type: "button", className: "dsh-subscription-usage-pill", style: { ...styles.pill, ...(hover || open ? styles.pillActive : {}) }, "aria-haspopup": "dialog", "aria-expanded": open, "aria-label": `${title} · ${label}`, title: title, onMouseEnter: () => { setHover(true); }, onMouseLeave: () => { setHover(false); }, onClick: toggle, children: [_jsx(UsageBadgeIcon, {}), _jsx("span", { style: styles.label, children: label })] }), open && createPortal(_jsxs("div", { ref: panelRef, role: "dialog", "aria-label": title, className: "dsh-subscription-usage-panel", style: { ...styles.panel, ...(pos ?? MEASURE_STYLE) }, children: [_jsx("div", { style: styles.title, children: _jsxs("span", { style: styles.titleLabel, children: [_jsx(UsageBadgeIcon, {}), title] }) }), _jsx("div", { style: styles.titleRule, "aria-hidden": true }), expanded.map((d, index) => (_jsxs("section", { style: index === 0 ? undefined : styles.section, children: [_jsxs("div", { style: styles.providerRow, children: [_jsxs("span", { style: styles.providerName, children: [d.name, d.provider === current && _jsx("span", { style: styles.currentTag, children: translate('usageBadgeCurrent') })] }), d.accounts.length === 1 && _jsx(AccountMeta, { account: d.accounts[0], translate: translate })] }), d.accounts.map((account, accountIndex) => (_jsxs("div", { style: accountIndex === 0 ? undefined : styles.accountBlock, children: [d.accounts.length > 1 && (_jsx("div", { style: styles.accountRow, children: _jsx(AccountMeta, { account: account, translate: translate }) })), _jsx(AccountWindows, { windows: account.windows, observedAt: account.observedAt, stale: account.stale, model: d.provider === dialogProvider ? dialogModel : undefined, provider: d.provider, translate: translate }, `${d.provider}:${dialogModel ?? ''}`)] }, account.key)))] }, d.provider)))] }), document.body)] }));
518
- return (_jsxs(_Fragment, { children: [seat, statsRow !== null && statsRow.isConnected ? createPortal(pill, statsRow) : pill] }));
622
+ const pill = (_jsxs("span", { ref: rootRef, style: styles.anchor, children: [_jsx("style", { children: subscriptionChromeCss }), _jsxs("button", { type: "button", className: "dsh-subscription-usage-pill", style: { ...styles.pill, ...(hover || open ? styles.pillActive : {}) }, "aria-haspopup": "dialog", "aria-expanded": open, "aria-label": `${title} · ${label}`, title: title, onMouseEnter: () => {
623
+ setHover(true);
624
+ }, onMouseLeave: () => {
625
+ setHover(false);
626
+ }, onClick: toggle, children: [_jsx(UsageBadgeIcon, {}), _jsx("span", { style: styles.label, children: label })] }), open &&
627
+ createPortal(_jsxs("div", { ref: panelRef, role: "dialog", "aria-label": title, className: "dsh-subscription-usage-panel", style: { ...styles.panel, ...(pos ?? MEASURE_STYLE) }, children: [_jsx("div", { style: styles.title, children: _jsxs("span", { style: styles.titleLabel, children: [_jsx(UsageBadgeIcon, {}), title] }) }), _jsx("div", { style: styles.titleRule, "aria-hidden": true }), expanded.map((d, index) => (_jsxs("section", { style: index === 0 ? undefined : styles.section, children: [_jsxs("div", { style: styles.providerRow, children: [_jsxs("span", { style: styles.providerName, children: [d.name, d.provider === current && (_jsx("span", { style: styles.currentTag, children: translate('usageBadgeCurrent') }))] }), d.accounts.length === 1 && (_jsx(AccountMeta, { account: d.accounts[0], translate: translate }))] }), d.accounts.map((account, accountIndex) => (_jsxs("div", { style: accountIndex === 0 ? undefined : styles.accountBlock, children: [d.accounts.length > 1 && (_jsx("div", { style: styles.accountRow, children: _jsx(AccountMeta, { account: account, translate: translate }) })), _jsx(AccountWindows, { windows: account.windows, observedAt: account.observedAt, stale: account.stale, model: d.provider === dialogProvider ? dialogModel : undefined, provider: d.provider, translate: translate }, `${d.provider}:${dialogModel ?? ''}`)] }, account.key)))] }, d.provider)))] }), document.body)] }));
628
+ return (_jsxs(_Fragment, { children: [seat, statsRow !== null && statsRow.isConnected
629
+ ? createPortal(pill, statsRow)
630
+ : pill] }));
519
631
  }
632
+ /** How far statsScopeOf walks looking for `composerStats.row`. */
633
+ const LEGACY_STATS_SCOPE_DEPTH = 4;
520
634
  /**
521
- * Nearest ancestor of the dock seat that can contain the host's stats row:
522
- * the composer bar. Bounded so a badge in an unfamiliar layout never adopts
523
- * some other composer's pills.
635
+ * Scope for the legacy stats row. The nearest ancestor within
636
+ * LEGACY_STATS_SCOPE_DEPTH that contains `composerStats.row`, so a badge
637
+ * does not adopt another composer's pills. When none does, the seat's
638
+ * parent: the slot outlet, which holds sibling `composerStats.dock` anchors
639
+ * on hosts whose pills are dock entries.
524
640
  */
525
641
  function statsScopeOf(seat) {
526
642
  let node = seat.parentElement;
527
- for (let depth = 0; node !== null && depth < 4; depth++) {
528
- if (node.querySelector('[data-composer-stats]') !== null)
643
+ for (let depth = 0; node !== null && depth < LEGACY_STATS_SCOPE_DEPTH; depth++) {
644
+ if (node.querySelector(STATS_ROW_SELECTOR) !== null)
529
645
  return node;
530
646
  node = node.parentElement;
531
647
  }
@@ -536,21 +652,25 @@ function statsScopeOf(seat) {
536
652
  * marks the default account (the one direct routes serve and the collapsed
537
653
  * pill reads), the same glyph Settings → 订阅 uses.
538
654
  */
539
- function AccountMeta({ account, translate }) {
540
- const parts = [account.account, account.plan === undefined ? undefined : translate('usagePlan', { plan: account.plan })]
541
- .filter((part) => part !== undefined && part !== '');
542
- return (_jsxs("span", { style: styles.providerMeta, title: account.account, children: [account.isDefault && _jsx("span", { style: styles.defaultStar, "aria-label": "default", children: "\u2605 " }), parts.join(' · ')] }));
655
+ function AccountMeta({ account, translate, }) {
656
+ const parts = [
657
+ account.account,
658
+ account.plan === undefined
659
+ ? undefined
660
+ : translate('usagePlan', { plan: account.plan }),
661
+ ].filter((part) => part !== undefined && part !== '');
662
+ return (_jsxs("span", { style: styles.providerMeta, title: account.account, children: [account.isDefault && (_jsxs("span", { style: styles.defaultStar, "aria-label": "default", children: ["\u2605", ' '] })), parts.join(' · ')] }));
543
663
  }
544
664
  /** Preview each account independently; all remaining quotas stay accessible. */
545
- export function AccountWindows({ windows, model, provider, translate, observedAt, stale }) {
665
+ export function AccountWindows({ windows, model, provider, translate, observedAt, stale, }) {
546
666
  const { shown, hidden } = previewWindows(windows, model, provider);
547
667
  const rows = (items) => (_jsx("dl", { style: styles.details, children: items.map((w, i) => (_jsx(WindowRow, { label: `${usageWindowLabel(translate, w)}${model !== undefined && w.scope === model ? ` · ${translate('usageBadgeCurrent')}` : ''}`, window: w, t: translate, observedAt: observedAt, stale: stale }, i))) }));
548
- return _jsxs(_Fragment, { children: [rows(shown), hidden.length > 0 && _jsxs("details", { style: styles.moreWindows, children: [_jsx("summary", { style: styles.moreSummary, children: translate('usageBadgeMoreWindows', { count: hidden.length }) }), rows(hidden)] })] });
668
+ return (_jsxs(_Fragment, { children: [rows(shown), hidden.length > 0 && (_jsxs("details", { style: styles.moreWindows, children: [_jsx("summary", { style: styles.moreSummary, children: translate('usageBadgeMoreWindows', { count: hidden.length }) }), rows(hidden)] }))] }));
549
669
  }
550
670
  /** One `dt`/`dd` pair: window name → `25% · 6d1h`, with the bar underneath. */
551
- function WindowRow({ label, window: w, t, observedAt, stale }) {
671
+ function WindowRow({ label, window: w, t, observedAt, stale, }) {
552
672
  const percent = displayUsedPercent(w.usedPercent);
553
- return (_jsxs(_Fragment, { children: [_jsx("dt", { style: styles.dt, children: label }), _jsxs("dd", { style: styles.dd, children: [percent === undefined ? t('usageMeterInvalid') : `${percent}%`, w.resetsAt !== undefined && Number.isFinite(w.resetsAt) && _jsxs("span", { style: styles.reset, children: [" \u00B7 ", windowLabel(w)] })] }), _jsx(UsageMeter, { window: w, t: t, style: styles.bar, observedAt: observedAt, stale: stale })] }));
673
+ return (_jsxs(_Fragment, { children: [_jsx("dt", { style: styles.dt, children: label }), _jsxs("dd", { style: styles.dd, children: [percent === undefined ? t('usageMeterInvalid') : `${percent}%`, w.resetsAt !== undefined && Number.isFinite(w.resetsAt) && (_jsxs("span", { style: styles.reset, children: [" \u00B7 ", windowLabel(w)] }))] }), _jsx(UsageMeter, { window: w, t: t, style: styles.bar, observedAt: observedAt, stale: stale })] }));
554
674
  }
555
675
  /**
556
676
  * Unplaced portal panel: hidden but laid out so the clamp measures real
@@ -563,12 +683,22 @@ const styles = {
563
683
  // Mirrors the host StatsPills pill so the badge reads as a sibling of the
564
684
  // shipped time/token pills.
565
685
  pill: {
566
- boxSizing: 'border-box', maxWidth: '100%',
686
+ boxSizing: 'border-box',
687
+ maxWidth: '100%',
567
688
  color: 'var(--dsw-alias-label-tertiary)',
568
- font: 'inherit', fontSize: 'calc(var(--dsh-content-font-size-secondary, 13px) - 1px)',
569
- fontVariantNumeric: 'tabular-nums', lineHeight: 'calc(20px + var(--dsh-content-font-delta-secondary, 0px))', whiteSpace: 'nowrap',
570
- background: 'transparent', border: 'none', borderRadius: 999,
571
- alignItems: 'center', gap: 6, padding: '1px 8px', display: 'inline-flex', cursor: 'pointer',
689
+ font: 'inherit',
690
+ fontSize: 'calc(var(--dsh-content-font-size-secondary, 13px) - 1px)',
691
+ fontVariantNumeric: 'tabular-nums',
692
+ lineHeight: 'calc(20px + var(--dsh-content-font-delta-secondary, 0px))',
693
+ whiteSpace: 'nowrap',
694
+ background: 'transparent',
695
+ border: 'none',
696
+ borderRadius: 999,
697
+ alignItems: 'center',
698
+ gap: 6,
699
+ padding: '1px 8px',
700
+ display: 'inline-flex',
701
+ cursor: 'pointer',
572
702
  },
573
703
  pillActive: {
574
704
  background: 'var(--dsw-alias-interactive-bg-hover)',
@@ -580,52 +710,111 @@ const styles = {
580
710
  // without the blur the transcript behind the dialog stays sharp and shows
581
711
  // through the fill.
582
712
  panel: {
583
- position: 'fixed', zIndex: 1100, boxSizing: 'border-box',
713
+ position: 'fixed',
714
+ zIndex: 1100,
715
+ boxSizing: 'border-box',
584
716
  background: 'var(--dsw-specific-menu)',
585
717
  backdropFilter: 'var(--dsw-menu-backdrop-filter)',
586
- width: 'max-content', minWidth: 'min(300px, 100vw - 24px)', maxWidth: 'min(440px, 100vw - 24px)',
587
- maxHeight: 'min(560px, 100dvh - 24px)', overflowY: 'auto', overscrollBehavior: 'contain',
718
+ width: 'max-content',
719
+ minWidth: 'min(300px, 100vw - 24px)',
720
+ maxWidth: 'min(440px, 100vw - 24px)',
721
+ maxHeight: 'min(560px, 100dvh - 24px)',
722
+ overflowY: 'auto',
723
+ overscrollBehavior: 'contain',
588
724
  boxShadow: 'var(--dsw-elevation-prominent)',
589
- color: 'var(--dsw-alias-label-secondary)', cursor: 'default',
590
- border: 0, borderRadius: 'var(--dsw-radius-lg, 16px)', padding: 16, fontSize: 12, lineHeight: '18px',
725
+ color: 'var(--dsw-alias-label-secondary)',
726
+ cursor: 'default',
727
+ border: 0,
728
+ borderRadius: 'var(--dsw-radius-lg, 16px)',
729
+ padding: 16,
730
+ fontSize: 12,
731
+ lineHeight: '18px',
591
732
  },
592
733
  title: {
593
- color: 'var(--dsw-alias-label-primary)', display: 'flex',
594
- justifyContent: 'space-between', gap: 16, marginBottom: 8, fontWeight: 500,
734
+ color: 'var(--dsw-alias-label-primary)',
735
+ display: 'flex',
736
+ justifyContent: 'space-between',
737
+ gap: 16,
738
+ marginBottom: 8,
739
+ fontWeight: 500,
740
+ },
741
+ titleLabel: {
742
+ alignItems: 'center',
743
+ gap: 6,
744
+ minWidth: 0,
745
+ display: 'inline-flex',
746
+ },
747
+ titleRule: {
748
+ borderTop: '0.5px solid var(--dsw-alias-border-l2)',
749
+ marginBottom: 10,
750
+ },
751
+ section: {
752
+ marginTop: 12,
753
+ paddingTop: 10,
754
+ borderTop: '0.5px solid var(--dsw-alias-border-l2)',
595
755
  },
596
- titleLabel: { alignItems: 'center', gap: 6, minWidth: 0, display: 'inline-flex' },
597
- titleRule: { borderTop: '0.5px solid var(--dsw-alias-border-l2)', marginBottom: 10 },
598
- section: { marginTop: 12, paddingTop: 10, borderTop: '0.5px solid var(--dsw-alias-border-l2)' },
599
756
  providerRow: {
600
- display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 16, marginBottom: 6,
757
+ display: 'flex',
758
+ justifyContent: 'space-between',
759
+ alignItems: 'baseline',
760
+ gap: 16,
761
+ marginBottom: 6,
762
+ },
763
+ providerName: {
764
+ color: 'var(--dsw-alias-label-primary)',
765
+ fontWeight: 500,
766
+ display: 'inline-flex',
767
+ alignItems: 'center',
768
+ gap: 6,
601
769
  },
602
- providerName: { color: 'var(--dsw-alias-label-primary)', fontWeight: 500, display: 'inline-flex', alignItems: 'center', gap: 6 },
603
770
  currentTag: {
604
- fontSize: 10, lineHeight: '14px', fontWeight: 400, padding: '0 5px', borderRadius: 7,
605
- color: 'var(--dsw-alias-label-secondary)', background: 'var(--dsw-alias-interactive-bg-hover)',
771
+ fontSize: 10,
772
+ lineHeight: '14px',
773
+ fontWeight: 400,
774
+ padding: '0 5px',
775
+ borderRadius: 7,
776
+ color: 'var(--dsw-alias-label-secondary)',
777
+ background: 'var(--dsw-alias-interactive-bg-hover)',
606
778
  },
607
779
  providerMeta: {
608
- color: 'var(--dsw-alias-label-tertiary)', minWidth: 0, overflow: 'hidden',
609
- textOverflow: 'ellipsis', whiteSpace: 'nowrap',
780
+ color: 'var(--dsw-alias-label-tertiary)',
781
+ minWidth: 0,
782
+ overflow: 'hidden',
783
+ textOverflow: 'ellipsis',
784
+ whiteSpace: 'nowrap',
610
785
  },
611
786
  // Same star and color as the default-account marker in Settings → 订阅.
612
787
  defaultStar: { color: 'var(--dsw-alias-state-warn-label)' },
613
788
  accountBlock: { marginTop: 8 },
614
789
  accountRow: { display: 'flex', marginBottom: 4 },
615
790
  details: {
616
- color: 'var(--dsw-alias-label-tertiary)', display: 'grid',
617
- gridTemplateColumns: 'minmax(0, 1fr) max-content', gap: '4px 16px', margin: 0,
791
+ color: 'var(--dsw-alias-label-tertiary)',
792
+ display: 'grid',
793
+ gridTemplateColumns: 'minmax(0, 1fr) max-content',
794
+ gap: '4px 16px',
795
+ margin: 0,
618
796
  },
619
797
  moreWindows: { marginTop: 8 },
620
- moreSummary: { cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)', marginBottom: 8 },
798
+ moreSummary: {
799
+ cursor: 'pointer',
800
+ color: 'var(--dsw-alias-label-secondary)',
801
+ marginBottom: 8,
802
+ },
621
803
  dt: { minWidth: 0, margin: 0, overflowWrap: 'anywhere' },
622
804
  dd: {
623
- minWidth: 0, margin: 0, color: 'var(--dsw-alias-label-secondary)',
624
- fontVariantNumeric: 'tabular-nums', textAlign: 'right',
805
+ minWidth: 0,
806
+ margin: 0,
807
+ color: 'var(--dsw-alias-label-secondary)',
808
+ fontVariantNumeric: 'tabular-nums',
809
+ textAlign: 'right',
625
810
  },
626
811
  reset: { color: 'var(--dsw-alias-label-tertiary)' },
627
812
  bar: {
628
- gridColumn: '1 / -1', height: 4, borderRadius: 2, overflow: 'hidden',
629
- background: 'var(--dsw-alias-border-l2)', marginBottom: 2,
813
+ gridColumn: '1 / -1',
814
+ height: 4,
815
+ borderRadius: 2,
816
+ overflow: 'hidden',
817
+ background: 'var(--dsw-alias-border-l2)',
818
+ marginBottom: 2,
630
819
  },
631
820
  };