dsh-subscriptions 0.2.0 → 0.2.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.
Files changed (105) hide show
  1. package/README.md +220 -180
  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 +8 -4
  6. package/lib/auth/rpc.js +89 -25
  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 +1 -1
  16. package/lib/client/OllamaCard.js +26 -8
  17. package/lib/client/OllamaSettingsManager.js +113 -30
  18. package/lib/client/ProviderAccountManager.d.ts +1 -1
  19. package/lib/client/ProviderAccountManager.js +164 -49
  20. package/lib/client/ProviderModelEditor.js +137 -34
  21. package/lib/client/SpeedSelect.d.ts +62 -14
  22. package/lib/client/SpeedSelect.js +261 -49
  23. package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
  24. package/lib/client/SubscriptionUsageBadge.js +300 -112
  25. package/lib/client/SubscriptionsSection.d.ts +2 -0
  26. package/lib/client/SubscriptionsSection.js +265 -85
  27. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  28. package/lib/client/UsageMeter.d.ts +1 -1
  29. package/lib/client/UsageMeter.js +62 -24
  30. package/lib/client/VideoGenerateToolview.js +69 -17
  31. package/lib/client/account-preferences.d.ts +8 -0
  32. package/lib/client/account-preferences.js +20 -5
  33. package/lib/client/host-contract.d.ts +11 -2
  34. package/lib/client/host-contract.js +20 -3
  35. package/lib/client/index.d.ts +5 -5
  36. package/lib/client/index.js +34 -12
  37. package/lib/client/locales.d.ts +22 -0
  38. package/lib/client/locales.js +73 -51
  39. package/lib/client/model-filter.d.ts +37 -0
  40. package/lib/client/model-filter.js +45 -0
  41. package/lib/client/ollama-draft.js +21 -12
  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.js +30 -12
  48. package/lib/client/usage-window-filter.d.ts +7 -0
  49. package/lib/client/usage-window-filter.js +12 -0
  50. package/lib/client.js +480 -136
  51. package/lib/client.js.map +1 -1
  52. package/lib/http.js +1 -1
  53. package/lib/index.d.ts +3 -3
  54. package/lib/index.js +167 -76
  55. package/lib/model-defaults.js +4 -2
  56. package/lib/provider-settings.d.ts +17 -0
  57. package/lib/provider-settings.js +79 -16
  58. package/lib/providers/account-preferences.js +51 -22
  59. package/lib/providers/accounts.js +15 -9
  60. package/lib/providers/antigravity-oauth-client.js +2 -2
  61. package/lib/providers/antigravity.js +141 -68
  62. package/lib/providers/catalog-store.js +120 -41
  63. package/lib/providers/claude-cli-version.js +3 -3
  64. package/lib/providers/claude.d.ts +9 -2
  65. package/lib/providers/claude.js +153 -90
  66. package/lib/providers/codex-client-version.js +3 -3
  67. package/lib/providers/codex-search.js +40 -13
  68. package/lib/providers/codex.js +196 -112
  69. package/lib/providers/common.js +71 -39
  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.js +14 -5
  77. package/lib/providers/external-usage.js +59 -23
  78. package/lib/providers/grok.js +152 -89
  79. package/lib/providers/image-pool.js +29 -11
  80. package/lib/providers/minimax-usage.js +31 -11
  81. package/lib/providers/npm-cli-version.js +10 -3
  82. package/lib/providers/ollama-settings.js +24 -8
  83. package/lib/providers/ollama-web.js +29 -11
  84. package/lib/providers/ollama.js +129 -66
  85. package/lib/providers/pool-family.js +11 -3
  86. package/lib/providers/pool-health.js +28 -8
  87. package/lib/providers/pool-usage.js +16 -9
  88. package/lib/providers/pool.d.ts +8 -0
  89. package/lib/providers/pool.js +81 -42
  90. package/lib/providers/rate-limit.js +8 -2
  91. package/lib/providers/replay.js +40 -17
  92. package/lib/providers/reset-redemption.js +18 -6
  93. package/lib/tools/image-generate.js +123 -65
  94. package/lib/tools/registration.js +5 -2
  95. package/lib/tools/video-generate.js +51 -31
  96. package/lib/tools/x-search.js +50 -22
  97. package/lib/translate/anthropic.js +85 -37
  98. package/lib/translate/antigravity-schema.js +71 -28
  99. package/lib/translate/antigravity-thinking.js +22 -9
  100. package/lib/translate/antigravity.js +151 -52
  101. package/lib/translate/chat-completions.js +67 -23
  102. package/lib/translate/resolved.js +55 -21
  103. package/lib/translate/responses.js +69 -29
  104. package/lib/translate/sse.js +4 -1
  105. package/package.json +27 -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,7 +63,7 @@ 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',
