dsh-plugin-subscriptions 0.5.3 → 0.6.0
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 +45 -6
- package/README.zh.md +43 -4
- package/lib/auth/rpc.d.ts +36 -2
- package/lib/auth/rpc.js +47 -5
- package/lib/client/ImageGenerateToolview.d.ts +1 -1
- package/lib/client/SpeedSelect.d.ts +25 -2
- package/lib/client/SpeedSelect.js +10 -6
- package/lib/client/SubscriptionsSection.d.ts +74 -0
- package/lib/client/SubscriptionsSection.js +325 -4
- package/lib/client/VideoGenerateToolview.d.ts +1 -1
- package/lib/client/index.d.ts +1 -9
- package/lib/client/index.js +7 -4
- package/lib/client/locales.d.ts +28 -0
- package/lib/client/locales.js +28 -0
- package/lib/client.js +458 -10
- package/lib/client.js.map +1 -1
- package/lib/compat.d.ts +36 -0
- package/lib/compat.js +20 -0
- package/lib/index.d.ts +5 -1
- package/lib/index.js +865 -111
- package/lib/model-defaults.d.ts +23 -0
- package/lib/model-defaults.js +237 -0
- package/lib/providers/claude.d.ts +24 -3
- package/lib/providers/claude.js +35 -24
- package/lib/providers/codex.d.ts +21 -0
- package/lib/providers/codex.js +37 -10
- package/lib/providers/common.d.ts +70 -6
- package/lib/providers/common.js +118 -19
- package/lib/providers/copilot.d.ts +10 -0
- package/lib/providers/copilot.js +21 -8
- package/lib/providers/grok.d.ts +21 -0
- package/lib/providers/grok.js +37 -7
- package/lib/providers/pool-usage.d.ts +23 -2
- package/lib/providers/pool-usage.js +70 -15
- package/lib/providers/rate-limit.d.ts +192 -0
- package/lib/providers/rate-limit.js +338 -0
- package/lib/translate/anthropic.js +5 -4
- package/lib/translate/chat-completions.js +5 -4
- package/lib/translate/responses.js +5 -4
- package/package.json +21 -21
- package/lib/providers/antigravity.d.ts +0 -90
- package/lib/providers/antigravity.js +0 -392
- package/lib/translate/antigravity.d.ts +0 -110
- package/lib/translate/antigravity.js +0 -303
package/lib/client.js
CHANGED
|
@@ -70,6 +70,20 @@ const en = {
|
|
|
70
70
|
usageWindow: "Window",
|
|
71
71
|
usageResets: "resets {date}",
|
|
72
72
|
usagePlan: "Plan: {plan}",
|
|
73
|
+
modelDefaultsTitle: "Default reasoning effort",
|
|
74
|
+
modelDefaultsHint: "The model picker preselects this level when you switch to the model; models without levels always follow the provider.",
|
|
75
|
+
modelDefaultsFollowProvider: "Follow provider",
|
|
76
|
+
modelDefaultsNoLevels: "{count} model(s) advertise no reasoning levels and always follow the provider.",
|
|
77
|
+
modelDefaultsLoading: "Loading models…",
|
|
78
|
+
modelDefaultsLoadFailed: "Failed to load models: {message}",
|
|
79
|
+
modelDefaultsRetry: "Retry",
|
|
80
|
+
modelDefaultsSaving: "Saving…",
|
|
81
|
+
modelDefaultsSaveFailedNamed: "Save failed for {model}: {message}",
|
|
82
|
+
modelDefaultsSummary: "{total} model(s) · {configured} overridden",
|
|
83
|
+
modelDefaultsSummaryNone: "{total} model(s) · following the provider",
|
|
84
|
+
modelDefaultsSummaryEmpty: "No model advertises reasoning levels",
|
|
85
|
+
modelDefaultsFilterPlaceholder: "Filter models",
|
|
86
|
+
modelDefaultsFilterEmpty: "No model matches “{query}”.",
|
|
73
87
|
generating: "Generating image…",
|
|
74
88
|
image: "image",
|
|
75
89
|
viewImage: "View image",
|
|
@@ -159,6 +173,20 @@ const zh = {
|
|
|
159
173
|
usageWindow: "窗口",
|
|
160
174
|
usageResets: "{date} 重置",
|
|
161
175
|
usagePlan: "计划:{plan}",
|
|
176
|
+
modelDefaultsTitle: "默认推理档",
|
|
177
|
+
modelDefaultsHint: "切换到该模型时,模型选择器会预选此档位;没有推理档的模型始终跟随服务商默认。",
|
|
178
|
+
modelDefaultsFollowProvider: "跟随服务商",
|
|
179
|
+
modelDefaultsNoLevels: "{count} 个模型未声明推理档,始终跟随服务商默认。",
|
|
180
|
+
modelDefaultsLoading: "加载模型中…",
|
|
181
|
+
modelDefaultsLoadFailed: "模型加载失败:{message}",
|
|
182
|
+
modelDefaultsRetry: "重试",
|
|
183
|
+
modelDefaultsSaving: "保存中…",
|
|
184
|
+
modelDefaultsSaveFailedNamed: "{model} 保存失败:{message}",
|
|
185
|
+
modelDefaultsSummary: "{total} 个模型 · {configured} 个已覆盖",
|
|
186
|
+
modelDefaultsSummaryNone: "{total} 个模型 · 全部跟随服务商",
|
|
187
|
+
modelDefaultsSummaryEmpty: "没有模型声明推理档",
|
|
188
|
+
modelDefaultsFilterPlaceholder: "筛选模型",
|
|
189
|
+
modelDefaultsFilterEmpty: "没有匹配「{query}」的模型。",
|
|
162
190
|
generating: "正在生成图片…",
|
|
163
191
|
image: "图片",
|
|
164
192
|
viewImage: "查看图片",
|
|
@@ -217,6 +245,17 @@ const zh = {
|
|
|
217
245
|
const SUBSCRIPTIONS_AUTH_CHANNEL$2 = "/subscriptions-auth";
|
|
218
246
|
/** Poll cadence while a provider login attempt is busy. */
|
|
219
247
|
const POLL_INTERVAL_MS$1 = 2e3;
|
|
248
|
+
/**
|
|
249
|
+
* Model count above which the expanded default-effort list also offers a name
|
|
250
|
+
* filter; below it the list is short enough to scan.
|
|
251
|
+
*/
|
|
252
|
+
const MODEL_FILTER_THRESHOLD = 8;
|
|
253
|
+
/**
|
|
254
|
+
* Height cap of the expanded default-effort list, in px. Past it the list
|
|
255
|
+
* scrolls internally so a provider with dozens of models cannot stretch the
|
|
256
|
+
* card (roughly 7 rows, which keeps the next card's header on screen).
|
|
257
|
+
*/
|
|
258
|
+
const MODEL_LIST_MAX_HEIGHT = 260;
|
|
220
259
|
/** Card display metadata, in page order (names are brand names, not translated). */
|
|
221
260
|
const PROVIDERS = [
|
|
222
261
|
{
|
|
@@ -456,6 +495,90 @@ const styles$4 = {
|
|
|
456
495
|
height: "100%",
|
|
457
496
|
borderRadius: 3
|
|
458
497
|
},
|
|
498
|
+
defaultEffort: {
|
|
499
|
+
display: "flex",
|
|
500
|
+
flexDirection: "column",
|
|
501
|
+
gap: 6,
|
|
502
|
+
marginTop: 4,
|
|
503
|
+
borderTop: "1px solid var(--dsw-alias-border-l2)",
|
|
504
|
+
paddingTop: 8
|
|
505
|
+
},
|
|
506
|
+
defaultEffortToggle: {
|
|
507
|
+
boxSizing: "border-box",
|
|
508
|
+
display: "flex",
|
|
509
|
+
alignItems: "center",
|
|
510
|
+
gap: 8,
|
|
511
|
+
width: "100%",
|
|
512
|
+
padding: 0,
|
|
513
|
+
border: "none",
|
|
514
|
+
background: "transparent",
|
|
515
|
+
font: "inherit",
|
|
516
|
+
textAlign: "left",
|
|
517
|
+
cursor: "pointer"
|
|
518
|
+
},
|
|
519
|
+
defaultEffortChevron: {
|
|
520
|
+
marginLeft: "auto",
|
|
521
|
+
flexShrink: 0,
|
|
522
|
+
fontSize: 10,
|
|
523
|
+
lineHeight: "18px",
|
|
524
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
525
|
+
},
|
|
526
|
+
defaultEffortList: {
|
|
527
|
+
display: "flex",
|
|
528
|
+
flexDirection: "column",
|
|
529
|
+
gap: 6,
|
|
530
|
+
maxHeight: MODEL_LIST_MAX_HEIGHT,
|
|
531
|
+
overflowY: "auto",
|
|
532
|
+
paddingRight: 2
|
|
533
|
+
},
|
|
534
|
+
defaultEffortRow: {
|
|
535
|
+
display: "flex",
|
|
536
|
+
alignItems: "center",
|
|
537
|
+
justifyContent: "space-between",
|
|
538
|
+
gap: 8
|
|
539
|
+
},
|
|
540
|
+
defaultEffortName: {
|
|
541
|
+
fontSize: 12,
|
|
542
|
+
lineHeight: "18px",
|
|
543
|
+
color: "var(--dsw-alias-label-primary)",
|
|
544
|
+
overflow: "hidden",
|
|
545
|
+
textOverflow: "ellipsis",
|
|
546
|
+
whiteSpace: "nowrap"
|
|
547
|
+
},
|
|
548
|
+
defaultEffortSaving: {
|
|
549
|
+
marginLeft: "auto",
|
|
550
|
+
flexShrink: 0,
|
|
551
|
+
fontSize: 12,
|
|
552
|
+
lineHeight: "18px",
|
|
553
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
554
|
+
},
|
|
555
|
+
defaultEffortSelect: {
|
|
556
|
+
maxWidth: 220,
|
|
557
|
+
flexShrink: 0,
|
|
558
|
+
height: 28,
|
|
559
|
+
boxSizing: "border-box",
|
|
560
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
561
|
+
borderRadius: 8,
|
|
562
|
+
padding: "0 8px",
|
|
563
|
+
font: "inherit",
|
|
564
|
+
fontSize: 12,
|
|
565
|
+
lineHeight: "18px",
|
|
566
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
567
|
+
color: "var(--dsw-alias-label-primary)"
|
|
568
|
+
},
|
|
569
|
+
defaultEffortFilter: {
|
|
570
|
+
height: 28,
|
|
571
|
+
width: "100%",
|
|
572
|
+
boxSizing: "border-box",
|
|
573
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
574
|
+
borderRadius: 8,
|
|
575
|
+
padding: "0 8px",
|
|
576
|
+
font: "inherit",
|
|
577
|
+
fontSize: 12,
|
|
578
|
+
lineHeight: "18px",
|
|
579
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
580
|
+
color: "var(--dsw-alias-label-primary)"
|
|
581
|
+
},
|
|
459
582
|
manual: {
|
|
460
583
|
marginTop: 4,
|
|
461
584
|
fontSize: 12,
|
|
@@ -591,6 +714,16 @@ function dotColor(status) {
|
|
|
591
714
|
return "var(--dsw-alias-label-dimmed)";
|
|
592
715
|
}
|
|
593
716
|
/**
|
|
717
|
+
* Whether a provider has at least one connected account. Multi-account made
|
|
718
|
+
* "logged in" a property of the account list rather than a flag, so every
|
|
719
|
+
* logged-in test goes through this one predicate.
|
|
720
|
+
* @param status - the provider's last reported state, possibly absent.
|
|
721
|
+
* @returns true when the provider serves at least one account.
|
|
722
|
+
*/
|
|
723
|
+
function hasAccount(status) {
|
|
724
|
+
return (status?.accounts.length ?? 0) > 0;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
594
727
|
* One-line status text for one provider state.
|
|
595
728
|
* @param t - section translate.
|
|
596
729
|
* @param status - the provider's last reported state.
|
|
@@ -631,6 +764,55 @@ function messageColor(tone) {
|
|
|
631
764
|
return tone === "error" ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-state-success-primary)";
|
|
632
765
|
}
|
|
633
766
|
/**
|
|
767
|
+
* Derive one provider's default-effort section from its catalog and filter.
|
|
768
|
+
* Pure so the collapsed-header counts and the filter stay testable without a
|
|
769
|
+
* DOM: rows come only from models that advertise levels, the count of the rest
|
|
770
|
+
* rides as one line, and the filter matches display name or model id.
|
|
771
|
+
* @param models - the provider's catalog models, or undefined while loading.
|
|
772
|
+
* @param filter - the raw filter input (trimmed and lowercased here).
|
|
773
|
+
* @returns the section's rows and header counts.
|
|
774
|
+
*/
|
|
775
|
+
function deriveModelDefaultsView(models, filter) {
|
|
776
|
+
const all = models ?? [];
|
|
777
|
+
const withEfforts = all.filter((model) => model.efforts.length > 0);
|
|
778
|
+
const query = filter.trim().toLowerCase();
|
|
779
|
+
return {
|
|
780
|
+
shown: query === "" ? withEfforts : withEfforts.filter((model) => model.name.toLowerCase().includes(query) || model.id.toLowerCase().includes(query)),
|
|
781
|
+
total: withEfforts.length,
|
|
782
|
+
overridden: withEfforts.filter((model) => model.configured !== void 0).length,
|
|
783
|
+
withoutEfforts: all.length - withEfforts.length,
|
|
784
|
+
showFilter: withEfforts.length > MODEL_FILTER_THRESHOLD
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Whether the default-effort catalog needs (re)fetching.
|
|
789
|
+
*
|
|
790
|
+
* Fetching is gated on an *attempt* signature rather than on the payload
|
|
791
|
+
* being empty: an empty answer is a legitimate result (a narrowed
|
|
792
|
+
* `config.providers`, or a catalog that is momentarily unavailable), and
|
|
793
|
+
* treating it as "not loaded yet" re-ran this effect forever. The signature
|
|
794
|
+
* also covers the accounts, so logging a second provider in refetches
|
|
795
|
+
* instead of leaving that card on the previous answer.
|
|
796
|
+
* @param input - the decision inputs.
|
|
797
|
+
* @returns true when the caller should start a fetch.
|
|
798
|
+
*/
|
|
799
|
+
function shouldFetchModelDefaults(input) {
|
|
800
|
+
if (input.failed) return false;
|
|
801
|
+
if (input.loggedIn.length === 0) return false;
|
|
802
|
+
if (!input.open.some((provider) => input.loggedIn.includes(provider))) return false;
|
|
803
|
+
return input.loadedFor !== input.signature;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Stable signature of the accounts a catalog answer depends on. A change
|
|
807
|
+
* means a previous answer is stale (an account arrived or left), so the next
|
|
808
|
+
* open disclosure refetches.
|
|
809
|
+
* @param statuses - the per-provider status snapshot.
|
|
810
|
+
* @returns a signature string, stable across renders with equal accounts.
|
|
811
|
+
*/
|
|
812
|
+
function modelDefaultsSignature(statuses) {
|
|
813
|
+
return PROVIDERS.map(({ id }) => `${id}:${(statuses[id]?.accounts ?? []).map((account) => account.key).sort().join(",")}`).join("|");
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
634
816
|
* The Subscriptions settings page component.
|
|
635
817
|
* @param props - the slot inject face ({@link SubscriptionsSectionInjected}).
|
|
636
818
|
* @returns the section body, or a notice while the RPC face is absent.
|
|
@@ -672,6 +854,24 @@ function SubscriptionsSection(props) {
|
|
|
672
854
|
const [proxyTesting, setProxyTesting] = (0, react.useState)(false);
|
|
673
855
|
const [proxyMessage, setProxyMessage] = (0, react.useState)(void 0);
|
|
674
856
|
const [proxyTestResult, setProxyTestResult] = (0, react.useState)(void 0);
|
|
857
|
+
/** Per-model default-effort picker state as answered by `modelDefaults`. */
|
|
858
|
+
const [modelDefaults, setModelDefaults] = (0, react.useState)({});
|
|
859
|
+
const [modelDefaultsLoading, setModelDefaultsLoading] = (0, react.useState)(false);
|
|
860
|
+
const [modelDefaultsLoadError, setModelDefaultsLoadError] = (0, react.useState)(void 0);
|
|
861
|
+
/** One set in flight: the `${provider}/${model}` key. */
|
|
862
|
+
const [modelDefaultsSaving, setModelDefaultsSaving] = (0, react.useState)(void 0);
|
|
863
|
+
/** Per-model save failures, keyed `${provider}/${model}`. */
|
|
864
|
+
const [modelDefaultsSaveErrors, setModelDefaultsSaveErrors] = (0, react.useState)({});
|
|
865
|
+
/** Providers whose default-effort disclosure is open (collapsed by default). */
|
|
866
|
+
const [modelDefaultsOpen, setModelDefaultsOpen] = (0, react.useState)({});
|
|
867
|
+
/** Per-provider name filter of the expanded list. */
|
|
868
|
+
const [modelDefaultsFilters, setModelDefaultsFilters] = (0, react.useState)({});
|
|
869
|
+
/** Account signature the last completed catalog fetch was answered for. */
|
|
870
|
+
const [modelDefaultsLoadedFor, setModelDefaultsLoadedFor] = (0, react.useState)(void 0);
|
|
871
|
+
/** Optimistic in-flight selections, keyed `${provider}/${model}` ('' = follow provider). */
|
|
872
|
+
const [modelDefaultsPending, setModelDefaultsPending] = (0, react.useState)({});
|
|
873
|
+
/** Guard the catalog effect against concurrent loads. */
|
|
874
|
+
const modelDefaultsInflightRef = (0, react.useRef)(false);
|
|
675
875
|
const setProviderError = (0, react.useCallback)((provider, message) => {
|
|
676
876
|
if (!mountedRef.current) return;
|
|
677
877
|
setErrors((prev) => {
|
|
@@ -733,7 +933,7 @@ function SubscriptionsSection(props) {
|
|
|
733
933
|
pollersRef.current.clear();
|
|
734
934
|
};
|
|
735
935
|
}, [refresh, startPolling]);
|
|
736
|
-
const loadUsage = (0, react.useCallback)(async (provider, account) => {
|
|
936
|
+
const loadUsage = (0, react.useCallback)(async (provider, account, force = false) => {
|
|
737
937
|
const key = `${provider}:${account}`;
|
|
738
938
|
if (rpc === void 0 || usageInflightRef.current.has(key)) return;
|
|
739
939
|
usageInflightRef.current.add(key);
|
|
@@ -744,7 +944,8 @@ function SubscriptionsSection(props) {
|
|
|
744
944
|
try {
|
|
745
945
|
const usage = await callSubscriptionsAuth(rpc, "usage", {
|
|
746
946
|
provider,
|
|
747
|
-
account
|
|
947
|
+
account,
|
|
948
|
+
...force ? { force: true } : {}
|
|
748
949
|
});
|
|
749
950
|
if (!mountedRef.current) return;
|
|
750
951
|
setUsages((prev) => ({
|
|
@@ -784,6 +985,116 @@ function SubscriptionsSection(props) {
|
|
|
784
985
|
usageErrors,
|
|
785
986
|
loadUsage
|
|
786
987
|
]);
|
|
988
|
+
const loadModelDefaultsData = (0, react.useCallback)(async (signature) => {
|
|
989
|
+
if (rpc === void 0 || modelDefaultsInflightRef.current) return;
|
|
990
|
+
modelDefaultsInflightRef.current = true;
|
|
991
|
+
setModelDefaultsLoading(true);
|
|
992
|
+
try {
|
|
993
|
+
const catalog = await callSubscriptionsAuth(rpc, "modelDefaults", {});
|
|
994
|
+
if (!mountedRef.current) return;
|
|
995
|
+
const next = {};
|
|
996
|
+
for (const entry of catalog) next[entry.provider] = entry;
|
|
997
|
+
setModelDefaults(next);
|
|
998
|
+
setModelDefaultsLoadError(void 0);
|
|
999
|
+
setModelDefaultsLoadedFor(signature);
|
|
1000
|
+
setModelDefaultsSaveErrors({});
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
if (mountedRef.current) setModelDefaultsLoadError(messageOf(error));
|
|
1003
|
+
} finally {
|
|
1004
|
+
modelDefaultsInflightRef.current = false;
|
|
1005
|
+
if (mountedRef.current) setModelDefaultsLoading(false);
|
|
1006
|
+
}
|
|
1007
|
+
}, [rpc]);
|
|
1008
|
+
(0, react.useEffect)(() => {
|
|
1009
|
+
const loggedIn = PROVIDERS.filter(({ id }) => hasAccount(statuses[id])).map(({ id }) => id);
|
|
1010
|
+
const signature = modelDefaultsSignature(statuses);
|
|
1011
|
+
if (loggedIn.length > 0) {
|
|
1012
|
+
if (shouldFetchModelDefaults({
|
|
1013
|
+
loggedIn,
|
|
1014
|
+
open: PROVIDERS.filter(({ id }) => modelDefaultsOpen[id] === true).map(({ id }) => id),
|
|
1015
|
+
loadedFor: modelDefaultsLoadedFor,
|
|
1016
|
+
signature,
|
|
1017
|
+
failed: modelDefaultsLoadError !== void 0
|
|
1018
|
+
})) loadModelDefaultsData(signature);
|
|
1019
|
+
} else if (modelDefaultsLoadedFor !== void 0 || Object.keys(modelDefaults).length > 0 || Object.keys(modelDefaultsOpen).length > 0) {
|
|
1020
|
+
setModelDefaults({});
|
|
1021
|
+
setModelDefaultsSaveErrors({});
|
|
1022
|
+
setModelDefaultsLoadError(void 0);
|
|
1023
|
+
setModelDefaultsLoadedFor(void 0);
|
|
1024
|
+
setModelDefaultsLoading(false);
|
|
1025
|
+
setModelDefaultsOpen({});
|
|
1026
|
+
setModelDefaultsFilters({});
|
|
1027
|
+
}
|
|
1028
|
+
}, [
|
|
1029
|
+
statuses,
|
|
1030
|
+
modelDefaults,
|
|
1031
|
+
modelDefaultsOpen,
|
|
1032
|
+
modelDefaultsLoadError,
|
|
1033
|
+
modelDefaultsLoadedFor,
|
|
1034
|
+
loadModelDefaultsData
|
|
1035
|
+
]);
|
|
1036
|
+
/** Open or close one provider's default-effort disclosure. */
|
|
1037
|
+
const toggleModelDefaults = (0, react.useCallback)((provider) => {
|
|
1038
|
+
setModelDefaultsOpen((prev) => ({
|
|
1039
|
+
...prev,
|
|
1040
|
+
[provider]: prev[provider] !== true
|
|
1041
|
+
}));
|
|
1042
|
+
}, []);
|
|
1043
|
+
const setModelDefault = (0, react.useCallback)(async (provider, model, effort) => {
|
|
1044
|
+
if (rpc === void 0) return;
|
|
1045
|
+
const key = `${provider}/${model}`;
|
|
1046
|
+
setModelDefaultsSaving(key);
|
|
1047
|
+
setModelDefaultsPending((prev) => ({
|
|
1048
|
+
...prev,
|
|
1049
|
+
[key]: effort ?? ""
|
|
1050
|
+
}));
|
|
1051
|
+
setModelDefaultsSaveErrors((prev) => {
|
|
1052
|
+
const next = { ...prev };
|
|
1053
|
+
delete next[key];
|
|
1054
|
+
return next;
|
|
1055
|
+
});
|
|
1056
|
+
try {
|
|
1057
|
+
await callSubscriptionsAuth(rpc, "setModelDefault", {
|
|
1058
|
+
provider,
|
|
1059
|
+
model,
|
|
1060
|
+
...effort === void 0 ? {} : { effort }
|
|
1061
|
+
});
|
|
1062
|
+
if (!mountedRef.current) return;
|
|
1063
|
+
setModelDefaults((prev) => {
|
|
1064
|
+
const section = prev[provider];
|
|
1065
|
+
if (section === void 0) return prev;
|
|
1066
|
+
return {
|
|
1067
|
+
...prev,
|
|
1068
|
+
[provider]: {
|
|
1069
|
+
...section,
|
|
1070
|
+
models: section.models.map((entry) => {
|
|
1071
|
+
if (entry.id !== model) return entry;
|
|
1072
|
+
if (effort !== void 0) return {
|
|
1073
|
+
...entry,
|
|
1074
|
+
configured: effort
|
|
1075
|
+
};
|
|
1076
|
+
const { configured: _cleared,...rest } = entry;
|
|
1077
|
+
return rest;
|
|
1078
|
+
})
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
});
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
if (mountedRef.current) setModelDefaultsSaveErrors((prev) => ({
|
|
1084
|
+
...prev,
|
|
1085
|
+
[key]: messageOf(error)
|
|
1086
|
+
}));
|
|
1087
|
+
} finally {
|
|
1088
|
+
if (mountedRef.current) {
|
|
1089
|
+
setModelDefaultsSaving((current) => current === key ? void 0 : current);
|
|
1090
|
+
setModelDefaultsPending((prev) => {
|
|
1091
|
+
const next = { ...prev };
|
|
1092
|
+
delete next[key];
|
|
1093
|
+
return next;
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}, [rpc]);
|
|
787
1098
|
const login = (0, react.useCallback)(async (provider, method) => {
|
|
788
1099
|
if (rpc === void 0) return;
|
|
789
1100
|
setProviderError(provider, void 0);
|
|
@@ -1148,7 +1459,7 @@ function SubscriptionsSection(props) {
|
|
|
1148
1459
|
},
|
|
1149
1460
|
disabled: usageLoading[usageKey] === true,
|
|
1150
1461
|
onClick: () => {
|
|
1151
|
-
loadUsage(id, account.key);
|
|
1462
|
+
loadUsage(id, account.key, true);
|
|
1152
1463
|
},
|
|
1153
1464
|
children: t("usageRefresh")
|
|
1154
1465
|
})
|
|
@@ -1235,6 +1546,135 @@ function SubscriptionsSection(props) {
|
|
|
1235
1546
|
style: styles$4.statusLine,
|
|
1236
1547
|
children: t("addAccountHint")
|
|
1237
1548
|
}),
|
|
1549
|
+
accounts.length > 0 && (() => {
|
|
1550
|
+
const open = modelDefaultsOpen[id] === true;
|
|
1551
|
+
const catalog = modelDefaults[id];
|
|
1552
|
+
const filter = modelDefaultsFilters[id] ?? "";
|
|
1553
|
+
const view = deriveModelDefaultsView(catalog?.models, filter);
|
|
1554
|
+
const saveErrors = Object.entries(modelDefaultsSaveErrors).filter(([key]) => key.startsWith(`${id}/`));
|
|
1555
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1556
|
+
style: styles$4.defaultEffort,
|
|
1557
|
+
children: [
|
|
1558
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1559
|
+
type: "button",
|
|
1560
|
+
style: styles$4.defaultEffortToggle,
|
|
1561
|
+
"aria-expanded": open,
|
|
1562
|
+
onClick: () => {
|
|
1563
|
+
toggleModelDefaults(id);
|
|
1564
|
+
},
|
|
1565
|
+
children: [
|
|
1566
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1567
|
+
style: styles$4.usageTitle,
|
|
1568
|
+
children: t("modelDefaultsTitle")
|
|
1569
|
+
}),
|
|
1570
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1571
|
+
style: styles$4.usagePlan,
|
|
1572
|
+
children: catalog === void 0 ? modelDefaultsLoading ? t("modelDefaultsLoading") : "" : view.total === 0 ? t("modelDefaultsSummaryEmpty") : view.overridden === 0 ? t("modelDefaultsSummaryNone", { total: view.total }) : t("modelDefaultsSummary", {
|
|
1573
|
+
total: view.total,
|
|
1574
|
+
configured: view.overridden
|
|
1575
|
+
})
|
|
1576
|
+
}),
|
|
1577
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1578
|
+
style: styles$4.defaultEffortChevron,
|
|
1579
|
+
"aria-hidden": "true",
|
|
1580
|
+
children: open ? "▲" : "▼"
|
|
1581
|
+
})
|
|
1582
|
+
]
|
|
1583
|
+
}),
|
|
1584
|
+
saveErrors.map(([key, message]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1585
|
+
style: styles$4.errorLine,
|
|
1586
|
+
role: "alert",
|
|
1587
|
+
children: t("modelDefaultsSaveFailedNamed", {
|
|
1588
|
+
model: catalog?.models.find((entry) => `${id}/${entry.id}` === key)?.name ?? key.slice(id.length + 1),
|
|
1589
|
+
message
|
|
1590
|
+
})
|
|
1591
|
+
}, key)),
|
|
1592
|
+
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1593
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1594
|
+
style: styles$4.statusLine,
|
|
1595
|
+
children: t("modelDefaultsHint")
|
|
1596
|
+
}),
|
|
1597
|
+
modelDefaultsLoadError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1598
|
+
style: styles$4.errorLine,
|
|
1599
|
+
children: t("modelDefaultsLoadFailed", { message: modelDefaultsLoadError })
|
|
1600
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1601
|
+
style: styles$4.actions,
|
|
1602
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1603
|
+
type: "button",
|
|
1604
|
+
style: styles$4.button,
|
|
1605
|
+
onClick: () => {
|
|
1606
|
+
setModelDefaultsLoadError(void 0);
|
|
1607
|
+
},
|
|
1608
|
+
children: t("modelDefaultsRetry")
|
|
1609
|
+
})
|
|
1610
|
+
})] }),
|
|
1611
|
+
modelDefaultsLoadError === void 0 && catalog === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1612
|
+
style: styles$4.statusLine,
|
|
1613
|
+
children: t("modelDefaultsLoading")
|
|
1614
|
+
}),
|
|
1615
|
+
view.showFilter && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1616
|
+
style: styles$4.defaultEffortFilter,
|
|
1617
|
+
value: filter,
|
|
1618
|
+
placeholder: t("modelDefaultsFilterPlaceholder"),
|
|
1619
|
+
"aria-label": t("modelDefaultsFilterPlaceholder"),
|
|
1620
|
+
onChange: (event) => {
|
|
1621
|
+
setModelDefaultsFilters((prev) => ({
|
|
1622
|
+
...prev,
|
|
1623
|
+
[id]: event.target.value
|
|
1624
|
+
}));
|
|
1625
|
+
}
|
|
1626
|
+
}),
|
|
1627
|
+
view.shown.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1628
|
+
style: styles$4.defaultEffortList,
|
|
1629
|
+
children: view.shown.map((model) => {
|
|
1630
|
+
const rowKey = `${id}/${model.id}`;
|
|
1631
|
+
const saving = modelDefaultsSaving === rowKey;
|
|
1632
|
+
const labelId = `dsh-model-default-${id}-${model.id}`;
|
|
1633
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1634
|
+
style: styles$4.defaultEffortRow,
|
|
1635
|
+
children: [
|
|
1636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1637
|
+
id: labelId,
|
|
1638
|
+
style: styles$4.defaultEffortName,
|
|
1639
|
+
title: model.id,
|
|
1640
|
+
children: model.name
|
|
1641
|
+
}),
|
|
1642
|
+
saving && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1643
|
+
style: styles$4.defaultEffortSaving,
|
|
1644
|
+
children: t("modelDefaultsSaving")
|
|
1645
|
+
}),
|
|
1646
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1647
|
+
style: styles$4.defaultEffortSelect,
|
|
1648
|
+
"aria-labelledby": labelId,
|
|
1649
|
+
value: modelDefaultsPending[rowKey] ?? model.configured ?? "",
|
|
1650
|
+
disabled: saving,
|
|
1651
|
+
onChange: (event) => {
|
|
1652
|
+
setModelDefault(id, model.id, event.target.value === "" ? void 0 : event.target.value);
|
|
1653
|
+
},
|
|
1654
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1655
|
+
value: "",
|
|
1656
|
+
children: t("modelDefaultsFollowProvider")
|
|
1657
|
+
}), model.efforts.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1658
|
+
value: effort.id,
|
|
1659
|
+
children: effort.name
|
|
1660
|
+
}, effort.id))]
|
|
1661
|
+
})
|
|
1662
|
+
]
|
|
1663
|
+
}, model.id);
|
|
1664
|
+
})
|
|
1665
|
+
}),
|
|
1666
|
+
catalog !== void 0 && view.shown.length === 0 && filter.trim() !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1667
|
+
style: styles$4.statusLine,
|
|
1668
|
+
children: t("modelDefaultsFilterEmpty", { query: filter.trim() })
|
|
1669
|
+
}),
|
|
1670
|
+
view.withoutEfforts > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1671
|
+
style: styles$4.statusLine,
|
|
1672
|
+
children: t("modelDefaultsNoLevels", { count: view.withoutEfforts })
|
|
1673
|
+
})
|
|
1674
|
+
] })
|
|
1675
|
+
]
|
|
1676
|
+
});
|
|
1677
|
+
})(),
|
|
1238
1678
|
busy && deviceCode !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1239
1679
|
style: styles$4.deviceCode,
|
|
1240
1680
|
children: [
|
|
@@ -2092,14 +2532,21 @@ function VideoGenerateToolview(props) {
|
|
|
2092
2532
|
* known state, so a transient failure never locks the toggle away.
|
|
2093
2533
|
*
|
|
2094
2534
|
* `sessionId` is a plain string: slot and command contexts brand it through
|
|
2095
|
-
* different dsh-session copies, and only the
|
|
2535
|
+
* different dsh-session copies, and only the service boundary needs one.
|
|
2536
|
+
*
|
|
2537
|
+
* `models` resolves lazily per call: the ui-model-selection service may
|
|
2538
|
+
* register after this plugin applies, and a shell without it (no model seat
|
|
2539
|
+
* at all) simply keeps the toggle hidden.
|
|
2096
2540
|
*/
|
|
2097
|
-
function createSpeedLoader(connection, sessionId) {
|
|
2541
|
+
function createSpeedLoader(connection, models, sessionId) {
|
|
2098
2542
|
return async () => {
|
|
2099
2543
|
const state = await callSubscriptionsAuth(connection.rpc, "speed", { sessionId });
|
|
2100
|
-
const
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2544
|
+
const directories = models();
|
|
2545
|
+
if (directories === void 0) return {
|
|
2546
|
+
visible: false,
|
|
2547
|
+
tier: state.tier
|
|
2548
|
+
};
|
|
2549
|
+
const { current } = await directories.directoryFor(sessionId).load();
|
|
2103
2550
|
return {
|
|
2104
2551
|
visible: current !== null && current.provider === "codex" && state.fastModels.includes(current.model),
|
|
2105
2552
|
tier: state.tier
|
|
@@ -2379,13 +2826,14 @@ function apply(ctx) {
|
|
|
2379
2826
|
locale: NS,
|
|
2380
2827
|
inject: videoToolviewInjected
|
|
2381
2828
|
}, VideoGenerateToolview));
|
|
2829
|
+
const models = () => ctx.get("modelDirectories");
|
|
2382
2830
|
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
2383
2831
|
name: "conversation.input.right",
|
|
2384
2832
|
id: "codex-speed",
|
|
2385
2833
|
order: 0,
|
|
2386
2834
|
locale: NS,
|
|
2387
2835
|
inject: (sessionId) => ({
|
|
2388
|
-
loadSpeed: createSpeedLoader(connection, sessionId),
|
|
2836
|
+
loadSpeed: createSpeedLoader(connection, models, sessionId),
|
|
2389
2837
|
setSpeed: createSpeedSetter(connection, sessionId)
|
|
2390
2838
|
})
|
|
2391
2839
|
}, SpeedSelect));
|
|
@@ -2398,7 +2846,7 @@ function apply(ctx) {
|
|
|
2398
2846
|
ui: {
|
|
2399
2847
|
kind: "popupSelect",
|
|
2400
2848
|
options: async (session) => {
|
|
2401
|
-
const state = await createSpeedLoader(connection, session.sessionId)();
|
|
2849
|
+
const state = await createSpeedLoader(connection, models, session.sessionId)();
|
|
2402
2850
|
if (!state.visible) throw new Error(t("commandFastUnavailable"));
|
|
2403
2851
|
return [{
|
|
2404
2852
|
id: "standard",
|