dsh-subscriptions 0.2.0 → 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 (99) hide show
  1. package/README.md +212 -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.js +74 -23
  6. package/lib/auth/store.js +80 -31
  7. package/lib/client/CursorCard.d.ts +1 -1
  8. package/lib/client/CursorCard.js +50 -16
  9. package/lib/client/ExternalUsageCards.d.ts +1 -1
  10. package/lib/client/ExternalUsageCards.js +37 -16
  11. package/lib/client/ImageGallery.d.ts +2 -2
  12. package/lib/client/ImageGallery.js +92 -33
  13. package/lib/client/ImageGenerateToolview.js +50 -14
  14. package/lib/client/OllamaCard.d.ts +1 -1
  15. package/lib/client/OllamaCard.js +26 -8
  16. package/lib/client/OllamaSettingsManager.js +113 -30
  17. package/lib/client/ProviderAccountManager.d.ts +1 -1
  18. package/lib/client/ProviderAccountManager.js +164 -49
  19. package/lib/client/ProviderModelEditor.js +107 -26
  20. package/lib/client/SpeedSelect.d.ts +62 -14
  21. package/lib/client/SpeedSelect.js +257 -46
  22. package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
  23. package/lib/client/SubscriptionUsageBadge.js +300 -112
  24. package/lib/client/SubscriptionsSection.js +258 -85
  25. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  26. package/lib/client/UsageMeter.d.ts +1 -1
  27. package/lib/client/UsageMeter.js +62 -24
  28. package/lib/client/VideoGenerateToolview.js +69 -17
  29. package/lib/client/account-preferences.d.ts +8 -0
  30. package/lib/client/account-preferences.js +20 -5
  31. package/lib/client/host-contract.d.ts +11 -2
  32. package/lib/client/host-contract.js +20 -3
  33. package/lib/client/index.d.ts +5 -5
  34. package/lib/client/index.js +34 -12
  35. package/lib/client/locales.d.ts +10 -0
  36. package/lib/client/locales.js +61 -51
  37. package/lib/client/ollama-draft.js +21 -12
  38. package/lib/client/reset-action.js +6 -4
  39. package/lib/client/reset-credits-view.d.ts +1 -1
  40. package/lib/client/reset-credits-view.js +66 -17
  41. package/lib/client/subscription-card-styles.js +94 -24
  42. package/lib/client/usage-color-preferences.js +4 -2
  43. package/lib/client/usage-pace.js +30 -12
  44. package/lib/client.js +287 -47
  45. package/lib/client.js.map +1 -1
  46. package/lib/http.js +1 -1
  47. package/lib/index.d.ts +3 -3
  48. package/lib/index.js +118 -64
  49. package/lib/model-defaults.js +4 -2
  50. package/lib/provider-settings.d.ts +12 -0
  51. package/lib/provider-settings.js +58 -16
  52. package/lib/providers/account-preferences.js +51 -22
  53. package/lib/providers/accounts.js +15 -9
  54. package/lib/providers/antigravity-oauth-client.js +2 -2
  55. package/lib/providers/antigravity.js +141 -68
  56. package/lib/providers/catalog-store.js +120 -41
  57. package/lib/providers/claude-cli-version.js +3 -3
  58. package/lib/providers/claude.d.ts +9 -2
  59. package/lib/providers/claude.js +153 -90
  60. package/lib/providers/codex-client-version.js +3 -3
  61. package/lib/providers/codex-search.js +40 -13
  62. package/lib/providers/codex.js +196 -112
  63. package/lib/providers/common.js +71 -39
  64. package/lib/providers/copilot.js +143 -82
  65. package/lib/providers/cursor-adapter.js +42 -18
  66. package/lib/providers/cursor-auth.d.ts +1 -1
  67. package/lib/providers/cursor-auth.js +69 -24
  68. package/lib/providers/cursor-model-settings.js +14 -5
  69. package/lib/providers/cursor-usage.js +27 -13
  70. package/lib/providers/external-usage-controller.js +14 -5
  71. package/lib/providers/external-usage.js +59 -23
  72. package/lib/providers/grok.js +152 -89
  73. package/lib/providers/image-pool.js +29 -11
  74. package/lib/providers/minimax-usage.js +31 -11
  75. package/lib/providers/npm-cli-version.js +10 -3
  76. package/lib/providers/ollama-settings.js +24 -8
  77. package/lib/providers/ollama-web.js +29 -11
  78. package/lib/providers/ollama.js +129 -66
  79. package/lib/providers/pool-family.js +11 -3
  80. package/lib/providers/pool-health.js +28 -8
  81. package/lib/providers/pool-usage.js +16 -9
  82. package/lib/providers/pool.d.ts +8 -0
  83. package/lib/providers/pool.js +81 -42
  84. package/lib/providers/rate-limit.js +8 -2
  85. package/lib/providers/replay.js +40 -17
  86. package/lib/providers/reset-redemption.js +18 -6
  87. package/lib/tools/image-generate.js +123 -65
  88. package/lib/tools/registration.js +5 -2
  89. package/lib/tools/video-generate.js +51 -31
  90. package/lib/tools/x-search.js +50 -22
  91. package/lib/translate/anthropic.js +85 -37
  92. package/lib/translate/antigravity-schema.js +71 -28
  93. package/lib/translate/antigravity-thinking.js +22 -9
  94. package/lib/translate/antigravity.js +141 -50
  95. package/lib/translate/chat-completions.js +67 -23
  96. package/lib/translate/resolved.js +55 -21
  97. package/lib/translate/responses.js +69 -29
  98. package/lib/translate/sse.js +4 -1
  99. package/package.json +26 -4
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import { setUsageBadgeMode, useUsageBadgeMode } from './usage-badge-preferences.js';
4
- import { isUsageColorPreset, setUsageColorPreset, useUsageColorPreset } from './usage-color-preferences.js';
3
+ import { setUsageBadgeMode, useUsageBadgeMode, } from './usage-badge-preferences.js';
4
+ import { isUsageColorPreset, setUsageColorPreset, useUsageColorPreset, } from './usage-color-preferences.js';
5
5
  /** Browser-local display preference; never changes provider or account settings. */