67
68
  'ollama-cloud': 'Ollama Cloud',
68
69
  };
@@ -91,13 +92,17 @@ export function groupUsageDisplays(roster, windowsOf, plans) {
91
92
  const row = {
92
93
  key: account.key,
93
94
  isDefault: account.isDefault,
94
- ...account.account === undefined ? {} : { account: account.account },
95
- ...plan === undefined ? {} : { plan },
95
+ ...(account.account === undefined ? {} : { account: account.account }),
96
+ ...(plan === undefined ? {} : { plan }),
96
97
  windows,
97
98
  };
98
99
  const display = byProvider.get(provider);
99
100
  if (display === undefined)
100
- 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
+ });
101
106
  else
102
107
  display.accounts.push(row);
103
108
  }
@@ -113,7 +118,13 @@ export async function loadBadgeRoster(rpc) {
113
118
  const roster = [];
114
119
  const refreshed = new Set();
115
120
  if (subscriptions.status === 'fulfilled') {
116
- 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
+ ]) {
117
128
  refreshed.add(provider);
118
129
  for (const account of accountsOf(subscriptions.value.providers[provider]))
119
130
  roster.push({ provider, account });
@@ -123,17 +134,23 @@ export async function loadBadgeRoster(rpc) {
123
134
  refreshed.add('cursor-subscription');
124
135
  if (cursor.value.authenticated)
125
136
  roster.push({
126
- provider: 'cursor-subscription', account: { key: 'cursor', isDefault: true },
137
+ provider: 'cursor-subscription',
138
+ account: { key: 'cursor', isDefault: true },
127
139
  });
128
140
  }
129
141
  if (external.status === 'fulfilled') {
130
142
  for (const [source, provider] of [
131
- ['opencode-go', 'opencode-go'], ['kimi-code', 'kimi-coding'], ['minimax', 'minimax'], ['minimax-cn', 'minimax-cn'], ['ollama-cloud', 'ollama-cloud'],
143
+ ['opencode-go', 'opencode-go'],
144
+ ['kimi-code', 'kimi-coding'],
145
+ ['minimax', 'minimax'],
146
+ ['minimax-cn', 'minimax-cn'],
147
+ ['ollama-cloud', 'ollama-cloud'],
132
148
  ]) {
133
149
  refreshed.add(provider);
134
150
  if (external.value[source]?.configured)
135
151
  roster.push({
136
- provider, account: { key: source, isDefault: true },
152
+ provider,
153
+ account: { key: source, isDefault: true },
137
154
  });
138
155
  }
139
156
  }
@@ -142,8 +159,14 @@ export async function loadBadgeRoster(rpc) {
142
159
  export async function usageOf(rpc, { provider, account }) {
143
160
  if (provider === 'cursor-subscription')
144
161
  return callSubscriptionsAuth(rpc, 'cursorUsage', {});
145
- if (provider === 'opencode-go' || provider === 'kimi-coding' || provider === 'minimax' || provider === 'minimax-cn' || provider === 'ollama-cloud') {
146
- 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
+ });
147
170
  }
148
171
  return callSubscriptionsAuth(rpc, 'usage', { provider, account: account.key });
149
172
  }
