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
@@ -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
  }