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
@@ -6,7 +6,12 @@ import { subscriptionCardStyles as styles } from './subscription-card-styles.js'
6
6
  import { providerSettingsCss } from './provider-settings-styles.js';
7
7
  const border = '0.5px solid var(--dsw-alias-border-l2)';
8
8
  const stack = { display: 'grid', gap: 12, minWidth: 0 };
9
- const actions = { display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' };
9
+ const actions = {
10
+ display: 'flex',
11
+ flexWrap: 'wrap',
12
+ gap: 8,
13
+ alignItems: 'center',
14
+ };
10
15
  function message(error) {
11
16
  return error instanceof Error ? error.message : String(error);
12
17
  }
@@ -17,7 +22,9 @@ function capabilities(t, model) {
17
22
  model.vision === true ? t('ollamaCapVision') : false,
18
23
  model.thinking === true ? t('ollamaCapThinking') : false,
19
24
  context === undefined ? false : t('ollamaContext', { tokens: context }),
20
- ].filter(Boolean).join(' · ');
25
+ ]
26
+ .filter(Boolean)
27
+ .join(' · ');
21
28
  }
22
29
  /**
23
30
  * Ollama Cloud settings dialog. It owns one draft (model list, web toggle,
@@ -70,7 +77,8 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
70
77
  let current = true;
71
78
  setLoading(true);
72
79
  setError('');
73
- void callSubscriptionsAuth(rpc, 'ollamaSettings', {}).then(data => {
80
+ void callSubscriptionsAuth(rpc, 'ollamaSettings', {})
81
+ .then((data) => {
74
82
  if (!current)
75
83
  return;
76
84
  setView(data);
@@ -80,12 +88,20 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
80
88
  setStash(new Map());
81
89
  setContextText({});
82
90
  setDirty(false);
83
- }).catch(failure => {
91
+ })
92
+ .catch((failure) => {
84
93
  if (current)
85
94
  setError(t('ollamaModelsLoadFailed', { message: message(failure) }));
86
- }).finally(() => { if (current)
87
- setLoading(false); });
88
- return () => { current = false; };
95
+ })
96
+ .finally(() => {
97
+ if (current)
98
+ setLoading(false);
99
+ });
100
+ return () => {
101
+ current = false;
102
+ };
103
+ // `t` only formats the load error. A new translator must not refetch.
104
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- refetch on rpc and attempt only
89
105
  }, [rpc, attempt]);
90
106
  function edit(next) {
91
107
  setDraft(next);
@@ -99,7 +115,10 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
99
115
  setFetching(true);
100
116
  setFetchError('');
101
117
  try {
102
- const value = await callSubscriptionsAuth(rpc, 'ollamaModels', { ...(apiKey ? { apiKey } : {}), ...(draft.baseURL ? { baseURL: draft.baseURL } : {}) });
118
+ const value = await callSubscriptionsAuth(rpc, 'ollamaModels', {
119
+ ...(apiKey ? { apiKey } : {}),
120
+ ...(draft.baseURL ? { baseURL: draft.baseURL } : {}),
121
+ });
103
122
  if (!alive.current)
104
123
  return;
105
124
  setCandidates(value.models);
@@ -150,7 +169,7 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
150
169
  try {
151
170
  await callSubscriptionsAuth(rpc, 'ollamaSetSettings', { settings: {} });
152
171
  if (alive.current) {
153
- setAttempt(value => value + 1);
172
+ setAttempt((value) => value + 1);
154
173
  onSaved();
155
174
  }
156
175
  }
@@ -178,15 +197,17 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
178
197
  * (stashed in this dialog, or fetched and not yet adopted). A kept model
179
198
  * the fetch did not return stays listed, ticked or not.
180
199
  */
181
- const fetchedById = new Map((candidates ?? []).map(candidate => [candidate.id, candidate]));
182
- const present = new Set((draft.models ?? []).map(model => model.id));
200
+ const fetchedById = new Map((candidates ?? []).map((candidate) => [candidate.id, candidate]));
201
+ const present = new Set((draft.models ?? []).map((model) => model.id));
183
202
  const asRow = (model, ticked) => {
184
203
  const fetched = fetchedById.get(model.id);
185
204
  // The latest fetch outranks the stored snapshot, matching the runtime:
186
205
  // discovery is the ceiling and the snapshot only stands in without it.
187
206
  const contextWindow = fetched?.contextWindow ?? model.contextWindow;
188
207
  return {
189
- id: model.id, name: model.name ?? fetched?.name ?? model.id, ticked,
208
+ id: model.id,
209
+ name: model.name ?? fetched?.name ?? model.id,
210
+ ticked,
190
211
  vision: model.vision === true || fetched?.vision === true,
191
212
  thinking: model.thinking === true || fetched?.thinking === true,
192
213
  ...(contextWindow === undefined ? {} : { contextWindow }),
@@ -194,13 +215,15 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
194
215
  };
195
216
  };
196
217
  const rows = [
197
- ...(draft.models ?? []).map(model => asRow(model, true)),
198
- ...[...stash.values()].filter(model => !present.has(model.id)).map(model => asRow(model, false)),
218
+ ...(draft.models ?? []).map((model) => asRow(model, true)),
219
+ ...[...stash.values()]
220
+ .filter((model) => !present.has(model.id))
221
+ .map((model) => asRow(model, false)),
199
222
  ...(candidates ?? [])
200
- .filter(candidate => !present.has(candidate.id) && !stash.has(candidate.id))
201
- .map(candidate => asRow({ id: candidate.id }, false)),
223
+ .filter((candidate) => !present.has(candidate.id) && !stash.has(candidate.id))
224
+ .map((candidate) => asRow({ id: candidate.id }, false)),
202
225
  ];