@@ -173,9 +196,12 @@ export function windowLabel(w) {
173
196
  if (w.scope !== undefined && w.scope !== '')
174
197
  return w.scope;
175
198
  switch (w.kind) {
176
- case 'session': return '5h';
177
- case 'weekly': return 'Wk';
178
- default: return 'W';
199
+ case 'session':
200
+ return '5h';
201
+ case 'weekly':
202
+ return 'Wk';
203
+ default:
204
+ return 'W';
179
205
  }
180
206
  }
181
207
  const ms = Math.max(0, w.resetsAt - Date.now());
@@ -198,10 +224,12 @@ function windowSegment(label, w) {
198
224
  }
199
225
  /** Keep model quotas separate: matching percentages do not imply a shared pool. */
200
226
  export function prioritizeWindows(windows, model) {
201
- return model === undefined ? [...windows] : [
202
- ...windows.filter(w => w.scope === model),
203
- ...windows.filter(w => w.scope !== model),
204
- ];
227
+ return model === undefined
228
+ ? [...windows]
229
+ : [
230
+ ...windows.filter((w) => w.scope === model),
231
+ ...windows.filter((w) => w.scope !== model),
232
+ ];
205
233
  }
206
234
  /** Small previews keep a live model catalog from taking over the dialog. */
207
235
  export const WINDOW_PREVIEW_LIMIT = 4;
@@ -209,7 +237,9 @@ export function previewWindows(windows, model, provider) {
209
237
  const ordered = prioritizeWindows(windows, model);
210
238
  // Antigravity has many model-specific quotas: preview only the current model.
211
239
  const limit = provider === 'antigravity'
212
- ? 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)
213
243
  : WINDOW_PREVIEW_LIMIT;
214
244
  return { shown: ordered.slice(0, limit), hidden: ordered.slice(limit) };
215
245
  }
@@ -217,27 +247,34 @@ export function previewWindows(windows, model, provider) {
217
247
  export function compactSegment(d, model, t = fallbackTranslate) {
218
248
  const windows = pillAccountOf(d).windows;
219
249
  if (d.provider === 'antigravity') {
220
- const matching = model === undefined ? [] : windows.filter(w => w.scope === model);
250
+ const matching = model === undefined ? [] : windows.filter((w) => w.scope === model);
221
251
  if (matching.length === 0) {
222
- return `${d.name} ${t(model === undefined ? 'usageBadgeModelCount' : 'usageBadgeModelUnavailable', {
223
- 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,
224
256
  })}`;
225
257
  }
226
- 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));
227
261
  return `${d.name} ${parts.join(' · ')}`;
228
262
  }
229
- 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));
230
264
  if (windows.length > 2)
231
265
  parts.push(`+${windows.length - 2}`);
232
266
  return `${d.name} ${parts.join(' · ')}`;
233
267
  }
234
268
  /** Remember only the most recent subscription in this view, including its model scope. */
235
269
  export function retainSubscriptionSelection(previous, current) {
236
- 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;
237
274
  }
238
275
  /** At most one provider; no known selection or no usage means no badge. */
239
276
  export function collapsedDisplays(displays, current) {
240
- const match = displays.find(d => d.provider === current);
277
+ const match = displays.find((d) => d.provider === current);
241
278
  return match === undefined ? [] : [match];
242
279
  }
243
280
  /**
@@ -255,16 +292,20 @@ export function collapsedDisplays(displays, current) {
255
292
  export function rotatingDisplay(displays, current, step) {
256
293
  if (displays.length === 0)
257
294
  return [];
258
- const match = displays.find(d => d.provider === current);
295
+ const match = displays.find((d) => d.provider === current);
259
296
  if (match !== undefined)
260
297
  return [match];
261
298
  const index = Number.isFinite(step) ? Math.floor(step) : 0;
262
- return [displays[((index % displays.length) + displays.length) % displays.length]];
299
+ return [
300
+ displays[((index % displays.length) + displays.length) % displays.length],
301
+ ];
263
302
  }
264
303
  /** Order for the expanded dialog: the current provider first, the rest in poll order. */