6
6
  export function UsageBadgeDisplaySetting({ t }) {
7
7
  const mode = useUsageBadgeMode();
8
8
  const preset = useUsageColorPreset();
9
9
  const [failed, setFailed] = useState(false);
10
- return (_jsxs("div", { style: styles.card, children: [_jsxs("label", { style: styles.field, children: [_jsx("span", { children: t('usageBadgeDisplay') }), _jsxs("select", { style: styles.select, "aria-label": t('usageBadgeDisplay'), value: mode, onChange: event => {
10
+ return (_jsxs("div", { style: styles.card, children: [_jsxs("label", { style: styles.field, children: [_jsx("span", { children: t('usageBadgeDisplay') }), _jsxs("select", { style: styles.select, "aria-label": t('usageBadgeDisplay'), value: mode, onChange: (event) => {
11
11
  const value = event.currentTarget.value;
12
12
  if (value !== 'recent' && value !== 'always' && value !== 'hidden')
13
13
  return;
@@ -16,11 +16,12 @@ export function UsageBadgeDisplaySetting({ t }) {
16
16
  setFailed(false);
17
17
  }
18
18
  catch (error) {
19
- if (!(error instanceof DOMException) || !['SecurityError', 'QuotaExceededError'].includes(error.name))
19
+ if (!(error instanceof DOMException) ||
20
+ !['SecurityError', 'QuotaExceededError'].includes(error.name))
20
21
  throw error;
21
22
  setFailed(true);
22
23
  }
23
- }, children: [_jsx("option", { value: "recent", children: t('usageBadgeDisplayRecent') }), _jsx("option", { value: "always", children: t('usageBadgeDisplayAlways') }), _jsx("option", { value: "hidden", children: t('usageBadgeDisplayHidden') })] })] }), _jsx("p", { style: styles.hint, children: t('usageBadgeDisplayHint') }), _jsxs("label", { style: styles.field, children: [_jsx("span", { children: t('usageColorLabel') }), _jsxs("select", { style: styles.select, "aria-label": t('usageColorLabel'), value: preset, onChange: event => {
24
+ }, children: [_jsx("option", { value: "recent", children: t('usageBadgeDisplayRecent') }), _jsx("option", { value: "always", children: t('usageBadgeDisplayAlways') }), _jsx("option", { value: "hidden", children: t('usageBadgeDisplayHidden') })] })] }), _jsx("p", { style: styles.hint, children: t('usageBadgeDisplayHint') }), _jsxs("label", { style: styles.field, children: [_jsx("span", { children: t('usageColorLabel') }), _jsxs("select", { style: styles.select, "aria-label": t('usageColorLabel'), value: preset, onChange: (event) => {
24
25
  const value = event.currentTarget.value;
25
26
  if (!isUsageColorPreset(value))
26
27
  return;
@@ -29,21 +30,43 @@ export function UsageBadgeDisplaySetting({ t }) {
29
30
  setFailed(false);
30
31
  }
31
32
  catch (error) {
32
- if (!(error instanceof DOMException) || !['SecurityError', 'QuotaExceededError'].includes(error.name))
33
+ if (!(error instanceof DOMException) ||
34
+ !['SecurityError', 'QuotaExceededError'].includes(error.name))
33
35
  throw error;
34
36
  setFailed(true);
35
37
  }
36
- }, children: [_jsx("option", { value: "standard", children: t('usageColorStandard') }), _jsx("option", { value: "relaxed", children: t('usageColorRelaxed') }), _jsx("option", { value: "remaining", children: t('usageColorRemaining') })] })] }), _jsx("p", { style: styles.hint, children: t(preset === 'remaining' ? 'usageColorRemainingHint' : 'usageColorPaceHint', {
38
+ }, children: [_jsx("option", { value: "standard", children: t('usageColorStandard') }), _jsx("option", { value: "relaxed", children: t('usageColorRelaxed') }), _jsx("option", { value: "remaining", children: t('usageColorRemaining') })] })] }), _jsx("p", { style: styles.hint, children: t(preset === 'remaining'
39
+ ? 'usageColorRemainingHint'
40
+ : 'usageColorPaceHint', {
37
41
  allowance: preset === 'relaxed' ? 15 : 10,
38
- }) }), failed && _jsx("p", { role: "alert", style: { ...styles.hint, color: 'var(--dsw-alias-state-error-primary)' }, children: t('usageBadgeDisplaySaveFailed') })] }));
42
+ }) }), failed && (_jsx("p", { role: "alert", style: {
43
+ ...styles.hint,
44
+ color: 'var(--dsw-alias-state-error-primary)',
45
+ }, children: t('usageBadgeDisplaySaveFailed') }))] }));
39
46
  }