203
- const shown = rows.filter(row => `${row.name} ${row.id}`.toLowerCase().includes(query.trim().toLowerCase()));
226
+ const shown = rows.filter((row) => `${row.name} ${row.id}`.toLowerCase().includes(query.trim().toLowerCase()));
204
227
  /** Rename and default-effort edits ride the tested draft helpers. */
205
228
  function rename(id, name) {
206
229
  edit({ ...draft, models: renameModel(draft.models ?? [], id, name) });
@@ -210,37 +233,97 @@ export function OllamaSettingsManager({ rpc, t, onClose, onSaved }) {
210
233
  }
211
234
  /** Keep the typed text; only a valid number (or blank) reaches the draft. */
212
235
  function setContext(id, text) {
213
- setContextText(current => ({ ...current, [id]: text }));
236
+ setContextText((current) => ({ ...current, [id]: text }));
214
237
  const tokens = parseContextInput(text);
215
238
  if (tokens !== null)
216
- edit({ ...draft, models: setModelContext(draft.models ?? [], id, tokens) });
239
+ edit({
240
+ ...draft,
241
+ models: setModelContext(draft.models ?? [], id, tokens),
242
+ });
217
243
  }
218
244
  function applyTicks(next) {
219
245
  setStash(next.stash);
220
246
  edit({ ...draft, models: next.models });
221
247
  }
222
248
  const invalidContext = Object.entries(contextText)
223
- .filter(([id, text]) => present.has(id) && parseContextInput(text) === null).map(([id]) => id);
224
- return _jsxs("dialog", { className: "dsh-subscription-manager", ref: dialog, "aria-labelledby": title, "aria-describedby": description, onCancel: event => { if (saveLock.current)
225
- event.preventDefault(); }, onClose: onClose, style: { width: 620, maxWidth: 'calc(100vw - 32px)', maxHeight: 'calc(100dvh - 32px)', overflow: 'auto' }, children: [_jsx("style", { children: providerSettingsCss }), _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0 }, children: t('ollamaManageTitle') }), _jsx("button", { type: "button", autoFocus: true, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, className: "dsh-subscription-hint", children: t('ollamaManageHint') }), view?.configOverridesModels === true && _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaCatalogOverride') }), error && _jsx("p", { role: "alert", children: error }), loading && _jsx("p", { role: "status", className: "dsh-subscription-hint", children: t('modelDefaultsLoading') }), !loading && !view && _jsx("button", { type: "button", onClick: () => setAttempt(value => value + 1), children: t('modelDefaultsRetry') }), view && _jsxs("fieldset", { disabled: saving, style: { ...stack, border: 0, margin: 0, padding: 0, minWidth: 0 }, children: [_jsxs("div", { style: { borderTop: border, paddingTop: 12, display: 'grid', gap: 12 }, children: [_jsx("h3", { style: { margin: 0 }, children: t('modelsEdit') }), _jsxs("div", { style: actions, children: [_jsx("input", { type: "password", autoComplete: "off", style: { flex: '1 1 180px' }, "aria-label": t('ollamaKeyPlaceholder'), placeholder: t('ollamaKeyPlaceholder'), value: apiKey, onChange: event => setApiKey(event.target.value) }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: fetching, onClick: () => { void fetchCandidates(); }, children: fetching ? t('ollamaFetching') : t('ollamaFetch') }), candidates !== undefined && _jsx("span", { className: "dsh-subscription-hint", children: t('ollamaCandidatesCount', { count: candidates.length }) })] }), _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaKeyHint') }), fetchError && _jsx("p", { role: "alert", children: fetchError }), candidates?.length === 0 && _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaCandidatesEmpty') }), _jsxs("div", { style: actions, children: [_jsx("input", { style: { flex: '1 1 180px' }, value: query, onChange: event => setQuery(event.target.value), placeholder: t('modelDefaultsFilterPlaceholder'), "aria-label": t('modelDefaultsFilterPlaceholder') }), _jsx("button", { type: "button", onClick: () => applyTicks(tickModels(draft.models ?? [], stash, candidates ?? [], shown.map(row => row.id))), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => applyTicks(untickModels(draft.models ?? [], stash, shown.map(row => row.id))), children: t('modelsSelectNone') })] }), _jsxs("div", { style: { maxHeight: 360, overflowY: 'auto', display: 'grid', gap: 8 }, children: [shown.map(row => {
226
- const stored = (draft.models ?? []).find(entry => entry.id === row.id);
227
- const index = (draft.models ?? []).findIndex(entry => entry.id === row.id);
249
+ .filter(([id, text]) => present.has(id) && parseContextInput(text) === null)
250
+ .map(([id]) => id);
251
+ return (_jsxs("dialog", { className: "dsh-subscription-manager", ref: dialog, "aria-labelledby": title, "aria-describedby": description, onCancel: (event) => {
252
+ if (saveLock.current)
253
+ event.preventDefault();
254
+ }, onClose: onClose, style: {
255
+ width: 620,
256
+ maxWidth: 'calc(100vw - 32px)',
257
+ maxHeight: 'calc(100dvh - 32px)',
258
+ overflow: 'auto',
259
+ }, children: [_jsx("style", { children: providerSettingsCss }), _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0 }, children: t('ollamaManageTitle') }), _jsx("button", { type: "button", autoFocus: true, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, className: "dsh-subscription-hint", children: t('ollamaManageHint') }), view?.configOverridesModels === true && (_jsx("p", { className: "dsh-subscription-hint", children: t('ollamaCatalogOverride') })), error && _jsx("p", { role: "alert", children: error }), loading && (_jsx("p", { role: "status", className: "dsh-subscription-hint", children: t('modelDefaultsLoading') })), !loading && !view && (_jsx("button", { type: "button", onClick: () => setAttempt((value) => value + 1), children: t('modelDefaultsRetry') })), view && (_jsxs("fieldset", { disabled: saving, style: { ...stack, border: 0, margin: 0, padding: 0, minWidth: 0 }, children: [_jsxs("div", { style: {
260
+ borderTop: border,
261
+ paddingTop: 12,
262
+ display: 'grid',
263
+ gap: 12,
264
+ }, children: [_jsx("h3", { style: { margin: 0 }, children: t('modelsEdit') }), _jsxs("div", { style: actions, children: [_jsx("input", { type: "password", autoComplete: "off", style: { flex: '1 1 180px' }, "aria-label": t('ollamaKeyPlaceholder'), placeholder: t('ollamaKeyPlaceholder'), value: apiKey, onChange: (event) => setApiKey(event.target.value) }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: fetching, onClick: () => {
265
+ void fetchCandidates();
266
+ }, children: fetching ? t('ollamaFetching') : t('ollamaFetch') }), candidates !== undefined && (_jsx("span", { className: "dsh-subscription-hint", children: t('ollamaCandidatesCount', { count: candidates.length }) }))] }), _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaKeyHint') }), fetchError && _jsx("p", { role: "alert", children: fetchError }), candidates?.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t('ollamaCandidatesEmpty') })), _jsxs("div", { style: actions, children: [_jsx("input", { style: { flex: '1 1 180px' }, value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('modelDefaultsFilterPlaceholder'), "aria-label": t('modelDefaultsFilterPlaceholder') }), _jsx("button", { type: "button", onClick: () => applyTicks(tickModels(draft.models ?? [], stash, candidates ?? [], shown.map((row) => row.id))), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => applyTicks(untickModels(draft.models ?? [], stash, shown.map((row) => row.id))), children: t('modelsSelectNone') })] }), _jsxs("div", { style: {
267
+ maxHeight: 360,
268
+ overflowY: 'auto',
269
+ display: 'grid',
270
+ gap: 8,
271
+ }, children: [shown.map((row) => {
272
+ const stored = (draft.models ?? []).find((entry) => entry.id === row.id);
273
+ const index = (draft.models ?? []).findIndex((entry) => entry.id === row.id);
228
274
  const open = expanded.has(row.id);
229
275
  const details = `${row.id}-details`;
230
- const efforts = stored === undefined ? [] : effortOptions(stored, fetchedById.get(row.id)?.efforts, view.models.find(entry => entry.id === row.id)?.efforts);
276
+ const efforts = stored === undefined
277
+ ? []
278
+ : effortOptions(stored, fetchedById.get(row.id)?.efforts, view.models.find((entry) => entry.id === row.id)
279
+ ?.efforts);
231
280
  const contextProblem = invalidContext.includes(row.id);
232
- return _jsxs("div", { style: { borderBottom: border, padding: '8px 2px', display: 'grid', gap: 8 }, children: [_jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: row.ticked, onChange: event => {
281
+ return (_jsxs("div", { style: {
282
+ borderBottom: border,
283
+ padding: '8px 2px',
284
+ display: 'grid',
285
+ gap: 8,
286
+ }, children: [_jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: row.ticked, onChange: (event) => {
233
287
  applyTicks(event.target.checked
234
288
  ? tickModel(draft.models ?? [], stash, candidates ?? [], row.id)
235
289
  : untickModel(draft.models ?? [], stash, row.id));
236
- } }), " ", row.name, " ", _jsx("span", { style: styles.status, children: capabilities(t, row) })] }), stored !== undefined && _jsxs("div", { style: actions, children: [row.thinking && efforts.length > 0 && _jsxs("label", { style: actions, children: [t('modelDefaultsTitle'), _jsxs("select", { "aria-label": `${row.name} ${t('modelDefaultsTitle')}`, value: stored.defaultEffort ?? '', onChange: event => setEffort(row.id, event.target.value), children: [_jsx("option", { value: "", children: t('modelDefaultsFollowProvider') }), efforts.map(effort => _jsx("option", { value: effort.id, disabled: effort.unavailable === true, children: effort.unavailable === true ? `${effort.name} (${t('modelsUnavailable')})` : effort.name }, effort.id))] })] }), row.thinking && efforts.length === 0 && _jsx("small", { children: t('ollamaEffortsUnknown') }), _jsxs("label", { style: actions, children: [t('modelsContext'), _jsx("input", { style: { width: 140 }, inputMode: "numeric", "aria-invalid": contextProblem, value: contextText[row.id] ?? (stored.contextOverride === undefined ? '' : String(stored.contextOverride)), "aria-label": `${row.name} ${t('modelsContext')}`, placeholder: row.contextWindow === undefined ? '' : String(row.contextWindow), onChange: event => setContext(row.id, event.target.value) })] }), row.contextWindow !== undefined && _jsx("small", { children: t('modelsContextBounds', { default: row.contextWindow, max: row.contextWindow }) }), _jsxs("button", { type: "button", className: "dsh-subscription-quiet", "aria-expanded": open, "aria-controls": details, "aria-label": `${row.name} ${t('ollamaRowDetails')}`, onClick: () => {
290
+ } }), ' ', row.name, ' ', _jsx("span", { style: styles.status, children: capabilities(t, row) })] }), stored !== undefined && (_jsxs("div", { style: actions, children: [row.thinking && efforts.length > 0 && (_jsxs("label", { style: actions, children: [t('modelDefaultsTitle'), _jsxs("select", { "aria-label": `${row.name} ${t('modelDefaultsTitle')}`, value: stored.defaultEffort ?? '', onChange: (event) => setEffort(row.id, event.target.value), children: [_jsx("option", { value: "", children: t('modelDefaultsFollowProvider') }), efforts.map((effort) => (_jsx("option", { value: effort.id, disabled: effort.unavailable === true, children: effort.unavailable === true
291
+ ? `${effort.name} (${t('modelsUnavailable')})`
292
+ : effort.name }, effort.id)))] })] })), row.thinking && efforts.length === 0 && (_jsx("small", { children: t('ollamaEffortsUnknown') })), _jsxs("label", { style: actions, children: [t('modelsContext'), _jsx("input", { style: { width: 140 }, inputMode: "numeric", "aria-invalid": contextProblem, value: contextText[row.id] ??
293
+ (stored.contextOverride === undefined
294
+ ? ''
295
+ : String(stored.contextOverride)), "aria-label": `${row.name} ${t('modelsContext')}`, placeholder: row.contextWindow === undefined
296
+ ? ''
297
+ : String(row.contextWindow), onChange: (event) => setContext(row.id, event.target.value) })] }), row.contextWindow !== undefined && (_jsx("small", { children: t('modelsContextBounds', {
298
+ default: row.contextWindow,
299
+ max: row.contextWindow,
300
+ }) })), _jsxs("button", { type: "button", className: "dsh-subscription-quiet", "aria-expanded": open, "aria-controls": details, "aria-label": `${row.name} ${t('ollamaRowDetails')}`, onClick: () => {
237
301
  const next = new Set(expanded);
238
302
  if (open)
239
303
  next.delete(row.id);
240
304
  else
241
305
  next.add(row.id);
242
306
  setExpanded(next);
243
- }, children: [t('ollamaRowDetails'), " ", open ? '\u25BE' : '\u25B8'] })] }), contextProblem && _jsx("p", { role: "alert", children: t('modelsContextInvalid', { model: row.name }) }), stored !== undefined && open && _jsxs("div", { id: details, style: actions, children: [_jsxs("label", { style: actions, children: [t('ollamaDisplayName'), _jsx("input", { value: stored.name ?? '', placeholder: row.id, "aria-label": `${row.name} ${t('ollamaDisplayName')}`, onChange: event => rename(row.id, event.target.value), onBlur: event => rename(row.id, event.target.value.trim()) })] }), _jsx("button", { type: "button", disabled: index === 0, "aria-label": `${row.name} ${t('ollamaMoveUp')}`, onClick: () => edit({ ...draft, models: moveModel(draft.models ?? [], row.id, -1) }), children: t('ollamaMoveUp') }), _jsx("button", { type: "button", disabled: index === (draft.models ?? []).length - 1, "aria-label": `${row.name} ${t('ollamaMoveDown')}`, onClick: () => edit({ ...draft, models: moveModel(draft.models ?? [], row.id, 1) }), children: t('ollamaMoveDown') })] })] }, row.id);
244
- }), shown.length === 0 && _jsx("span", { className: "dsh-subscription-hint", children: query.trim() ? t('modelDefaultsFilterEmpty', { query }) : t('ollamaModelsEmpty') })] }), _jsx("small", { children: t('modelsContextHint') })] }), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('ollamaBaseURL') }), _jsx("input", { value: draft.baseURL ?? '', placeholder: view.effective.baseURL, onChange: event => editOverride('baseURL', event.target.value) })] }), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('ollamaApiKeyRef') }), _jsx("input", { value: draft.apiKeyRef ?? '', placeholder: view.effective.apiKeyRef, onChange: event => editOverride('apiKeyRef', event.target.value) })] }), _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaConnectionHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: (draft.web ?? view.effective.web) === true, onChange: event => edit({ ...draft, web: event.target.checked }) }), " ", t('modelsWebSearch')] }), _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaWebHint') })] }), _jsxs("footer", { style: { ...stack, gap: 8, borderTop: border, padding: '14px 0 8px',
245
- position: 'sticky', bottom: 0, background: 'var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1))' }, children: [saveError && _jsx("p", { role: "alert", children: saveError }), _jsxs("div", { style: { ...actions, justifyContent: 'flex-end' }, children: [_jsx("button", { type: "button", style: { marginRight: 'auto' }, disabled: loading || saving, onClick: () => { void resetDefaults(); }, children: t('ollamaResetDefaults') }), _jsx("button", { type: "button", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx("button", { type: "button", className: "dsh-subscription-primary", disabled: loading || saving || !dirty || invalidContext.length > 0, onClick: () => { void save(); }, children: saving ? t('modelDefaultsSaving') : t('modelsSave') })] })] })] })] });
307
+ }, children: [t('ollamaRowDetails'), " ", open ? '\u25BE' : '\u25B8'] })] })), contextProblem && (_jsx("p", { role: "alert", children: t('modelsContextInvalid', { model: row.name }) })), stored !== undefined && open && (_jsxs("div", { id: details, style: actions, children: [_jsxs("label", { style: actions, children: [t('ollamaDisplayName'), _jsx("input", { value: stored.name ?? '', placeholder: row.id, "aria-label": `${row.name} ${t('ollamaDisplayName')}`, onChange: (event) => rename(row.id, event.target.value), onBlur: (event) => rename(row.id, event.target.value.trim()) })] }), _jsx("button", { type: "button", disabled: index === 0, "aria-label": `${row.name} ${t('ollamaMoveUp')}`, onClick: () => edit({
308
+ ...draft,
309
+ models: moveModel(draft.models ?? [], row.id, -1),
310
+ }), children: t('ollamaMoveUp') }), _jsx("button", { type: "button", disabled: index === (draft.models ?? []).length - 1, "aria-label": `${row.name} ${t('ollamaMoveDown')}`, onClick: () => edit({
311
+ ...draft,
312
+ models: moveModel(draft.models ?? [], row.id, 1),
313
+ }), children: t('ollamaMoveDown') })] }))] }, row.id));
314
+ }), shown.length === 0 && (_jsx("span", { className: "dsh-subscription-hint", children: query.trim()
315
+ ? t('modelDefaultsFilterEmpty', { query })
316
+ : t('ollamaModelsEmpty') }))] }), _jsx("small", { children: t('modelsContextHint') })] }), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('ollamaBaseURL') }), _jsx("input", { value: draft.baseURL ?? '', placeholder: view.effective.baseURL, onChange: (event) => editOverride('baseURL', event.target.value) })] }), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('ollamaApiKeyRef') }), _jsx("input", { value: draft.apiKeyRef ?? '', placeholder: view.effective.apiKeyRef, onChange: (event) => editOverride('apiKeyRef', event.target.value) })] }), _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaConnectionHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: (draft.web ?? view.effective.web) === true, onChange: (event) => edit({ ...draft, web: event.target.checked }) }), ' ', t('modelsWebSearch')] }), _jsx("p", { className: "dsh-subscription-hint", children: t('ollamaWebHint') })] })), _jsxs("footer", { style: {
317
+ ...stack,
318
+ gap: 8,
319
+ borderTop: border,
320
+ padding: '14px 0 8px',
321
+ position: 'sticky',
322
+ bottom: 0,
323
+ background: 'var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1))',
324
+ }, children: [saveError && _jsx("p", { role: "alert", children: saveError }), _jsxs("div", { style: { ...actions, justifyContent: 'flex-end' }, children: [_jsx("button", { type: "button", style: { marginRight: 'auto' }, disabled: loading || saving, onClick: () => {
325
+ void resetDefaults();
326
+ }, children: t('ollamaResetDefaults') }), _jsx("button", { type: "button", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx("button", { type: "button", className: "dsh-subscription-primary", disabled: loading || saving || !dirty || invalidContext.length > 0, onClick: () => {
327
+ void save();
328
+ }, children: saving ? t('modelDefaultsSaving') : t('modelsSave') })] })] })] })] }));
246
329
  }