265
304
  export function expandedDisplays(displays, current) {
266
- const match = displays.find(d => d.provider === current);
267
- 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)];
268
309
  }
269
310
  /**
270
311
  * A provider's logged-in accounts, the effective default first. When no
@@ -274,9 +315,9 @@ export function expandedDisplays(displays, current) {
274
315
  function accountsOf(status) {
275
316
  if (status === undefined || status.accounts.length === 0)
276
317
  return [];
277
- const fallback = status.accounts.find(a => a.isDefault) ?? status.accounts[0];
318
+ const fallback = status.accounts.find((a) => a.isDefault) ?? status.accounts[0];
278
319
  return status.accounts
279
- .map(a => (a === fallback ? { ...a, isDefault: true } : a))
320
+ .map((a) => (a === fallback ? { ...a, isDefault: true } : a))
280
321
  .sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
281
322
  }
282
323
  /** English-dictionary fallback for a missing inject `t` (standalone renders). */
@@ -287,8 +328,12 @@ function fallbackTranslate(key, params) {
287
328
  function usageWindowLabel(t, window) {
288
329
  const base = window.kind === 'session'
289
330
  ? t('usageSession')
290
- : window.kind === 'weekly' ? t('usageWeekly') : t('usageWindow');
291
- 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;
292
337
  }
293
338
  /**
294
339
  * The composer subscription-usage badge: a pill reading e.g.
@@ -296,7 +341,9 @@ function usageWindowLabel(t, window) {
296
341
  * every provider's accounts and their windows. Returns null when no data is
297
342
  * available.
298
343
  */
299
- 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, }) {
300
347
  const translate = t ?? fallbackTranslate;
301
348
  const [displays, setDisplays] = useState([]);
302
349
  const [selection, setSelection] = useState(undefined);
@@ -343,7 +390,8 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
343
390
  // real signal, unlike a fetch failure.
344
391
  const live = new Set(roster.map(({ provider, account }) => usageKeyOf(provider, account)));
345
392
  for (const key of lastKnown.keys()) {
346
- if (refreshed.has(key.split(':', 1)[0]) && !live.has(key)) {
393
+ if (refreshed.has(key.split(':', 1)[0]) &&
394
+ !live.has(key)) {
347
395
  lastKnown.delete(key);
348
396
  observationsRef.current.delete(key);
349
397
  }
@@ -356,7 +404,10 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
356
404
  return;
357
405
  for (const { provider, account } of roster) {
358
406
  const key = usageKeyOf(provider, account);
359
- observationsRef.current.set(key, { ...observationsRef.current.get(key), stale: true });
407
+ observationsRef.current.set(key, {
408
+ ...observationsRef.current.get(key),
409
+ stale: true,
410
+ });
360
411
  }
361
412
  const plans = new Map();
362
413
  for (const r of results) {
@@ -372,24 +423,39 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
372
423
  continue;
373
424
  }
374
425
  lastKnown.set(key, usage.windows);
375
- 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
+ });
376
430
  }
377
- setDisplays(previous => [
378
- ...groupUsageDisplays(roster, lastKnown, plans).map(display => ({ ...display,
379
- 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,
380
436
  ...observationsRef.current.get(`${display.provider}:${account.key}`),
381
437
  })),
382
438
  })),
383
- ...previous.filter(display => !refreshed.has(display.provider)).map(display => ({ ...display,
384
- 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
+ })),
385
447
  })),
386
448
  ]);
387
449
  }
388
450
  catch {
389
451
  // Keep cached numbers, but never present failed observations as current.
390
452
  if (mountedRef.current)
391
- setDisplays(previous => previous.map(display => ({ ...display,
392
- 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
+ })),
393
459
  })));
394
460
  }
395
461
  finally {
@@ -398,14 +464,18 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
398
464
  }, [rpc]);
