dsh-plugin-subscriptions 0.5.2 → 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 +79 -5
- package/README.zh.md +78 -4
- package/lib/auth/rpc.d.ts +64 -13
- package/lib/auth/rpc.js +75 -10
- package/lib/auth/store.d.ts +75 -17
- package/lib/auth/store.js +148 -27
- 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 +83 -3
- package/lib/client/SubscriptionsSection.js +411 -62
- 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 +46 -10
- package/lib/client/locales.js +46 -10
- package/lib/client.js +703 -132
- 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 +26 -1
- package/lib/index.js +2377 -309
- package/lib/model-defaults.d.ts +23 -0
- package/lib/model-defaults.js +237 -0
- package/lib/providers/accounts.d.ts +102 -0
- package/lib/providers/accounts.js +123 -0
- package/lib/providers/claude.d.ts +46 -7
- package/lib/providers/claude.js +125 -34
- package/lib/providers/codex.d.ts +45 -3
- package/lib/providers/codex.js +152 -26
- package/lib/providers/common.d.ts +87 -6
- package/lib/providers/common.js +185 -22
- package/lib/providers/copilot.d.ts +32 -3
- package/lib/providers/copilot.js +111 -19
- package/lib/providers/grok.d.ts +45 -4
- package/lib/providers/grok.js +136 -20
- package/lib/providers/pool-family.d.ts +56 -0
- package/lib/providers/pool-family.js +45 -0
- package/lib/providers/pool-health.d.ts +74 -0
- package/lib/providers/pool-health.js +148 -0
- package/lib/providers/pool-usage.d.ts +78 -0
- package/lib/providers/pool-usage.js +185 -0
- package/lib/providers/pool.d.ts +107 -0
- package/lib/providers/pool.js +371 -0
- package/lib/providers/rate-limit.d.ts +192 -0
- package/lib/providers/rate-limit.js +338 -0
- package/lib/tools/image-generate.d.ts +3 -3
- package/lib/tools/image-generate.js +2 -1
- package/lib/tools/video-generate.d.ts +2 -2
- package/lib/tools/video-generate.js +2 -1
- package/lib/tools/x-search.d.ts +2 -2
- package/lib/tools/x-search.js +2 -1
- 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
|
@@ -39,15 +39,19 @@ const en = {
|
|
|
39
39
|
unavailable: "Connection unavailable; subscription status cannot be loaded.",
|
|
40
40
|
checking: "Checking…",
|
|
41
41
|
loginInProgress: "Login in progress…",
|
|
42
|
-
loggedIn: "Logged in",
|
|
43
|
-
loggedInAccount: "Logged in as {account}",
|
|
44
|
-
loggedInExpires: "Logged in · expires {date}",
|
|
45
|
-
loggedInAccountExpires: "Logged in as {account} · expires {date}",
|
|
46
42
|
notLoggedIn: "Not logged in",
|
|
43
|
+
loggedInCount: "{count} account(s) connected",
|
|
44
|
+
accountExpires: "expires {date}",
|
|
45
|
+
defaultBadge: "Default",
|
|
46
|
+
setDefault: "Set as default",
|
|
47
|
+
addAccount: "Add account",
|
|
48
|
+
addAccountOAuth: "Browser authorization",
|
|
49
|
+
addAccountKeychain: "Import Claude Code",
|
|
50
|
+
addAccountHint: "Browser authorization signs in whichever account the browser currently uses — switch accounts there first (or use an incognito window with the manual code below) to add a different one.",
|
|
47
51
|
login: "Log in",
|
|
48
52
|
cancel: "Cancel",
|
|
49
53
|
logout: "Log out",
|
|
50
|
-
|
|
54
|
+
logoutAccountConfirm: "Log out {account} of {provider}?",
|
|
51
55
|
manualSummary: "Browser flow not working? Paste the callback URL or code",
|
|
52
56
|
manualPlaceholder: "Paste the callback URL or code",
|
|
53
57
|
submit: "Submit",
|
|
@@ -66,6 +70,20 @@ const en = {
|
|
|
66
70
|
usageWindow: "Window",
|
|
67
71
|
usageResets: "resets {date}",
|
|
68
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}”.",
|
|
69
87
|
generating: "Generating image…",
|
|
70
88
|
image: "image",
|
|
71
89
|
viewImage: "View image",
|
|
@@ -124,15 +142,19 @@ const zh = {
|
|
|
124
142
|
unavailable: "连接不可用,无法加载订阅状态。",
|
|
125
143
|
checking: "查询中…",
|
|
126
144
|
loginInProgress: "登录中…",
|
|
127
|
-
loggedIn: "已登录",
|
|
128
|
-
loggedInAccount: "已登录:{account}",
|
|
129
|
-
loggedInExpires: "已登录 · 过期时间 {date}",
|
|
130
|
-
loggedInAccountExpires: "已登录:{account} · 过期时间 {date}",
|
|
131
145
|
notLoggedIn: "未登录",
|
|
146
|
+
loggedInCount: "已连接 {count} 个账号",
|
|
147
|
+
accountExpires: "过期时间 {date}",
|
|
148
|
+
defaultBadge: "默认",
|
|
149
|
+
setDefault: "设为默认",
|
|
150
|
+
addAccount: "添加账号",
|
|
151
|
+
addAccountOAuth: "浏览器授权",
|
|
152
|
+
addAccountKeychain: "导入 Claude Code",
|
|
153
|
+
addAccountHint: "浏览器授权以浏览器当前登录的账号为准;要添加不同账号,请先在浏览器里切换账号,或用无痕窗口走下方手动授权码。",
|
|
132
154
|
login: "登录",
|
|
133
155
|
cancel: "取消",
|
|
134
156
|
logout: "退出登录",
|
|
135
|
-
|
|
157
|
+
logoutAccountConfirm: "确定退出 {provider} 的账号 {account} 吗?",
|
|
136
158
|
manualSummary: "浏览器流程无法完成?粘贴回调 URL 或授权码",
|
|
137
159
|
manualPlaceholder: "粘贴回调 URL 或授权码",
|
|
138
160
|
submit: "提交",
|
|
@@ -151,6 +173,20 @@ const zh = {
|
|
|
151
173
|
usageWindow: "窗口",
|
|
152
174
|
usageResets: "{date} 重置",
|
|
153
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}」的模型。",
|
|
154
190
|
generating: "正在生成图片…",
|
|
155
191
|
image: "图片",
|
|
156
192
|
viewImage: "查看图片",
|
|
@@ -209,6 +245,17 @@ const zh = {
|
|
|
209
245
|
const SUBSCRIPTIONS_AUTH_CHANNEL$2 = "/subscriptions-auth";
|
|
210
246
|
/** Poll cadence while a provider login attempt is busy. */
|
|
211
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;
|
|
212
259
|
/** Card display metadata, in page order (names are brand names, not translated). */
|
|
213
260
|
const PROVIDERS = [
|
|
214
261
|
{
|
|
@@ -252,6 +299,14 @@ async function callSubscriptionsAuth(rpc, endpoint, payload) {
|
|
|
252
299
|
function messageOf(error) {
|
|
253
300
|
return error instanceof Error ? error.message : String(error);
|
|
254
301
|
}
|
|
302
|
+
/** Copy a keyed map without the entries whose key is not in `live`. */
|
|
303
|
+
function dropStale(map, live) {
|
|
304
|
+
const stale = Object.keys(map).filter((key) => !live.has(key));
|
|
305
|
+
if (stale.length === 0) return map;
|
|
306
|
+
const next = { ...map };
|
|
307
|
+
for (const key of stale) delete next[key];
|
|
308
|
+
return next;
|
|
309
|
+
}
|
|
255
310
|
/**
|
|
256
311
|
* English-dictionary fallback for a missing inject `t` (standalone renders);
|
|
257
312
|
* the slot inject always supplies the locale-bound one.
|
|
@@ -399,6 +454,36 @@ const styles$4 = {
|
|
|
399
454
|
lineHeight: "18px",
|
|
400
455
|
color: "var(--dsw-alias-label-tertiary)"
|
|
401
456
|
},
|
|
457
|
+
accountRow: {
|
|
458
|
+
display: "flex",
|
|
459
|
+
flexDirection: "column",
|
|
460
|
+
gap: 6,
|
|
461
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
462
|
+
borderRadius: 8,
|
|
463
|
+
padding: "8px 10px",
|
|
464
|
+
marginTop: 4
|
|
465
|
+
},
|
|
466
|
+
accountHeader: {
|
|
467
|
+
display: "flex",
|
|
468
|
+
alignItems: "center",
|
|
469
|
+
gap: 8
|
|
470
|
+
},
|
|
471
|
+
accountName: {
|
|
472
|
+
fontSize: 13,
|
|
473
|
+
lineHeight: "20px",
|
|
474
|
+
color: "var(--dsw-alias-label-primary)",
|
|
475
|
+
userSelect: "all"
|
|
476
|
+
},
|
|
477
|
+
starButton: {
|
|
478
|
+
border: "none",
|
|
479
|
+
background: "transparent",
|
|
480
|
+
padding: 0,
|
|
481
|
+
font: "inherit",
|
|
482
|
+
fontSize: 14,
|
|
483
|
+
lineHeight: "20px",
|
|
484
|
+
cursor: "pointer",
|
|
485
|
+
color: "var(--dsw-alias-state-warn-label)"
|
|
486
|
+
},
|
|
402
487
|
usageTrack: {
|
|
403
488
|
height: 6,
|
|
404
489
|
borderRadius: 3,
|
|
@@ -410,6 +495,90 @@ const styles$4 = {
|
|
|
410
495
|
height: "100%",
|
|
411
496
|
borderRadius: 3
|
|
412
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
|
+
},
|
|
413
582
|
manual: {
|
|
414
583
|
marginTop: 4,
|
|
415
584
|
fontSize: 12,
|
|
@@ -541,10 +710,20 @@ const styles$4 = {
|
|
|
541
710
|
/** Status dot color for one provider state. */
|
|
542
711
|
function dotColor(status) {
|
|
543
712
|
if (status?.busy === true) return "var(--dsw-alias-state-warn-label)";
|
|
544
|
-
if (status?.
|
|
713
|
+
if ((status?.accounts.length ?? 0) > 0) return "var(--dsw-alias-state-success-primary)";
|
|
545
714
|
return "var(--dsw-alias-label-dimmed)";
|
|
546
715
|
}
|
|
547
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
|
+
/**
|
|
548
727
|
* One-line status text for one provider state.
|
|
549
728
|
* @param t - section translate.
|
|
550
729
|
* @param status - the provider's last reported state.
|
|
@@ -553,15 +732,7 @@ function dotColor(status) {
|
|
|
553
732
|
function statusText(t, status) {
|
|
554
733
|
if (status === void 0) return t("checking");
|
|
555
734
|
if (status.busy) return t("loginInProgress");
|
|
556
|
-
if (status.
|
|
557
|
-
const params = {};
|
|
558
|
-
if (status.account !== void 0) params.account = status.account;
|
|
559
|
-
if (status.expiresAt !== void 0) params.date = new Date(status.expiresAt).toLocaleString();
|
|
560
|
-
if (params.account !== void 0 && params.date !== void 0) return t("loggedInAccountExpires", params);
|
|
561
|
-
if (params.account !== void 0) return t("loggedInAccount", params);
|
|
562
|
-
if (params.date !== void 0) return t("loggedInExpires", params);
|
|
563
|
-
return t("loggedIn");
|
|
564
|
-
}
|
|
735
|
+
if (status.accounts.length > 0) return t("loggedInCount", { count: status.accounts.length });
|
|
565
736
|
return t("notLoggedIn");
|
|
566
737
|
}
|
|
567
738
|
/**
|
|
@@ -593,6 +764,55 @@ function messageColor(tone) {
|
|
|
593
764
|
return tone === "error" ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-state-success-primary)";
|
|
594
765
|
}
|
|
595
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
|
+
/**
|
|
596
816
|
* The Subscriptions settings page component.
|
|
597
817
|
* @param props - the slot inject face ({@link SubscriptionsSectionInjected}).
|
|
598
818
|
* @returns the section body, or a notice while the RPC face is absent.
|
|
@@ -611,12 +831,13 @@ function SubscriptionsSection(props) {
|
|
|
611
831
|
/** Pending device-flow codes (copilot), shown while the attempt polls. */
|
|
612
832
|
const [deviceCodes, setDeviceCodes] = (0, react.useState)({});
|
|
613
833
|
const [copiedCode, setCopiedCode] = (0, react.useState)(void 0);
|
|
834
|
+
/** Usage snapshots keyed `${provider}:${accountKey}` — every account tracks its own windows. */
|
|
614
835
|
const [usages, setUsages] = (0, react.useState)({});
|
|
615
836
|
const [usageErrors, setUsageErrors] = (0, react.useState)({});
|
|
616
837
|
const [usageLoading, setUsageLoading] = (0, react.useState)({});
|
|
617
838
|
const mountedRef = (0, react.useRef)(true);
|
|
618
839
|
const pollersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
619
|
-
/**
|
|
840
|
+
/** Accounts with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
|
|
620
841
|
const usageInflightRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
621
842
|
/** Proxy config as last answered by `proxyGet`/`proxySet`. */
|
|
622
843
|
const [proxy, setProxy] = (0, react.useState)(void 0);
|
|
@@ -633,6 +854,24 @@ function SubscriptionsSection(props) {
|
|
|
633
854
|
const [proxyTesting, setProxyTesting] = (0, react.useState)(false);
|
|
634
855
|
const [proxyMessage, setProxyMessage] = (0, react.useState)(void 0);
|
|
635
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);
|
|
636
875
|
const setProviderError = (0, react.useCallback)((provider, message) => {
|
|
637
876
|
if (!mountedRef.current) return;
|
|
638
877
|
setErrors((prev) => {
|
|
@@ -662,7 +901,7 @@ function SubscriptionsSection(props) {
|
|
|
662
901
|
setStatuses(response.providers);
|
|
663
902
|
for (const { id } of PROVIDERS) {
|
|
664
903
|
const status = response.providers[id];
|
|
665
|
-
if (status.
|
|
904
|
+
if (status.accounts.length > 0 || !status.busy) {
|
|
666
905
|
stopPolling(id);
|
|
667
906
|
setDeviceCodes((prev) => {
|
|
668
907
|
if (prev[id] === void 0) return prev;
|
|
@@ -694,68 +933,176 @@ function SubscriptionsSection(props) {
|
|
|
694
933
|
pollersRef.current.clear();
|
|
695
934
|
};
|
|
696
935
|
}, [refresh, startPolling]);
|
|
697
|
-
const loadUsage = (0, react.useCallback)(async (provider) => {
|
|
698
|
-
|
|
699
|
-
usageInflightRef.current.
|
|
936
|
+
const loadUsage = (0, react.useCallback)(async (provider, account, force = false) => {
|
|
937
|
+
const key = `${provider}:${account}`;
|
|
938
|
+
if (rpc === void 0 || usageInflightRef.current.has(key)) return;
|
|
939
|
+
usageInflightRef.current.add(key);
|
|
700
940
|
setUsageLoading((prev) => ({
|
|
701
941
|
...prev,
|
|
702
|
-
[
|
|
942
|
+
[key]: true
|
|
703
943
|
}));
|
|
704
944
|
try {
|
|
705
|
-
const usage = await callSubscriptionsAuth(rpc, "usage", {
|
|
945
|
+
const usage = await callSubscriptionsAuth(rpc, "usage", {
|
|
946
|
+
provider,
|
|
947
|
+
account,
|
|
948
|
+
...force ? { force: true } : {}
|
|
949
|
+
});
|
|
706
950
|
if (!mountedRef.current) return;
|
|
707
951
|
setUsages((prev) => ({
|
|
708
952
|
...prev,
|
|
709
|
-
[
|
|
953
|
+
[key]: usage
|
|
710
954
|
}));
|
|
711
955
|
setUsageErrors((prev) => {
|
|
712
956
|
const next = { ...prev };
|
|
713
|
-
delete next[
|
|
957
|
+
delete next[key];
|
|
714
958
|
return next;
|
|
715
959
|
});
|
|
716
960
|
} catch (error) {
|
|
717
961
|
if (mountedRef.current) setUsageErrors((prev) => ({
|
|
718
962
|
...prev,
|
|
719
|
-
[
|
|
963
|
+
[key]: messageOf(error)
|
|
720
964
|
}));
|
|
721
965
|
} finally {
|
|
722
|
-
usageInflightRef.current.delete(
|
|
966
|
+
usageInflightRef.current.delete(key);
|
|
723
967
|
if (mountedRef.current) setUsageLoading((prev) => ({
|
|
724
968
|
...prev,
|
|
725
|
-
[
|
|
969
|
+
[key]: false
|
|
726
970
|
}));
|
|
727
971
|
}
|
|
728
972
|
}, [rpc]);
|
|
729
973
|
(0, react.useEffect)(() => {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
} else if (usages[id] !== void 0 || usageErrors[id] !== void 0) {
|
|
736
|
-
setUsages((prev) => {
|
|
737
|
-
const next = { ...prev };
|
|
738
|
-
delete next[id];
|
|
739
|
-
return next;
|
|
740
|
-
});
|
|
741
|
-
setUsageErrors((prev) => {
|
|
742
|
-
const next = { ...prev };
|
|
743
|
-
delete next[id];
|
|
744
|
-
return next;
|
|
745
|
-
});
|
|
746
|
-
}
|
|
974
|
+
const live = /* @__PURE__ */ new Set();
|
|
975
|
+
for (const { id } of PROVIDERS) for (const account of statuses[id]?.accounts ?? []) {
|
|
976
|
+
const key = `${id}:${account.key}`;
|
|
977
|
+
live.add(key);
|
|
978
|
+
if (usages[key] === void 0 && usageErrors[key] === void 0) loadUsage(id, account.key);
|
|
747
979
|
}
|
|
980
|
+
setUsages((prev) => dropStale(prev, live));
|
|
981
|
+
setUsageErrors((prev) => dropStale(prev, live));
|
|
748
982
|
}, [
|
|
749
983
|
statuses,
|
|
750
984
|
usages,
|
|
751
985
|
usageErrors,
|
|
752
986
|
loadUsage
|
|
753
987
|
]);
|
|
754
|
-
const
|
|
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]);
|
|
1098
|
+
const login = (0, react.useCallback)(async (provider, method) => {
|
|
755
1099
|
if (rpc === void 0) return;
|
|
756
1100
|
setProviderError(provider, void 0);
|
|
757
1101
|
try {
|
|
758
|
-
const response = await callSubscriptionsAuth(rpc, "login", {
|
|
1102
|
+
const response = await callSubscriptionsAuth(rpc, "login", {
|
|
1103
|
+
provider,
|
|
1104
|
+
...method === void 0 ? {} : { method }
|
|
1105
|
+
});
|
|
759
1106
|
if (typeof response.authorizeUrl === "string" && response.authorizeUrl === "") {
|
|
760
1107
|
await refresh();
|
|
761
1108
|
return;
|
|
@@ -765,9 +1112,9 @@ function SubscriptionsSection(props) {
|
|
|
765
1112
|
setStatuses((prev) => ({
|
|
766
1113
|
...prev,
|
|
767
1114
|
[provider]: {
|
|
1115
|
+
accounts: prev[provider]?.accounts ?? [],
|
|
768
1116
|
...prev[provider],
|
|
769
|
-
busy: true
|
|
770
|
-
loggedIn: false
|
|
1117
|
+
busy: true
|
|
771
1118
|
}
|
|
772
1119
|
}));
|
|
773
1120
|
if (typeof response.userCode === "string" && response.userCode.length > 0) setDeviceCodes((prev) => ({
|
|
@@ -827,12 +1174,18 @@ function SubscriptionsSection(props) {
|
|
|
827
1174
|
setProviderError,
|
|
828
1175
|
refresh
|
|
829
1176
|
]);
|
|
830
|
-
const logout = (0, react.useCallback)(async (provider, name) => {
|
|
1177
|
+
const logout = (0, react.useCallback)(async (provider, account, display, name) => {
|
|
831
1178
|
if (rpc === void 0) return;
|
|
832
|
-
if (!window.confirm(t("
|
|
1179
|
+
if (!window.confirm(t("logoutAccountConfirm", {
|
|
1180
|
+
provider: name,
|
|
1181
|
+
account: display
|
|
1182
|
+
}))) return;
|
|
833
1183
|
setProviderError(provider, void 0);
|
|
834
1184
|
try {
|
|
835
|
-
await callSubscriptionsAuth(rpc, "logout", {
|
|
1185
|
+
await callSubscriptionsAuth(rpc, "logout", {
|
|
1186
|
+
provider,
|
|
1187
|
+
account
|
|
1188
|
+
});
|
|
836
1189
|
} catch (error) {
|
|
837
1190
|
setProviderError(provider, messageOf(error));
|
|
838
1191
|
}
|
|
@@ -843,6 +1196,23 @@ function SubscriptionsSection(props) {
|
|
|
843
1196
|
setProviderError,
|
|
844
1197
|
refresh
|
|
845
1198
|
]);
|
|
1199
|
+
const setDefault = (0, react.useCallback)(async (provider, account) => {
|
|
1200
|
+
if (rpc === void 0) return;
|
|
1201
|
+
setProviderError(provider, void 0);
|
|
1202
|
+
try {
|
|
1203
|
+
await callSubscriptionsAuth(rpc, "setDefault", {
|
|
1204
|
+
provider,
|
|
1205
|
+
account
|
|
1206
|
+
});
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
setProviderError(provider, messageOf(error));
|
|
1209
|
+
}
|
|
1210
|
+
await refresh();
|
|
1211
|
+
}, [
|
|
1212
|
+
rpc,
|
|
1213
|
+
setProviderError,
|
|
1214
|
+
refresh
|
|
1215
|
+
]);
|
|
846
1216
|
const copyDeviceCode = (0, react.useCallback)((provider, userCode) => {
|
|
847
1217
|
navigator.clipboard?.writeText(userCode).then(() => {
|
|
848
1218
|
if (!mountedRef.current) return;
|
|
@@ -993,9 +1363,7 @@ function SubscriptionsSection(props) {
|
|
|
993
1363
|
const status = statuses[id];
|
|
994
1364
|
const busy = status?.busy === true;
|
|
995
1365
|
const deviceCode = deviceCodes[id];
|
|
996
|
-
const
|
|
997
|
-
const usageError = usageErrors[id];
|
|
998
|
-
const showUsage = status?.loggedIn === true && usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[id] === true);
|
|
1366
|
+
const accounts = status?.accounts ?? [];
|
|
999
1367
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1000
1368
|
style: styles$4.card,
|
|
1001
1369
|
children: [
|
|
@@ -1021,10 +1389,119 @@ function SubscriptionsSection(props) {
|
|
|
1021
1389
|
style: styles$4.errorLine,
|
|
1022
1390
|
children: errors[id]
|
|
1023
1391
|
}),
|
|
1392
|
+
accounts.map((account) => {
|
|
1393
|
+
const usageKey = `${id}:${account.key}`;
|
|
1394
|
+
const usage = usages[usageKey];
|
|
1395
|
+
const usageError = usageErrors[usageKey];
|
|
1396
|
+
const display = account.account ?? account.key;
|
|
1397
|
+
const showUsage = usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[usageKey] === true);
|
|
1398
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1399
|
+
style: styles$4.accountRow,
|
|
1400
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1401
|
+
style: styles$4.accountHeader,
|
|
1402
|
+
children: [
|
|
1403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1404
|
+
type: "button",
|
|
1405
|
+
style: styles$4.starButton,
|
|
1406
|
+
title: account.isDefault ? t("defaultBadge") : t("setDefault"),
|
|
1407
|
+
onClick: () => {
|
|
1408
|
+
if (!account.isDefault) setDefault(id, account.key);
|
|
1409
|
+
},
|
|
1410
|
+
children: account.isDefault ? "★" : "☆"
|
|
1411
|
+
}),
|
|
1412
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1413
|
+
style: styles$4.accountName,
|
|
1414
|
+
children: display
|
|
1415
|
+
}),
|
|
1416
|
+
account.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1417
|
+
style: styles$4.usagePlan,
|
|
1418
|
+
children: account.plan
|
|
1419
|
+
}),
|
|
1420
|
+
account.expiresAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1421
|
+
style: styles$4.statusLine,
|
|
1422
|
+
children: t("accountExpires", { date: new Date(account.expiresAt).toLocaleString() })
|
|
1423
|
+
}),
|
|
1424
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1425
|
+
type: "button",
|
|
1426
|
+
style: {
|
|
1427
|
+
...styles$4.button,
|
|
1428
|
+
marginLeft: "auto",
|
|
1429
|
+
flexShrink: 0
|
|
1430
|
+
},
|
|
1431
|
+
onClick: () => {
|
|
1432
|
+
logout(id, account.key, display, name);
|
|
1433
|
+
},
|
|
1434
|
+
children: t("logout")
|
|
1435
|
+
})
|
|
1436
|
+
]
|
|
1437
|
+
}), showUsage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1438
|
+
style: styles$4.usage,
|
|
1439
|
+
children: [
|
|
1440
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1441
|
+
style: styles$4.usageHeader,
|
|
1442
|
+
children: [
|
|
1443
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1444
|
+
style: styles$4.usageTitle,
|
|
1445
|
+
children: t("usageTitle")
|
|
1446
|
+
}),
|
|
1447
|
+
usage?.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1448
|
+
style: styles$4.usagePlan,
|
|
1449
|
+
children: t("usagePlan", { plan: usage.plan })
|
|
1450
|
+
}),
|
|
1451
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1452
|
+
type: "button",
|
|
1453
|
+
style: {
|
|
1454
|
+
...styles$4.usageRefresh,
|
|
1455
|
+
...usageLoading[usageKey] === true ? {
|
|
1456
|
+
opacity: .5,
|
|
1457
|
+
cursor: "default"
|
|
1458
|
+
} : {}
|
|
1459
|
+
},
|
|
1460
|
+
disabled: usageLoading[usageKey] === true,
|
|
1461
|
+
onClick: () => {
|
|
1462
|
+
loadUsage(id, account.key, true);
|
|
1463
|
+
},
|
|
1464
|
+
children: t("usageRefresh")
|
|
1465
|
+
})
|
|
1466
|
+
]
|
|
1467
|
+
}),
|
|
1468
|
+
usage === void 0 && usageError === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1469
|
+
style: styles$4.statusLine,
|
|
1470
|
+
children: t("usageLoading")
|
|
1471
|
+
}),
|
|
1472
|
+
usageError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1473
|
+
style: styles$4.errorLine,
|
|
1474
|
+
children: t("usageError", { message: usageError })
|
|
1475
|
+
}),
|
|
1476
|
+
usage?.windows !== void 0 && usage.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1477
|
+
style: styles$4.statusLine,
|
|
1478
|
+
children: t("usageEmpty")
|
|
1479
|
+
}),
|
|
1480
|
+
(usage?.windows ?? []).map((window$1, index) => {
|
|
1481
|
+
const percent = Math.min(100, Math.max(0, window$1.usedPercent));
|
|
1482
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1483
|
+
style: styles$4.usageRow,
|
|
1484
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1485
|
+
style: styles$4.usageMeta,
|
|
1486
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: usageWindowLabel(t, window$1) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [`${String(Math.round(percent))}%`, window$1.resetsAt !== void 0 && ` · ${t("usageResets", { date: new Date(window$1.resetsAt).toLocaleString() })}`] })]
|
|
1487
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1488
|
+
style: styles$4.usageTrack,
|
|
1489
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
1490
|
+
...styles$4.usageFill,
|
|
1491
|
+
width: `${String(percent)}%`,
|
|
1492
|
+
background: usageBarColor(percent)
|
|
1493
|
+
} })
|
|
1494
|
+
})]
|
|
1495
|
+
}, index);
|
|
1496
|
+
})
|
|
1497
|
+
]
|
|
1498
|
+
})]
|
|
1499
|
+
}, account.key);
|
|
1500
|
+
}),
|
|
1024
1501
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1025
1502
|
style: styles$4.actions,
|
|
1026
1503
|
children: [
|
|
1027
|
-
!busy &&
|
|
1504
|
+
!busy && accounts.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1028
1505
|
type: "button",
|
|
1029
1506
|
style: styles$4.button,
|
|
1030
1507
|
onClick: () => {
|
|
@@ -1032,86 +1509,172 @@ function SubscriptionsSection(props) {
|
|
|
1032
1509
|
},
|
|
1033
1510
|
children: t("login")
|
|
1034
1511
|
}),
|
|
1035
|
-
busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1512
|
+
!busy && accounts.length > 0 && id === "claude" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1036
1513
|
type: "button",
|
|
1037
1514
|
style: styles$4.button,
|
|
1038
1515
|
onClick: () => {
|
|
1039
|
-
|
|
1516
|
+
login(id, "oauth");
|
|
1040
1517
|
},
|
|
1041
|
-
children: t("
|
|
1518
|
+
children: t("addAccountOAuth")
|
|
1519
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1520
|
+
type: "button",
|
|
1521
|
+
style: styles$4.button,
|
|
1522
|
+
onClick: () => {
|
|
1523
|
+
login(id, "keychain");
|
|
1524
|
+
},
|
|
1525
|
+
children: t("addAccountKeychain")
|
|
1526
|
+
})] }),
|
|
1527
|
+
!busy && accounts.length > 0 && id !== "claude" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1528
|
+
type: "button",
|
|
1529
|
+
style: styles$4.button,
|
|
1530
|
+
onClick: () => {
|
|
1531
|
+
login(id);
|
|
1532
|
+
},
|
|
1533
|
+
children: t("addAccount")
|
|
1042
1534
|
}),
|
|
1043
|
-
|
|
1535
|
+
busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1044
1536
|
type: "button",
|
|
1045
1537
|
style: styles$4.button,
|
|
1046
1538
|
onClick: () => {
|
|
1047
|
-
|
|
1539
|
+
cancel(id);
|
|
1048
1540
|
},
|
|
1049
|
-
children: t("
|
|
1541
|
+
children: t("cancel")
|
|
1050
1542
|
})
|
|
1051
1543
|
]
|
|
1052
1544
|
}),
|
|
1053
|
-
|
|
1054
|
-
style: styles$4.
|
|
1055
|
-
children:
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1545
|
+
!busy && accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1546
|
+
style: styles$4.statusLine,
|
|
1547
|
+
children: t("addAccountHint")
|
|
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")
|
|
1062
1596
|
}),
|
|
1063
|
-
|
|
1064
|
-
style: styles$4.
|
|
1065
|
-
children: t("
|
|
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")
|
|
1066
1614
|
}),
|
|
1067
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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 })
|
|
1081
1673
|
})
|
|
1082
|
-
]
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
children: t("usageLoading")
|
|
1087
|
-
}),
|
|
1088
|
-
usageError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1089
|
-
style: styles$4.errorLine,
|
|
1090
|
-
children: t("usageError", { message: usageError })
|
|
1091
|
-
}),
|
|
1092
|
-
usage?.windows !== void 0 && usage.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1093
|
-
style: styles$4.statusLine,
|
|
1094
|
-
children: t("usageEmpty")
|
|
1095
|
-
}),
|
|
1096
|
-
(usage?.windows ?? []).map((window$1, index) => {
|
|
1097
|
-
const percent = Math.min(100, Math.max(0, window$1.usedPercent));
|
|
1098
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1099
|
-
style: styles$4.usageRow,
|
|
1100
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1101
|
-
style: styles$4.usageMeta,
|
|
1102
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: usageWindowLabel(t, window$1) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [`${String(Math.round(percent))}%`, window$1.resetsAt !== void 0 && ` · ${t("usageResets", { date: new Date(window$1.resetsAt).toLocaleString() })}`] })]
|
|
1103
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1104
|
-
style: styles$4.usageTrack,
|
|
1105
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
|
|
1106
|
-
...styles$4.usageFill,
|
|
1107
|
-
width: `${String(percent)}%`,
|
|
1108
|
-
background: usageBarColor(percent)
|
|
1109
|
-
} })
|
|
1110
|
-
})]
|
|
1111
|
-
}, index);
|
|
1112
|
-
})
|
|
1113
|
-
]
|
|
1114
|
-
}),
|
|
1674
|
+
] })
|
|
1675
|
+
]
|
|
1676
|
+
});
|
|
1677
|
+
})(),
|
|
1115
1678
|
busy && deviceCode !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1116
1679
|
style: styles$4.deviceCode,
|
|
1117
1680
|
children: [
|
|
@@ -1969,14 +2532,21 @@ function VideoGenerateToolview(props) {
|
|
|
1969
2532
|
* known state, so a transient failure never locks the toggle away.
|
|
1970
2533
|
*
|
|
1971
2534
|
* `sessionId` is a plain string: slot and command contexts brand it through
|
|
1972
|
-
* 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.
|
|
1973
2540
|
*/
|
|
1974
|
-
function createSpeedLoader(connection, sessionId) {
|
|
2541
|
+
function createSpeedLoader(connection, models, sessionId) {
|
|
1975
2542
|
return async () => {
|
|
1976
2543
|
const state = await callSubscriptionsAuth(connection.rpc, "speed", { sessionId });
|
|
1977
|
-
const
|
|
1978
|
-
if (
|
|
1979
|
-
|
|
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();
|
|
1980
2550
|
return {
|
|
1981
2551
|
visible: current !== null && current.provider === "codex" && state.fastModels.includes(current.model),
|
|
1982
2552
|
tier: state.tier
|
|
@@ -2256,13 +2826,14 @@ function apply(ctx) {
|
|
|
2256
2826
|
locale: NS,
|
|
2257
2827
|
inject: videoToolviewInjected
|
|
2258
2828
|
}, VideoGenerateToolview));
|
|
2829
|
+
const models = () => ctx.get("modelDirectories");
|
|
2259
2830
|
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
2260
2831
|
name: "conversation.input.right",
|
|
2261
2832
|
id: "codex-speed",
|
|
2262
2833
|
order: 0,
|
|
2263
2834
|
locale: NS,
|
|
2264
2835
|
inject: (sessionId) => ({
|
|
2265
|
-
loadSpeed: createSpeedLoader(connection, sessionId),
|
|
2836
|
+
loadSpeed: createSpeedLoader(connection, models, sessionId),
|
|
2266
2837
|
setSpeed: createSpeedSetter(connection, sessionId)
|
|
2267
2838
|
})
|
|
2268
2839
|
}, SpeedSelect));
|
|
@@ -2275,7 +2846,7 @@ function apply(ctx) {
|
|
|
2275
2846
|
ui: {
|
|
2276
2847
|
kind: "popupSelect",
|
|
2277
2848
|
options: async (session) => {
|
|
2278
|
-
const state = await createSpeedLoader(connection, session.sessionId)();
|
|
2849
|
+
const state = await createSpeedLoader(connection, models, session.sessionId)();
|
|
2279
2850
|
if (!state.visible) throw new Error(t("commandFastUnavailable"));
|
|
2280
2851
|
return [{
|
|
2281
2852
|
id: "standard",
|