@@ -9,5 +9,5 @@ interface Props {
9
9
  onClose: () => void;
10
10
  }
11
11
  /** Native top-layer dialog provides keyboard containment, Escape and focus restoration. */
12
- export declare function ProviderAccountManager({ provider, name, rpc, t, onClose }: Props): import("react").JSX.Element;
12
+ export declare function ProviderAccountManager({ provider, name, rpc, t, onClose, }: Props): import("react").JSX.Element;
13
13
  export {};
@@ -1,14 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useId, useRef, useState } from 'react';
3
3
  import { callSubscriptionsAuth } from './subscriptions-rpc.js';
4
- import { accountModelRows, accountPoolSelection, mergeAccountChanges, mergeLatestAccounts } from './account-preferences.js';
4
+ import { accountModelRows, accountPoolSelection, mergeAccountChanges, mergeLatestAccounts, mergePromptCacheTtl, } from './account-preferences.js';
5
5
  import { ProviderModelEditor } from './ProviderModelEditor.js';
6
6
  import { providerSettingsCss } from './provider-settings-styles.js';
7
7
  const border = '0.5px solid var(--dsw-alias-border-l2)';
8
8
  const stack = { display: 'grid', gap: 12, minWidth: 0 };
9
- const actions = { display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' };
9
+ const actions = {
10
+ display: 'flex',
11
+ flexWrap: 'wrap',
12
+ gap: 8,
13
+ alignItems: 'center',
14
+ };
10
15
  /** Native top-layer dialog provides keyboard containment, Escape and focus restoration. */
11
- export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
16
+ export function ProviderAccountManager({ provider, name, rpc, t, onClose, }) {
12
17
  const dialog = useRef(null);
13
18
  const title = useId();
14
19
  const description = useId();
@@ -20,6 +25,9 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
20
25
  const [saveError, setSaveError] = useState('');
21
26
  const [attempt, setAttempt] = useState(0);
22
27
  const [modelsDirty, setModelsDirty] = useState(false);
28
+ // The Claude prompt-cache TTL draft: undefined follows the plugin config.
29
+ const [ttlDraft, setTtlDraft] = useState();
30
+ const [ttlDirty, setTtlDirty] = useState(false);
23
31
  const alive = useRef(true);
24
32
  const saveLock = useRef(false);
25
33
  const editor = useRef(null);
@@ -39,18 +47,34 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
39
47
  let current = true;
40
48
  setLoading(true);
41
49
  setError('');
42
- void callSubscriptionsAuth(rpc, provider === 'cursor-subscription' ? 'cursorSettings' : 'providerSettings', { provider }).then(data => {
43
- if (current)
50
+ void callSubscriptionsAuth(rpc, provider === 'cursor-subscription'
51
+ ? 'cursorSettings'
52
+ : 'providerSettings', { provider })
53
+ .then((data) => {
54
+ if (current) {
44
55
  setCatalog(data);
45
- }).catch(error => {
56
+ setTtlDraft(data.settings.promptCacheTtl);
57
+ setTtlDirty(false);
58
+ }
59
+ })
60
+ .catch((error) => {
61
+ if (current)
62
+ setError(t('accountsLoadFailed', {
63
+ message: error instanceof Error ? error.message : String(error),
64
+ }));
65
+ })
66
+ .finally(() => {
46
67
  if (current)
47
- setError(t('accountsLoadFailed', { message: error instanceof Error ? error.message : String(error) }));
48
- }).finally(() => { if (current)
49
- setLoading(false); });
50
- return () => { current = false; };
68
+ setLoading(false);
69
+ });
70
+ return () => {
71
+ current = false;
72
+ };
73
+ // `t` only formats the load error. A new translator must not refetch.
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- refetch on provider, rpc, and attempt only
51
75
  }, [provider, rpc, attempt]);