40
47
  const styles = {
41
48
  card: {
42
49
  border: '0.5px solid var(--dsw-alias-settings-card-stroke, var(--dsw-alias-border-l4))',
43
- borderRadius: 'var(--dsw-radius-xl, 20px)', background: 'var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2))',
44
- padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 6,
50
+ borderRadius: 'var(--dsw-radius-xl, 20px)',
51
+ background: 'var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2))',
52
+ padding: '12px 14px',
53
+ display: 'flex',
54
+ flexDirection: 'column',
55
+ gap: 6,
56
+ },
57
+ field: {
58
+ display: 'flex',
59
+ flexDirection: 'column',
60
+ gap: 6,
61
+ fontSize: 13,
62
+ fontWeight: 500,
63
+ lineHeight: '20px',
45
64
  },
46
- field: { display: 'flex', flexDirection: 'column', gap: 6, fontSize: 13, fontWeight: 500, lineHeight: '20px' },
47
65
  select: { width: '100%' },
48
- hint: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
66
+ hint: {
67
+ margin: 0,
68
+ fontSize: 12,
69
+ lineHeight: '18px',
70
+ color: 'var(--dsw-alias-label-tertiary)',
71
+ },
49
72
  };
@@ -1,7 +1,7 @@
1
1
  import type { CSSProperties } from 'react';
2
2
  import type { UsageWindow, SubscriptionsSectionInjected } from './SubscriptionsSection.js';
3
3
  import type { UsageObservation } from './usage-pace.js';