399
465
  useEffect(() => {
400
466
  mountedRef.current = true;
401
- return () => { mountedRef.current = false; };
467
+ return () => {
468
+ mountedRef.current = false;
469
+ };
402
470
  }, []);
403
471
  // Hidden renders nothing, so it polls nothing; showing it again refreshes at once.
404
472
  useEffect(() => {
405
473
  if (displayMode === 'hidden')
406
474
  return;
407
475
  void refresh();
408
- const timer = setInterval(() => { void refresh(); }, USAGE_POLL_INTERVAL_MS);
476
+ const timer = setInterval(() => {
477
+ void refresh();
478
+ }, USAGE_POLL_INTERVAL_MS);
409
479
  window.addEventListener(USAGE_BADGE_REFRESH_EVENT, refresh);
410
480
  return () => {
411
481
  clearInterval(timer);
@@ -422,12 +492,18 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
422
492
  if (read === undefined || inflight)
423
493
  return;
424
494
  inflight = true;
425
- void read().then((model) => {
495
+ void read()
496
+ .then((model) => {
426
497
  if (cancelled)
427
498
  return;
428
- setLastSubscription(previous => retainSubscriptionSelection(previous, model));
499
+ setLastSubscription((previous) => retainSubscriptionSelection(previous, model));
429
500
  setSelection(model);
430
- }, () => { }).finally(() => { inflight = false; });
501
+ }, () => {
502
+ /* keep the last known provider; the next tick retries */
503
+ })
504
+ .finally(() => {
505
+ inflight = false;
506
+ });
431
507
  };
432
508
  reload();
433
509
  const timer = setInterval(reload, MODEL_POLL_INTERVAL_MS);
@@ -436,7 +512,14 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
436
512
  clearInterval(timer);
437
513
  };
438
514
  }, []);
439
- 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
+ });
440
523
  useDismissOnOutsidePointer(rootRef, open, setOpen, panelRef);
441
524
  useEffect(() => {
442
525
  if (!open)
@@ -446,15 +529,20 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
446
529
  setOpen(false);
447
530
  };
448
531
  document.addEventListener('keydown', onKeyDown);
449
- return () => { document.removeEventListener('keydown', onKeyDown); };
532
+ return () => {
533
+ document.removeEventListener('keydown', onKeyDown);
534
+ };
450
535
  }, [open]);
451
- // Sit on the host's stats row when there is one. Every dock entry is its
452
- // own row in the composer bar, so a badge rendered in place lands under the
453
- // shipped time/token pills; the host marks its pill row with
454
- // `data-composer-stats`, and rendering into it makes the badge a third pill
455
- // on that line. The marker is watched (the row mounts only once the session
456
- // has steps or tokens, and unmounts with them) and older hosts without it
457
- // 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.
458
546
  const [statsRow, setStatsRow] = useState(null);
459
547
  useEffect(() => {
460
548
  const seat = seatRef.current;
@@ -463,11 +551,19 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
463
551
  const scope = statsScopeOf(seat);
464
552
  if (scope === null)
465
553
  return;
466
- const find = () => scope.querySelector('[data-composer-stats]');
467
- setStatsRow(find());
468
- 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);
469
562
  observer.observe(scope, { childList: true, subtree: true });
470
- return () => { observer.disconnect(); };
563
+ settle();
564
+ return () => {
565
+ observer.disconnect();
566
+ };
471
567
  }, []);
472
568
  useEffect(() => {
473
569
  if (displayMode === 'hidden')
@@ -481,8 +577,12 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
481
577
  const interval = rotationMs !== undefined && Number.isFinite(rotationMs) && rotationMs > 0
482
578
  ? rotationMs
483
579
  : BADGE_ROTATION_INTERVAL_MS;
484
- const timer = setInterval(() => { setRotation(step => step + 1); }, interval);
485
- return () => { clearInterval(timer); };
580
+ const timer = setInterval(() => {
581
+ setRotation((step) => step + 1);
582
+ }, interval);
583
+ return () => {
584
+ clearInterval(timer);
585
+ };
486
586
  }, [displayMode, rotationMs]);