52
76
  function edit(key, next) {
53
- setChanges(current => ({ ...current, [key]: next }));
77
+ setChanges((current) => ({ ...current, [key]: next }));
54
78
  }
55
79
  /**
56
80
  * One submit for both drafts: the model editor's (visibility, effort,
@@ -72,7 +96,11 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
72
96
  try {
73
97
  if (provider !== 'cursor-subscription') {
74
98
  for (const { model, effort } of models.efforts) {
75
- await callSubscriptionsAuth(rpc, 'setModelDefault', { provider, model, ...(effort ? { effort } : {}) });
99
+ await callSubscriptionsAuth(rpc, 'setModelDefault', {
100
+ provider,
101
+ model,
102
+ ...(effort ? { effort } : {}),
103
+ });
76
104
  savedEfforts++;
77
105
  if (!alive.current)
78
106
  return;
@@ -80,15 +108,28 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
80
108
  }
81
109
  // Re-read before writing: another client may have saved since this
82
110
  // dialog loaded, and a stale snapshot must never be written back.
83
- const latest = await callSubscriptionsAuth(rpc, provider === 'cursor-subscription' ? 'cursorSettings' : 'providerSettings', { provider });
111
+ const latest = await callSubscriptionsAuth(rpc, provider === 'cursor-subscription'
112
+ ? 'cursorSettings'
113
+ : 'providerSettings', { provider });
84
114
  if (!alive.current)
85
115
  return;
86
116
  if (provider === 'cursor-subscription') {
87
- await callSubscriptionsAuth(rpc, 'cursorSetSettings', { settings: models.settings ?? latest.settings });
117
+ await callSubscriptionsAuth(rpc, 'cursorSetSettings', {
118
+ settings: models.settings ?? latest.settings,
119
+ });
88
120
  }
89
121
  else {
90
- const base = models.settings === undefined ? latest.settings : mergeLatestAccounts(models.settings, latest.settings);
91
- await callSubscriptionsAuth(rpc, 'setProviderSettings', { provider, settings: mergeAccountChanges(base, changes) });
122
+ const base = models.settings === undefined
123
+ ? latest.settings
124
+ : mergeLatestAccounts(models.settings, latest.settings);
125
+ // The editor's draft carries the TTL it loaded, so the dialog always
126
+ // replaces it: the picker's choice when touched, otherwise the value
127
+ // from the freshly re-read settings.
128
+ const withTtl = mergePromptCacheTtl(base, ttlDirty ? ttlDraft : latest.settings.promptCacheTtl);
129
+ await callSubscriptionsAuth(rpc, 'setProviderSettings', {
130
+ provider,
131
+ settings: mergeAccountChanges(withTtl, changes),
132
+ });
92
133
  }
93
134
  if (alive.current)
94
135
  onClose();
@@ -96,7 +137,8 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
96
137
  catch (error) {
97
138
  if (alive.current) {
98
139
  const message = error instanceof Error ? error.message : String(error);
99
- setSaveError((savedEfforts ? t('modelsPartialSave') + ' ' : '') + t('accountsSaveFailed', { message }));
140
+ setSaveError((savedEfforts ? t('modelsPartialSave') + ' ' : '') +
141
+ t('accountsSaveFailed', { message }));
100
142
  }
101
143
  }
102
144
  finally {
@@ -105,36 +147,109 @@ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
105
147
  setSaving(false);
106
148
  }
107
149
  }
108
- return _jsxs("dialog", { className: "dsh-subscription-manager", ref: dialog, "aria-labelledby": title, "aria-describedby": description, onCancel: event => { if (saveLock.current)
109
- event.preventDefault(); }, onClose: onClose, style: { width: 620, maxWidth: 'calc(100vw - 32px)', maxHeight: 'calc(100dvh - 32px)', overflow: 'auto' }, children: [_jsx("style", { children: providerSettingsCss }), _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0 }, children: t('accountsTitle', { provider: name }) }), _jsx("button", { type: "button", autoFocus: true, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription' ? 'cursorAccountsHint' : 'accountsHint') }), error && _jsx("p", { role: "alert", children: error }), loading && _jsx("p", { role: "status", className: "dsh-subscription-hint", children: t('accountsLoading') }), !loading && !catalog && _jsx("button", { type: "button", onClick: () => setAttempt(value => value + 1), children: t('modelDefaultsRetry') }), catalog && _jsxs("fieldset", { disabled: saving || loading, style: { ...stack, border: 0, margin: 0, padding: 0 }, children: [catalog.accounts.length === 0 && _jsx("p", { className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription' ? 'cursorManageSignIn' : 'accountsEmpty') }), provider === 'cursor-subscription' && catalog.accounts.length > 0 &&
110
- _jsxs("fieldset", { style: { border, borderRadius: 'var(--dsw-radius-lg, 12px)', padding: 14 }, children: [_jsx("legend", { style: { padding: '0 6px' }, children: name }), _jsx("p", { className: "dsh-subscription-hint", children: t('cursorConnected') })] }), provider !== 'cursor-subscription' && catalog.accounts.map(account => {
111
- const preferences = changes[account.key] ?? catalog.settings.accounts?.[account.key] ?? {};
112
- const selected = accountPoolSelection(preferences, account.models);
113
- const models = accountModelRows(account, preferences);
114
- return _jsxs("fieldset", { style: { ...stack, border, borderRadius: 'var(--dsw-radius-lg, 12px)', padding: 14, margin: 0 }, children: [_jsx("legend", { style: { padding: '0 6px', overflowWrap: 'anywhere', maxWidth: '100%' }, children: account.label }), account.unavailable && _jsx("p", { className: "dsh-subscription-hint", children: t('accountsCatalogUnavailable') }), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('accountsAlias') }), _jsx("input", { value: preferences.alias ?? '', placeholder: account.label, onChange: event => {
115
- const next = { ...preferences };
116
- if (event.target.value.trim())
117
- next.alias = event.target.value;
118
- else
119
- delete next.alias;
120
- edit(account.key, next);
121
- } })] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolEnabled !== false, onChange: event => edit(account.key, { ...preferences, poolEnabled: event.target.checked }) }), " ", t('accountsPoolEnabled')] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.independentEntry === true, onChange: event => edit(account.key, { ...preferences, independentEntry: event.target.checked }) }), " ", t('accountsIndependent')] }), _jsx("p", { className: "dsh-subscription-hint", children: t('accountsIndependentHint') }), _jsxs("details", { children: [_jsxs("summary", { style: { cursor: 'pointer', fontSize: 13 }, children: [t('accountsModels'), " \u00B7 ", preferences.poolModels === undefined
122
- ? t('accountsModelsAll') : t('accountsModelsCount', { count: preferences.poolModels.length })] }), _jsxs("div", { style: { ...stack, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t('accountsModelsHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolModels === undefined, onChange: event => {
123
- const next = { ...preferences };
124
- if (event.target.checked)
125
- delete next.poolModels;
126
- else
127
- next.poolModels = account.models.map(model => model.id);
128
- edit(account.key, next);
129
- } }), " ", t('accountsModelsAutomatic')] }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", onClick: () => edit(account.key, { ...preferences, poolModels: models.map(model => model.id) }), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => edit(account.key, { ...preferences, poolModels: [] }), children: t('modelsSelectNone') })] }), _jsxs("div", { style: { ...stack, gap: 8, maxHeight: 240, overflowY: 'auto' }, children: [models.map(model => _jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: selected.has(model.id), onChange: event => {
130
- const next = new Set(selected);
131
- if (event.target.checked)
132
- next.add(model.id);
133
- else
134
- next.delete(model.id);
135
- edit(account.key, { ...preferences, poolModels: [...next] });
136
- } }), " ", model.name, model.unavailable && ` (${t('modelsUnavailable')})`] }, model.id)), models.length === 0 && _jsx("p", { className: "dsh-subscription-hint", children: t('accountsNoModels') })] })] })] })] }, account.key);
137
- })] }), (provider !== 'cursor-subscription' || (catalog?.accounts.length ?? 0) > 0) &&
138
- _jsx(ProviderModelEditor, { ref: editor, provider: provider, rpc: rpc, t: t, disabled: saving, onDirtyChange: setModelsDirty }), _jsxs("footer", { style: { ...stack, gap: 8, borderTop: border, padding: '14px 0 8px',
139
- position: 'sticky', bottom: 0, background: 'var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1))' }, children: [saveError && _jsx("p", { role: "alert", children: saveError }), _jsxs("div", { style: { ...actions, justifyContent: 'flex-end' }, children: [_jsx("button", { type: "button", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx("button", { type: "button", className: "dsh-subscription-primary", disabled: loading || saving || (!Object.keys(changes).length && !modelsDirty), onClick: () => { void save(); }, children: saving ? t('modelDefaultsSaving') : t('modelsSave') })] })] })] })] });
150
+ return (_jsxs("dialog", { className: "dsh-subscription-manager", ref: dialog, "aria-labelledby": title, "aria-describedby": description, onCancel: (event) => {
151
+ if (saveLock.current)
152
+ event.preventDefault();
153
+ }, onClose: onClose, style: {
154
+ width: 620,
155
+ maxWidth: 'calc(100vw - 32px)',
156
+ maxHeight: 'calc(100dvh - 32px)',
157
+ overflow: 'auto',
158
+ }, children: [_jsx("style", { children: providerSettingsCss }), _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0 }, children: t('accountsTitle', { provider: name }) }), _jsx("button", { type: "button", autoFocus: true, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription'
159
+ ? 'cursorAccountsHint'
160
+ : 'accountsHint') }), error && _jsx("p", { role: "alert", children: error }), loading && (_jsx("p", { role: "status", className: "dsh-subscription-hint", children: t('accountsLoading') })), !loading && !catalog && (_jsx("button", { type: "button", onClick: () => setAttempt((value) => value + 1), children: t('modelDefaultsRetry') })), catalog && (_jsxs("fieldset", { disabled: saving || loading, style: { ...stack, border: 0, margin: 0, padding: 0 }, children: [catalog.accounts.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t(provider === 'cursor-subscription'
161
+ ? 'cursorManageSignIn'
162
+ : 'accountsEmpty') })), provider === 'cursor-subscription' &&
163
+ catalog.accounts.length > 0 && (_jsxs("fieldset", { style: {
164
+ border,
165
+ borderRadius: 'var(--dsw-radius-lg, 12px)',
166
+ padding: 14,
167
+ }, children: [_jsx("legend", { style: { padding: '0 6px' }, children: name }), _jsx("p", { className: "dsh-subscription-hint", children: t('cursorConnected') })] })), provider !== 'cursor-subscription' &&
168
+ catalog.accounts.map((account) => {
169
+ const preferences = changes[account.key] ??
170
+ catalog.settings.accounts?.[account.key] ??
171
+ {};
172
+ const selected = accountPoolSelection(preferences, account.models);
173
+ const models = accountModelRows(account, preferences);
174
+ return (_jsxs("fieldset", { style: {
175
+ ...stack,
176
+ border,
177
+ borderRadius: 'var(--dsw-radius-lg, 12px)',
178
+ padding: 14,
179
+ margin: 0,
180
+ }, children: [_jsx("legend", { style: {
181
+ padding: '0 6px',
182
+ overflowWrap: 'anywhere',
183
+ maxWidth: '100%',
184
+ }, children: account.label }), account.unavailable && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsCatalogUnavailable') })), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('accountsAlias') }), _jsx("input", { value: preferences.alias ?? '', placeholder: account.label, onChange: (event) => {
185
+ const next = { ...preferences };
186
+ if (event.target.value.trim())
187
+ next.alias = event.target.value;
188
+ else
189
+ delete next.alias;
190
+ edit(account.key, next);
191
+ } })] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolEnabled !== false, onChange: (event) => edit(account.key, {
192
+ ...preferences,
193
+ poolEnabled: event.target.checked,
194
+ }) }), ' ', t('accountsPoolEnabled')] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.independentEntry === true, onChange: (event) => edit(account.key, {
195
+ ...preferences,
196
+ independentEntry: event.target.checked,
197
+ }) }), ' ', t('accountsIndependent')] }), _jsx("p", { className: "dsh-subscription-hint", children: t('accountsIndependentHint') }), _jsxs("details", { children: [_jsxs("summary", { style: { cursor: 'pointer', fontSize: 13 }, children: [t('accountsModels'), " \u00B7", ' ', preferences.poolModels === undefined
198
+ ? t('accountsModelsAll')
199
+ : t('accountsModelsCount', {
200
+ count: preferences.poolModels.length,
201
+ })] }), _jsxs("div", { style: { ...stack, marginTop: 12 }, children: [_jsx("p", { className: "dsh-subscription-hint", children: t('accountsModelsHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolModels === undefined, onChange: (event) => {
202
+ const next = { ...preferences };
203
+ if (event.target.checked)
204
+ delete next.poolModels;
205
+ else
206
+ next.poolModels = account.models.map((model) => model.id);
207
+ edit(account.key, next);
208
+ } }), ' ', t('accountsModelsAutomatic')] }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", onClick: () => edit(account.key, {
209
+ ...preferences,
210
+ poolModels: models.map((model) => model.id),
211
+ }), children: t('modelsSelectAll') }), _jsx("button", { type: "button", onClick: () => edit(account.key, {
212
+ ...preferences,
213
+ poolModels: [],
214
+ }), children: t('modelsSelectNone') })] }), _jsxs("div", { style: {
215
+ ...stack,
216
+ gap: 8,
217
+ maxHeight: 240,
218
+ overflowY: 'auto',
219
+ }, children: [models.map((model) => (_jsxs("label", { style: { overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: selected.has(model.id), onChange: (event) => {
220
+ const next = new Set(selected);
221
+ if (event.target.checked)
222
+ next.add(model.id);
223
+ else
224
+ next.delete(model.id);
225
+ edit(account.key, {
226
+ ...preferences,
227
+ poolModels: [...next],
228
+ });
229
+ } }), ' ', model.name, model.unavailable &&
230
+ ` (${t('modelsUnavailable')})`] }, model.id))), models.length === 0 && (_jsx("p", { className: "dsh-subscription-hint", children: t('accountsNoModels') }))] })] })] })] }, account.key));
231
+ })] })), (provider !== 'cursor-subscription' ||
232
+ (catalog?.accounts.length ?? 0) > 0) && (_jsx(ProviderModelEditor, { ref: editor, provider: provider, rpc: rpc, t: t, disabled: saving, onDirtyChange: setModelsDirty })), provider === 'claude' && catalog && (_jsxs("div", { style: {
233
+ ...stack,
234
+ gap: 6,
235
+ borderTop: border,
236
+ marginTop: 12,
237
+ paddingTop: 12,
238
+ }, children: [_jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { className: "dsh-subscription-label", children: t('claudeCacheTtl') }), _jsxs("select", { "aria-label": t('claudeCacheTtl'), style: { width: 220 }, value: ttlDraft ?? '', disabled: saving, onChange: (event) => {
239
+ const value = event.target.value;
240
+ setTtlDraft(value === '' ? undefined : value);
241
+ setTtlDirty(true);
242
+ }, children: [_jsx("option", { value: "", children: t('claudeCacheTtlFollowConfig') }), _jsx("option", { value: "5m", children: t('claudeCacheTtl5m') }), _jsx("option", { value: "1h", children: t('claudeCacheTtl1h') })] })] }), _jsx("p", { className: "dsh-subscription-hint", children: t('claudeCacheTtlHint') })] })), _jsxs("footer", { style: {
243
+ ...stack,
244
+ gap: 8,
245
+ borderTop: border,
246
+ padding: '14px 0 8px',
247
+ position: 'sticky',
248
+ bottom: 0,
249
+ background: 'var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1))',
250
+ }, children: [saveError && _jsx("p", { role: "alert", children: saveError }), _jsxs("div", { style: { ...actions, justifyContent: 'flex-end' }, children: [_jsx("button", { type: "button", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx("button", { type: "button", className: "dsh-subscription-primary", disabled: loading ||
251
+ saving ||
252
+ (!Object.keys(changes).length && !modelsDirty && !ttlDirty), onClick: () => {
253
+ void save();
254
+ }, children: saving ? t('modelDefaultsSaving') : t('modelsSave') })] })] })] })] }));
140
255
  }