4
- export declare function UsageMeter({ window: quota, t, style, observedAt, stale }: {
4
+ export declare function UsageMeter({ window: quota, t, style, observedAt, stale, }: {
5
5
  window: UsageWindow;
6
6
  t: SubscriptionsSectionInjected['t'];
7
7
  style?: CSSProperties;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { displayStartPercent, elapsedPercent, isUsageFresh, resetCountdownParts, usageBarColor, usageColorState, validUsage } from './usage-pace.js';
3
+ import { displayStartPercent, elapsedPercent, isUsageFresh, resetCountdownParts, usageBarColor, usageColorState, validUsage, } from './usage-pace.js';
4
4
  import { useUsageColorPreset } from './usage-color-preferences.js';
5
5
  /** Singular/plural locale keys per countdown unit (zh maps both to one string). */
6
6
  const UNIT_KEYS = {
@@ -8,7 +8,7 @@ const UNIT_KEYS = {
8
8
  hour: ['usageUnitHour', 'usageUnitHours'],
9
9
  minute: ['usageUnitMinute', 'usageUnitMinutes'],
10
10
  };
11
- export function UsageMeter({ window: quota, t, style, observedAt, stale }) {
11
+ export function UsageMeter({ window: quota, t, style, observedAt, stale, }) {
12
12
  const preset = useUsageColorPreset();
13
13
  const [now, setNow] = useState(Date.now);
14
14
  useEffect(() => {
@@ -31,30 +31,68 @@ export function UsageMeter({ window: quota, t, style, observedAt, stale }) {
31
31
  const state = usageColorState(quota.usedPercent, pace, preset, fresh);
32
32
  const lead = pace === undefined ? 0 : Math.round(used - pace);
33
33
  const label = [
34
- valid ? t('usageMeterUsed', { used: Math.round(used), remaining: Math.round(100 - used) }) : t('usageMeterInvalid'),
34
+ valid
35
+ ? t('usageMeterUsed', {
36
+ used: Math.round(used),
37
+ remaining: Math.round(100 - used),
38
+ })
39
+ : t('usageMeterInvalid'),
35
40
  // An invalid reading already states unavailability; the stale line would repeat it.
36
- !valid ? '' : !fresh ? t('usageMeterStale') : state === 'red' ? t('usageMeterNearLimit')
37
- : pace === undefined && preset !== 'remaining' ? t('usageMeterUnknownTime')
38
- : state === 'yellow' ? t('usageMeterAhead') : t('usageMeterNoWarning'),
41
+ !valid
42
+ ? ''
43
+ : !fresh
44
+ ? t('usageMeterStale')
45
+ : state === 'red'
46
+ ? t('usageMeterNearLimit')
47
+ : pace === undefined && preset !== 'remaining'
48
+ ? t('usageMeterUnknownTime')
49
+ : state === 'yellow'
50
+ ? t('usageMeterAhead')
51
+ : t('usageMeterNoWarning'),
39
52
  // The elapsed-time line is a timing fact: it shows whenever the window's
40
53
  // interval is known, even for a stale or invalid reading.
41
- marker === undefined ? '' : t('usageMeterPace', { elapsed: Math.round(marker) }),
42
- pace === undefined ? '' : lead > 0 ? t('usageMeterPaceAhead', { points: lead })
43
- : lead < 0 ? t('usageMeterPaceBehind', { points: -lead }) : t('usageMeterPaceEven'),
44
- quota.resetsAt !== undefined && quota.resetsAt > current ? t('usageMeterReset', {
45
- duration: resetCountdownParts(quota.resetsAt - current)
46
- .map(part => t(UNIT_KEYS[part.unit][part.count === 1 ? 0 : 1], { count: part.count }))
47
- .join(' '),
48
- }) : '',
49
- ].filter(Boolean).join(' · ');
50
- return _jsxs("div", { role: "img", "aria-label": label, title: label, "data-usage-color": state, style: {
51
- height: 6, borderRadius: 3, overflow: 'hidden',
52
- background: 'var(--dsw-alias-bg-layer-1)', border: '1px solid var(--dsw-alias-border-l2)',
53
- ...style, position: 'relative',
54
- }, children: [_jsx("div", { style: { height: '100%', borderRadius: 'inherit', width: `${used}%`,
55
- background: usageBarColor(quota.usedPercent, pace, preset, fresh) } }), marker !== undefined && _jsx("span", { "aria-hidden": "true", "data-usage-time-marker": true, style: {
56
- position: 'absolute', left: `clamp(0px, calc(${marker}% - 1px), calc(100% - 2px))`,
57
- top: 0, bottom: 0, width: 2, background: 'var(--dsw-alias-label-primary)',
54
+ marker === undefined
55
+ ? ''
56
+ : t('usageMeterPace', { elapsed: Math.round(marker) }),
57
+ pace === undefined
58
+ ? ''
59
+ : lead > 0
60
+ ? t('usageMeterPaceAhead', { points: lead })
61
+ : lead < 0
62
+ ? t('usageMeterPaceBehind', { points: -lead })
63
+ : t('usageMeterPaceEven'),
64
+ quota.resetsAt !== undefined && quota.resetsAt > current
65
+ ? t('usageMeterReset', {
66
+ duration: resetCountdownParts(quota.resetsAt - current)
67
+ .map((part) => t(UNIT_KEYS[part.unit][part.count === 1 ? 0 : 1], {
68
+ count: part.count,
69
+ }))
70
+ .join(' '),
71
+ })
72
+ : '',
73
+ ]
74
+ .filter(Boolean)
75
+ .join(' · ');
76
+ return (_jsxs("div", { role: "img", "aria-label": label, title: label, "data-usage-color": state, style: {
77
+ height: 6,
78
+ borderRadius: 3,
79
+ overflow: 'hidden',
80
+ background: 'var(--dsw-alias-bg-layer-1)',
81
+ border: '1px solid var(--dsw-alias-border-l2)',
82
+ ...style,
83
+ position: 'relative',
84
+ }, children: [_jsx("div", { style: {
85
+ height: '100%',
86
+ borderRadius: 'inherit',
87
+ width: `${used}%`,
88
+ background: usageBarColor(quota.usedPercent, pace, preset, fresh),
89
+ } }), marker !== undefined && (_jsx("span", { "aria-hidden": "true", "data-usage-time-marker": true, style: {
90
+ position: 'absolute',
91
+ left: `clamp(0px, calc(${marker}% - 1px), calc(100% - 2px))`,
92
+ top: 0,
93
+ bottom: 0,
94
+ width: 2,
95
+ background: 'var(--dsw-alias-label-primary)',
58
96
  boxShadow: '0 0 0 1px var(--dsw-alias-bg-layer-1)',
59
- } })] });
97
+ } }))] }));
60
98
  }
@@ -29,7 +29,7 @@ const PROMPT_MAX_LENGTH = 60;
29
29
  * @returns loader resolving a bare file name to the decoded bytes.
30
30
  */
31
31
  export function createVideoLoader(rpc) {
32
- return name => callSubscriptionsAuth(rpc, 'video', { name });
32
+ return (name) => callSubscriptionsAuth(rpc, 'video', { name });
33
33
  }
34
34
  /**
35
35
  * English-dictionary fallback for a missing locale seat (standalone renders);
@@ -67,7 +67,9 @@ function derivePrompt(argsRaw) {
67
67
  }
68
68
  }
69
69
  const line = (prompt ?? argsRaw).split('\n', 1)[0] ?? '';
70
- return line.length > PROMPT_MAX_LENGTH ? `${line.slice(0, PROMPT_MAX_LENGTH)}…` : line;
70
+ return line.length > PROMPT_MAX_LENGTH
71
+ ? `${line.slice(0, PROMPT_MAX_LENGTH)}…`
72
+ : line;
71
73
  }
72
74
  /** Flatten a settled result's text blocks (the fallback body and the error line). */
73
75
  function resultText(block) {
@@ -111,21 +113,51 @@ function base64Bytes(dataBase64) {
111
113
  return bytes;
112
114
  }
113
115
  const styles = {
114
- container: { display: 'flex', flexDirection: 'column', gap: 6, padding: '4px 0' },
116
+ container: {
117
+ display: 'flex',
118
+ flexDirection: 'column',
119
+ gap: 6,
120
+ padding: '4px 0',
121
+ },
115
122
  row: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 },
116
- icon: { display: 'inline-flex', flexShrink: 0, color: 'var(--dsw-alias-label-tertiary)' },
123
+ icon: {
124
+ display: 'inline-flex',
125
+ flexShrink: 0,
126
+ color: 'var(--dsw-alias-label-tertiary)',
127
+ },
117
128
  title: {
118
- fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)',
119
- overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
129
+ fontSize: 13,
130
+ lineHeight: '20px',
131
+ color: 'var(--dsw-alias-label-primary)',
132
+ overflow: 'hidden',
133
+ textOverflow: 'ellipsis',
134
+ whiteSpace: 'nowrap',
135
+ },
136
+ subtle: {
137
+ margin: 0,
138
+ fontSize: 12,
139
+ lineHeight: '18px',
140
+ color: 'var(--dsw-alias-label-tertiary)',
120
141
  },
121
- subtle: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
122
142
  output: {
123
- margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)',
124
- whiteSpace: 'pre-wrap', overflowWrap: 'anywhere',
143
+ margin: 0,
144
+ fontSize: 12,
145
+ lineHeight: '18px',
146
+ color: 'var(--dsw-alias-label-secondary)',
147
+ whiteSpace: 'pre-wrap',
148
+ overflowWrap: 'anywhere',
149
+ },
150
+ error: {
151
+ margin: 0,
152
+ fontSize: 12,
153
+ lineHeight: '18px',
154
+ color: 'var(--dsw-alias-state-error-primary)',
125
155
  },
126
- error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },
127
156
  video: {
128
- display: 'block', maxWidth: 480, width: '100%', borderRadius: 8,
157
+ display: 'block',
158
+ maxWidth: 480,
159
+ width: '100%',
160
+ borderRadius: 8,
129
161
  backgroundColor: 'var(--dsw-alias-bg-skeleton)',
130
162
  },
131
163
  };
@@ -139,7 +171,9 @@ export function VideoGenerateToolview(props) {
139
171
  const t = props.t ?? fallbackTranslate;
140
172
  const settled = block !== undefined && 'kind' in block;
141
173
  const isError = settled && block.isError;
142
- const fileName = block !== undefined && settled && !isError ? resolveFileName(block) : undefined;
174
+ const fileName = block !== undefined && settled && !isError
175
+ ? resolveFileName(block)
176
+ : undefined;
143
177
  const [load, setLoad] = useState({ phase: 'loading' });
144
178
  useEffect(() => {
145
179
  if (fileName === undefined || loadVideo === undefined)
@@ -151,12 +185,17 @@ export function VideoGenerateToolview(props) {
151
185
  if (cancelled)
152
186
  return;
153
187
  // Uint8Array#slice detaches a plain ArrayBuffer view for BlobPart.
154
- objectUrl = URL.createObjectURL(new Blob([base64Bytes(video.dataBase64).slice()], { type: video.mediaType }));
188
+ objectUrl = URL.createObjectURL(new Blob([base64Bytes(video.dataBase64).slice()], {
189
+ type: video.mediaType,
190
+ }));
155
191
  setLoad({ phase: 'ready', url: objectUrl });
156
192
  }, (error) => {
157
193
  if (cancelled)
158
194
  return;
159
- setLoad({ phase: 'failed', message: error instanceof Error ? error.message : String(error) });
195
+ setLoad({
196
+ phase: 'failed',
197
+ message: error instanceof Error ? error.message : String(error),
198
+ });
160
199
  });
161
200
  return () => {
162
201
  cancelled = true;
@@ -166,10 +205,23 @@ export function VideoGenerateToolview(props) {
166
205
  }, [fileName, loadVideo]);
167
206
  if (block === undefined)
168
207
  return null;
169
- const argsRaw = (settled ? block.call?.argsRaw : block.phase === 'start' ? block.argsRaw : undefined) ?? '';
208
+ const argsRaw = (settled
209
+ ? block.call?.argsRaw
210
+ : block.phase === 'start'
211
+ ? block.argsRaw
212
+ : undefined) ?? '';
170
213
  const title = `video_generate: ${derivePrompt(argsRaw)}`;
171
214
  const text = settled ? resultText(block) : '';
172
- return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.row, children: [_jsx("span", { style: styles.icon, children: _jsx(SparkleIcon, { size: 14 }) }), _jsx("span", { style: styles.title, children: title })] }), !settled && _jsx("p", { style: styles.subtle, children: t('generatingVideo') }), settled && isError && text !== '' && (_jsx("p", { style: styles.error, children: text.split('\n', 1)[0] })), settled && !isError && fileName !== undefined && load.phase === 'loading' && (_jsx("p", { style: styles.subtle, children: t('videoLoading') })), settled && !isError && fileName !== undefined && load.phase === 'failed' && (_jsx("p", { style: styles.error, children: t('videoLoadFailed', { message: load.message }) })), settled && !isError && fileName !== undefined && load.phase === 'ready' && (
173
- // eslint-disable-next-line jsx-a11y/media-has-caption -- generated clip; no caption track exists.
215
+ return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.row, children: [_jsx("span", { style: styles.icon, children: _jsx(SparkleIcon, { size: 14 }) }), _jsx("span", { style: styles.title, children: title })] }), !settled && _jsx("p", { style: styles.subtle, children: t('generatingVideo') }), settled && isError && text !== '' && (_jsx("p", { style: styles.error, children: text.split('\n', 1)[0] })), settled &&
216
+ !isError &&
217
+ fileName !== undefined &&
218
+ load.phase === 'loading' && (_jsx("p", { style: styles.subtle, children: t('videoLoading') })), settled &&
219
+ !isError &&
220
+ fileName !== undefined &&
221
+ load.phase === 'failed' && (_jsx("p", { style: styles.error, children: t('videoLoadFailed', { message: load.message }) })), settled &&
222
+ !isError &&
223
+ fileName !== undefined &&
224
+ load.phase === 'ready' && (
225
+ // Generated clip; no caption track exists.
174
226
  _jsx("video", { style: styles.video, src: load.url, controls: true, preload: "metadata" })), settled && !isError && fileName === undefined && text !== '' && (_jsx("p", { style: styles.output, children: text }))] }));