487
587
  const seat = _jsx("span", { ref: seatRef, style: styles.seat, "aria-hidden": true });
488
588
  // `always` follows the live model, not the retained subscription: the
@@ -495,7 +595,9 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
495
595
  // Model scope only while the pill shows the current model's provider;
496
596
  // a rotated provider has no live model, so it reads at account level.
497
597
  const pillModel = displayMode === 'always'
498
- ? (current === collapsed[0]?.provider ? selection?.model : undefined)
598
+ ? current === collapsed[0]?.provider
599
+ ? selection?.model
600
+ : undefined
499
601
  : badgeSelection?.model;
500
602
  const label = compactSegment(collapsed[0], pillModel, translate);
501
603
  // The dialog opens from the badge, so it follows the provider and model the
@@ -504,7 +606,9 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
504
606
  // the provider that was showing when it opened instead of reordering live.
505
607
  const dialogProvider = open && displayMode === 'always' ? dialogLead : badgeSelection?.provider;
506
608
  const dialogModel = open && displayMode === 'always'
507
- ? (dialogLead === current ? selection?.model : undefined)
609
+ ? dialogLead === current
610
+ ? selection?.model
611
+ : undefined
508
612
  : badgeSelection?.model;
509
613
  const expanded = expandedDisplays(displays, dialogProvider);
510
614
  const title = translate('usageBadgeTitle');
@@ -515,18 +619,29 @@ export function SubscriptionUsageBadge({ rpc, currentModel, t, rotationMs }) {
515
619
  if (next)
516
620
  void refresh();
517
621
  };
518
- 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)] }));
519
- 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] }));
520
631
  }
632
+ /** How far statsScopeOf walks looking for `composerStats.row`. */
633
+ const LEGACY_STATS_SCOPE_DEPTH = 4;
521
634
  /**
522
- * Nearest ancestor of the dock seat that can contain the host's stats row:
523
- * the composer bar. Bounded so a badge in an unfamiliar layout never adopts
524
- * 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.
525
640
  */
526
641
  function statsScopeOf(seat) {
527
642
  let node = seat.parentElement;
528
- for (let depth = 0; node !== null && depth < 4; depth++) {
529
- 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)
530
645
  return node;
531
646
  node = node.parentElement;
532
647
  }
@@ -537,21 +652,25 @@ function statsScopeOf(seat) {
537
652
  * marks the default account (the one direct routes serve and the collapsed
538
653
  * pill reads), the same glyph Settings → 订阅 uses.
539
654
  */
540
- function AccountMeta({ account, translate }) {
541
- const parts = [account.account, account.plan === undefined ? undefined : translate('usagePlan', { plan: account.plan })]
542
- .filter((part) => part !== undefined && part !== '');
543
- 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(' · ')] }));
544
663
  }
545
664
  /** Preview each account independently; all remaining quotas stay accessible. */
546
- export function AccountWindows({ windows, model, provider, translate, observedAt, stale }) {
665
+ export function AccountWindows({ windows, model, provider, translate, observedAt, stale, }) {
547
666
  const { shown, hidden } = previewWindows(windows, model, provider);
548
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))) }));
549
- 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)] }))] }));
550
669
  }
551
670
  /** One `dt`/`dd` pair: window name → `25% · 6d1h`, with the bar underneath. */
552
- function WindowRow({ label, window: w, t, observedAt, stale }) {
671
+ function WindowRow({ label, window: w, t, observedAt, stale, }) {
553
672
  const percent = displayUsedPercent(w.usedPercent);
554
- 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 })] }));
555
674
  }
