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.
- package/README.md +212 -180
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.js +74 -23
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +1 -1
- package/lib/client/OllamaCard.js +26 -8
- package/lib/client/OllamaSettingsManager.js +113 -30
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +107 -26
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +257 -46
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.js +258 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +62 -24
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +10 -0
- package/lib/client/locales.js +61 -51
- package/lib/client/ollama-draft.js +21 -12
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.js +30 -12
- package/lib/client.js +287 -47
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +118 -64
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +12 -0
- package/lib/provider-settings.js +58 -16
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +9 -2
- package/lib/providers/claude.js +153 -90
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.js +71 -39
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.js +14 -5
- package/lib/providers/external-usage.js +59 -23
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.js +24 -8
- package/lib/providers/ollama-web.js +29 -11
- package/lib/providers/ollama.js +129 -66
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +141 -50
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- 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 = {
|
|
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
|
-
]
|
|
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', {})
|
|
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
|
-
})
|
|
91
|
+
})
|
|
92
|
+
.catch((failure) => {
|
|
84
93
|
if (current)
|
|
85
94
|
setError(t('ollamaModelsLoadFailed', { message: message(failure) }));
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
|
|
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', {
|
|
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,
|
|
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()]
|
|
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({
|
|
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)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
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: {
|
|
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
|
-
} }),
|
|
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({
|
|
244
|
-
|
|
245
|
-
|
|
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 = {
|
|
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'
|
|
43
|
-
|
|
50
|
+
void callSubscriptionsAuth(rpc, provider === 'cursor-subscription'
|
|
51
|
+
? 'cursorSettings'
|
|
52
|
+
: 'providerSettings', { provider })
|
|
53
|
+
.then((data) => {
|
|
54
|
+
if (current) {
|
|
44
55
|
setCatalog(data);
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
|
|
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', {
|
|
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'
|
|
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', {
|
|
117
|
+
await callSubscriptionsAuth(rpc, 'cursorSetSettings', {
|
|
118
|
+
settings: models.settings ?? latest.settings,
|
|
119
|
+
});
|
|
88
120
|
}
|
|
89
121
|
else {
|
|
90
|
-
const base = models.settings === undefined
|
|
91
|
-
|
|
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') + ' ' : '') +
|
|
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 => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
}
|