175
227
  }
@@ -1,4 +1,5 @@
1
1
  import type { AccountPreferences, ProviderPreferences } from '../provider-settings.js';
2
+ import type { PromptCacheTtl } from '../translate/anthropic.js';
2
3
  export interface AccountCatalogRow {
3
4
  key: string;
4
5
  label: string;
@@ -20,5 +21,12 @@ export declare function accountModelRows(account: AccountCatalogRow, preferences
20
21
  }[];
21
22
  /** The model editor owns everything except the separately managed accounts. */
22
23
  export declare function mergeLatestAccounts(draft: ProviderPreferences, latest: ProviderPreferences): ProviderPreferences;
24
+ /**
25
+ * The account manager owns the Claude prompt-cache TTL picker. The model
26
+ * editor's draft carries the TTL it loaded, so the dialog always replaces it
27
+ * with the picker's choice when touched, or the freshly re-read value
28
+ * otherwise.
29
+ */
30
+ export declare function mergePromptCacheTtl(settings: ProviderPreferences, promptCacheTtl: PromptCacheTtl | undefined): ProviderPreferences;
23
31
  /** Only touched accounts replace their latest preferences; other settings survive. */
24
32
  export declare function mergeAccountChanges(latest: ProviderPreferences, changes: Record<string, AccountPreferences>): ProviderPreferences;
@@ -1,19 +1,34 @@
1
1
  /** Missing allowlist follows discovery; explicit [] must remain empty. */
2
2
  export function accountPoolSelection(account, models) {
3
- return new Set(account.poolModels ?? models.map(model => model.id));
3
+ return new Set(account.poolModels ?? models.map((model) => model.id));
4
4
  }
5
5
  /** Retain unavailable saved IDs so a temporary catalog failure cannot erase them. */
6
6
  export function accountModelRows(account, preferences) {
7
- const known = new Set(account.models.map(model => model.id));
7
+ const known = new Set(account.models.map((model) => model.id));
8
8
  return [
9
- ...account.models.map(model => ({ ...model, unavailable: false })),
10
- ...(preferences.poolModels ?? []).filter(id => !known.has(id)).map(id => ({ id, name: id, unavailable: true })),
9
+ ...account.models.map((model) => ({ ...model, unavailable: false })),
10
+ ...(preferences.poolModels ?? [])
11
+ .filter((id) => !known.has(id))
12
+ .map((id) => ({ id, name: id, unavailable: true })),
11
13
  ];
12
14
  }
13
15
  /** The model editor owns everything except the separately managed accounts. */
14
16
  export function mergeLatestAccounts(draft, latest) {
15
17
  const { accounts: _stale, ...settings } = draft;
16
- return { ...settings, ...(latest.accounts === undefined ? {} : { accounts: latest.accounts }) };
18
+ return {
19
+ ...settings,
20
+ ...(latest.accounts === undefined ? {} : { accounts: latest.accounts }),
21
+ };
22
+ }
23
+ /**
24
+ * The account manager owns the Claude prompt-cache TTL picker. The model
25
+ * editor's draft carries the TTL it loaded, so the dialog always replaces it
26
+ * with the picker's choice when touched, or the freshly re-read value
27
+ * otherwise.
28
+ */
29
+ export function mergePromptCacheTtl(settings, promptCacheTtl) {
30
+ const { promptCacheTtl: _stale, ...rest } = settings;
31
+ return promptCacheTtl === undefined ? rest : { ...rest, promptCacheTtl };
17
32
  }
18
33
  /** Only touched accounts replace their latest preferences; other settings survive. */
19
34
  export function mergeAccountChanges(latest, changes) {
@@ -62,9 +62,16 @@ export declare const HOST_CONTRACT: {
62
62
  };
63
63
  /** DOM attributes the client looks up in host-rendered markup. */
64
64
  readonly markers: {
65
- /** The shipped StatsPills row; the usage badge renders into it as a third pill. */
65
+ /**
66
+ * Stats pill anchors, by role. `row` is the shared stats row through DSH
67
+ * 0.2.0; the badge portals into it. `dock` is the per-pill anchor from
68
+ * 0.2.1; the badge stays in its dock entry and stops watching once that
69
+ * anchor is in scope. A host renders one of them. The package check
70
+ * accepts either spelling; callers read `row` and `dock` by name.
71
+ */
66
72
  readonly composerStats: {
67
- readonly attribute: "data-composer-stats";
73
+ readonly row: "data-composer-stats";
74
+ readonly dock: "data-composer-stat";
68
75
  readonly package: "@deepseek-ai/dsh-client-ui-chat";
69
76
  };
70
77
  };
@@ -88,3 +95,5 @@ export declare const HOST_CONTRACT: {
88
95
  };
89
96
  /** A glyph name listed in {@link HOST_CONTRACT}; hostIcon() accepts no other. */
90
97
  export type HostIconName = (typeof HOST_CONTRACT.icons.names)[number];
98
+ /** Attribute selector for a host DOM marker (`data-composer-stats` → `[data-composer-stats]`). */
99
+ export declare function markerSelector(attribute: string): string;
@@ -30,7 +30,10 @@ export const HOST_CONTRACT = {
30
30
  moduleTable: '@deepseek-ai/dsh-web-frontend',
31
31
  /** Named runtime exports the client reads from a host module. */
32
32
  exports: {
33
- '@deepseek-ai/dsh-client-ui-primitives': ['useAnchoredPosition', 'useDismissOnOutsidePointer'],
33
+ '@deepseek-ai/dsh-client-ui-primitives': [
34
+ 'useAnchoredPosition',
35
+ 'useDismissOnOutsidePointer',
36
+ ],
34
37
  },
35
38
  /**
36
39
  * 16px glyphs read through hostIcon(). The host must export
@@ -62,8 +65,18 @@ export const HOST_CONTRACT = {
62
65
  },
63
66
  /** DOM attributes the client looks up in host-rendered markup. */
64
67
  markers: {
65
- /** The shipped StatsPills row; the usage badge renders into it as a third pill. */
66
- composerStats: { attribute: 'data-composer-stats', package: '@deepseek-ai/dsh-client-ui-chat' },
68
+ /**
69
+ * Stats pill anchors, by role. `row` is the shared stats row through DSH
70
+ * 0.2.0; the badge portals into it. `dock` is the per-pill anchor from
71
+ * 0.2.1; the badge stays in its dock entry and stops watching once that
72
+ * anchor is in scope. A host renders one of them. The package check
73
+ * accepts either spelling; callers read `row` and `dock` by name.
74
+ */
75
+ composerStats: {
76
+ row: 'data-composer-stats',
77
+ dock: 'data-composer-stat',
78
+ package: '@deepseek-ai/dsh-client-ui-chat',
79
+ },
67
80
  },
68
81
  /**
69
82
  * What the slot renderer emits when an entry throws. It catches the error,
@@ -122,3 +135,7 @@ export const HOST_CONTRACT = {
122
135
  ],
123
136
  },
124
137
  };
138
+ /** Attribute selector for a host DOM marker (`data-composer-stats` → `[data-composer-stats]`). */
139
+ export function markerSelector(attribute) {
140
+ return `[${attribute}]`;
141
+ }
@@ -1,10 +1,10 @@
1
1
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  import type { SubscriptionsKey } from './locales.js';
3
- export type { SubscriptionsSectionInjected, SubscriptionsSectionProps } from './SubscriptionsSection.js';
4
- export type { ImageGenerateToolviewInjected, ImageGenerateToolviewProps } from './ImageGenerateToolview.js';
5
- export type { VideoGenerateToolviewInjected, VideoGenerateToolviewProps } from './VideoGenerateToolview.js';
6
- export type { SpeedSelectInjected, SpeedSelectProps, SpeedState, SpeedTier } from './SpeedSelect.js';
7
- export type { SubscriptionUsageBadgeInjected, SubscriptionUsageBadgeProps } from './SubscriptionUsageBadge.js';
3
+ export type { SubscriptionsSectionInjected, SubscriptionsSectionProps, } from './SubscriptionsSection.js';
4
+ export type { ImageGenerateToolviewInjected, ImageGenerateToolviewProps, } from './ImageGenerateToolview.js';
5
+ export type { VideoGenerateToolviewInjected, VideoGenerateToolviewProps, } from './VideoGenerateToolview.js';
6
+ export type { SpeedSelectInjected, SpeedSelectProps, SpeedState, SpeedTier, } from './SpeedSelect.js';
7
+ export type { SubscriptionUsageBadgeInjected, SubscriptionUsageBadgeProps, } from './SubscriptionUsageBadge.js';
8
8
  export type { SubscriptionsKey } from './locales.js';
9
9
  declare module '@deepseek-ai/dsh-client-ui-slots' {
10
10
  interface LocaleNamespaceMap {
@@ -2,10 +2,10 @@
2
2
  // allowImportingTsExtensions/rewriteRelativeImportExtensions pair; under
3
3
  // nodenext the .js specifier resolves to the .tsx source (see README note).
4
4
  import { SubscriptionsSection } from './SubscriptionsSection.js';
5
- import { ImageGenerateToolview, createImageLoader } from './ImageGenerateToolview.js';
6
- import { VideoGenerateToolview, createVideoLoader } from './VideoGenerateToolview.js';
7
- import { SpeedSelect, createSpeedLoader, createSpeedSetter } from './SpeedSelect.js';
8
- import { SubscriptionUsageBadge, createCurrentModelReader } from './SubscriptionUsageBadge.js';
5
+ import { ImageGenerateToolview, createImageLoader, } from './ImageGenerateToolview.js';
6
+ import { VideoGenerateToolview, createVideoLoader, } from './VideoGenerateToolview.js';
7
+ import { SpeedSelect, createModelSelectionWatch, createSpeedLoader, createSpeedSetter, notifySpeedChanged, } from './SpeedSelect.js';
8
+ import { SubscriptionUsageBadge, createCurrentModelReader, } from './SubscriptionUsageBadge.js';
9
9
  import { en, zh } from './locales.js';
10
10
  import { fastCommandDescription } from './fast-command.js';
11
11
  /** Dictionary namespace owned by this plugin. */
@@ -31,7 +31,8 @@ export function apply(ctx) {
31
31
  ctx.effect(() => {
32
32
  const style = document.createElement('style');
33
33
  style.setAttribute('data-plugin', 'dsh-subscriptions');
34
- style.textContent = 'div[role="dialog"][aria-modal="true"]:has(> nav) { padding-top: 14px; }';
34
+ style.textContent =
35
+ 'div[role="dialog"][aria-modal="true"]:has(> nav) { padding-top: 14px; }';
35
36
  document.head.appendChild(style);
36
37
  return () => style.remove();
37
38
  }, 'dsh-subscriptions: settings panel breathing room');
@@ -39,7 +40,10 @@ export function apply(ctx) {
39
40
  // browser shell the same key holds the full client ConnectionHandle.
40
41
  const connection = ctx.get('connection');
41
42
  const t = ctx.locale.bind(NS);
42
- const injected = () => ({ rpc: connection.rpc, t });
43
+ const injected = () => ({
44
+ rpc: connection.rpc,
45
+ t,
46
+ });
43
47
  ctx.slots.inject('settings.section', () => ctx.slots.register({
44
48
  name: 'settings.section',
45
49
  id: 'subscriptions',
@@ -51,7 +55,9 @@ export function apply(ctx) {
51
55
  // The image_generate keyed toolview owns how image calls render inline; its
52
56
  // gallery bytes ride the same channel through the injected loader. The
53
57
  // framework synthesizes the toolview's own `t` seat from `locale: NS`.
54
- const toolviewInjected = () => ({ load: createImageLoader(connection.rpc) });
58
+ const toolviewInjected = () => ({
59
+ load: createImageLoader(connection.rpc),
60
+ });
55
61
  ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({
56
62
  name: 'tool.call.toolview',
57
63
  key: 'image_generate',
@@ -60,7 +66,9 @@ export function apply(ctx) {
60
66
  }, ImageGenerateToolview));
61
67
  // The video_generate keyed toolview plays the saved MP4 inline; its bytes
62
68
  // ride the same channel's `video` endpoint through the injected loader.
63
- const videoToolviewInjected = () => ({ loadVideo: createVideoLoader(connection.rpc) });
69
+ const videoToolviewInjected = () => ({
70
+ loadVideo: createVideoLoader(connection.rpc),
71
+ });
64
72
  ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({
65
73
  name: 'tool.call.toolview',
66
74
  key: 'video_generate',
@@ -81,6 +89,7 @@ export function apply(ctx) {
81
89
  inject: (sessionId) => ({
82
90
  loadSpeed: createSpeedLoader(connection, models, sessionId),
83
91
  setSpeed: createSpeedSetter(connection, sessionId),
92
+ watchModel: createModelSelectionWatch(models, sessionId),
84
93
  }),
85
94
  }, SpeedSelect));
86
95
  // The subscription usage badge renders a stats pill in the composer's dock
@@ -123,12 +132,25 @@ export function apply(ctx) {
123
132
  if (!state.visible)
124
133
  throw new Error(t('commandFastUnavailable'));
125
134
  return [
126
- { id: 'standard', label: t('speedStandard'), detail: t('speedStandardDescription') },
127
- { id: 'fast', label: t('speedFast'), detail: t('speedFastDescription') },
128
- ].map(option => ({ ...option, active: option.id === state.tier }));
135
+ {
136
+ id: 'standard',
137
+ label: t('speedStandard'),
138
+ detail: t('speedStandardDescription'),
139
+ },
140
+ {
141
+ id: 'fast',
142
+ label: t('speedFast'),
143
+ detail: t('speedFastDescription'),
144
+ },
145
+ ].map((option) => ({
146
+ ...option,
147
+ active: option.id === state.tier,
148
+ }));
129
149
  },
130
150
  onSelect: async (option, session) => {
131
- await createSpeedSetter(connection, session.sessionId)(option.id);
151
+ const wrote = await createSpeedSetter(connection, session.sessionId)(option.id);
152
+ if (wrote)
153
+ notifySpeedChanged();
132
154
  },
133
155
  },
134
156
  }), 'dsh-subscriptions: /fast contribution');
@@ -37,6 +37,11 @@ export declare const en: {
37
37
  modelsWebSearch: string;
38
38
  modelsSave: string;
39
39
  modelsUnavailable: string;
40
+ claudeCacheTtl: string;
41
+ claudeCacheTtlFollowConfig: string;
42
+ claudeCacheTtl5m: string;
43
+ claudeCacheTtl1h: string;
44
+ claudeCacheTtlHint: string;
40
45
  nav: string;
41
46
  intro: string;
42
47
  unavailable: string;
@@ -272,6 +277,11 @@ export declare const zh: {
272
277
  modelsWebSearch: string;
273
278
  modelsSave: string;
274
279
  modelsUnavailable: string;
280
+ claudeCacheTtl: string;
281
+ claudeCacheTtlFollowConfig: string;
282
+ claudeCacheTtl5m: string;
283
+ claudeCacheTtl1h: string;
284
+ claudeCacheTtlHint: string;
275
285
  nav: string;
276
286
  intro: string;
277
287
  unavailable: string;