556
675
  /**
557
676
  * Unplaced portal panel: hidden but laid out so the clamp measures real
@@ -564,12 +683,22 @@ const styles = {
564
683
  // Mirrors the host StatsPills pill so the badge reads as a sibling of the
565
684
  // shipped time/token pills.
566
685
  pill: {
567
- boxSizing: 'border-box', maxWidth: '100%',
686
+ boxSizing: 'border-box',
687
+ maxWidth: '100%',
568
688
  color: 'var(--dsw-alias-label-tertiary)',
569
- font: 'inherit', fontSize: 'calc(var(--dsh-content-font-size-secondary, 13px) - 1px)',
570
- fontVariantNumeric: 'tabular-nums', lineHeight: 'calc(20px + var(--dsh-content-font-delta-secondary, 0px))', whiteSpace: 'nowrap',
571
- background: 'transparent', border: 'none', borderRadius: 999,
572
- 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',
573
702
  },
574
703
  pillActive: {
575
704
  background: 'var(--dsw-alias-interactive-bg-hover)',
@@ -581,52 +710,111 @@ const styles = {
581
710
  // without the blur the transcript behind the dialog stays sharp and shows
582
711
  // through the fill.
583
712
  panel: {
584
- position: 'fixed', zIndex: 1100, boxSizing: 'border-box',
713
+ position: 'fixed',
714
+ zIndex: 1100,
715
+ boxSizing: 'border-box',
585
716
  background: 'var(--dsw-specific-menu)',
586
717
  backdropFilter: 'var(--dsw-menu-backdrop-filter)',
587
- width: 'max-content', minWidth: 'min(300px, 100vw - 24px)', maxWidth: 'min(440px, 100vw - 24px)',
588
- 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',
589
724
  boxShadow: 'var(--dsw-elevation-prominent)',
590
- color: 'var(--dsw-alias-label-secondary)', cursor: 'default',
591
- 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',
592
732
  },
593
733
  title: {
594
- color: 'var(--dsw-alias-label-primary)', display: 'flex',
595
- 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)',
596
755
  },
597
- titleLabel: { alignItems: 'center', gap: 6, minWidth: 0, display: 'inline-flex' },
598
- titleRule: { borderTop: '0.5px solid var(--dsw-alias-border-l2)', marginBottom: 10 },
599
- section: { marginTop: 12, paddingTop: 10, borderTop: '0.5px solid var(--dsw-alias-border-l2)' },
600
756
  providerRow: {
601
- 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,
602
769
  },
603
- providerName: { color: 'var(--dsw-alias-label-primary)', fontWeight: 500, display: 'inline-flex', alignItems: 'center', gap: 6 },
604
770
  currentTag: {
605
- fontSize: 10, lineHeight: '14px', fontWeight: 400, padding: '0 5px', borderRadius: 7,
606
- 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)',
607
778
  },
608
779
  providerMeta: {
609
- color: 'var(--dsw-alias-label-tertiary)', minWidth: 0, overflow: 'hidden',
610
- textOverflow: 'ellipsis', whiteSpace: 'nowrap',
780
+ color: 'var(--dsw-alias-label-tertiary)',
781
+ minWidth: 0,
782
+ overflow: 'hidden',
783
+ textOverflow: 'ellipsis',
784
+ whiteSpace: 'nowrap',
611
785
  },
612
786
  // Same star and color as the default-account marker in Settings → 订阅.
613
787
  defaultStar: { color: 'var(--dsw-alias-state-warn-label)' },
614
788
  accountBlock: { marginTop: 8 },
615
789
  accountRow: { display: 'flex', marginBottom: 4 },
616
790
  details: {
617
- color: 'var(--dsw-alias-label-tertiary)', display: 'grid',
618
- 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,
619
796
  },
620
797
  moreWindows: { marginTop: 8 },
621
- 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
+ },
622
803
  dt: { minWidth: 0, margin: 0, overflowWrap: 'anywhere' },
623
804
  dd: {
624
- minWidth: 0, margin: 0, color: 'var(--dsw-alias-label-secondary)',
625
- 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',
626
810
  },
627
811
  reset: { color: 'var(--dsw-alias-label-tertiary)' },
628
812
  bar: {
629
- gridColumn: '1 / -1', height: 4, borderRadius: 2, overflow: 'hidden',
630
- 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,
631
819
  },
632
820
  };