dsh-plugin-subscriptions 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +42 -1
  2. package/README.zh.md +42 -1
  3. package/lib/auth/device-flow.d.ts +0 -9
  4. package/lib/auth/device-flow.js +2 -1
  5. package/lib/auth/rpc.d.ts +44 -13
  6. package/lib/auth/rpc.js +127 -9
  7. package/lib/auth/store.d.ts +75 -17
  8. package/lib/auth/store.js +148 -27
  9. package/lib/client/SubscriptionsSection.d.ts +26 -3
  10. package/lib/client/SubscriptionsSection.js +263 -67
  11. package/lib/client/index.js +11 -0
  12. package/lib/client/locales.d.ts +82 -10
  13. package/lib/client/locales.js +82 -10
  14. package/lib/client.js +837 -223
  15. package/lib/client.js.map +1 -1
  16. package/lib/http.d.ts +114 -0
  17. package/lib/http.js +402 -0
  18. package/lib/index.d.ts +21 -0
  19. package/lib/index.js +1938 -208
  20. package/lib/providers/accounts.d.ts +102 -0
  21. package/lib/providers/accounts.js +123 -0
  22. package/lib/providers/antigravity.d.ts +90 -0
  23. package/lib/providers/antigravity.js +392 -0
  24. package/lib/providers/claude.d.ts +22 -4
  25. package/lib/providers/claude.js +97 -16
  26. package/lib/providers/codex.d.ts +24 -3
  27. package/lib/providers/codex.js +121 -21
  28. package/lib/providers/common.d.ts +17 -0
  29. package/lib/providers/common.js +67 -3
  30. package/lib/providers/copilot.d.ts +23 -4
  31. package/lib/providers/copilot.js +99 -19
  32. package/lib/providers/grok.d.ts +24 -4
  33. package/lib/providers/grok.js +106 -19
  34. package/lib/providers/pool-family.d.ts +56 -0
  35. package/lib/providers/pool-family.js +45 -0
  36. package/lib/providers/pool-health.d.ts +74 -0
  37. package/lib/providers/pool-health.js +148 -0
  38. package/lib/providers/pool-usage.d.ts +57 -0
  39. package/lib/providers/pool-usage.js +130 -0
  40. package/lib/providers/pool.d.ts +107 -0
  41. package/lib/providers/pool.js +371 -0
  42. package/lib/tools/image-generate.d.ts +3 -3
  43. package/lib/tools/image-generate.js +4 -2
  44. package/lib/tools/video-generate.d.ts +2 -2
  45. package/lib/tools/video-generate.js +4 -2
  46. package/lib/tools/x-search.d.ts +2 -2
  47. package/lib/tools/x-search.js +4 -2
  48. package/lib/translate/antigravity.d.ts +110 -0
  49. package/lib/translate/antigravity.js +303 -0
  50. package/package.json +14 -9
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
- logoutConfirm: "Log out of {provider}?",
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",
@@ -83,7 +87,39 @@ const en = {
83
87
  speedFast: "Fast",
84
88
  speedFastDescription: "1.5x speed, more usage",
85
89
  commandFast: "Switch the Codex speed tier (Standard/Fast)",
86
- commandFastUnavailable: "The current model has no fast tier; /fast only works on Codex models whose catalog advertises one"
90
+ commandFastUnavailable: "The current model has no fast tier; /fast only works on Codex models whose catalog advertises one",
91
+ proxyTitle: "Proxy",
92
+ proxyStatusNone: "Not configured — subscription requests go direct.",
93
+ proxyStatusEnabled: "Enabled · {url}",
94
+ proxyStatusError: "Config error: {message}",
95
+ proxyConfigure: "Configure…",
96
+ proxyDialogTitle: "Proxy settings",
97
+ proxyDialogClose: "Close",
98
+ proxyEnabled: "Route subscription requests through a proxy",
99
+ proxyUrl: "Proxy URL",
100
+ proxyUrlPlaceholder: "http://localhost:7890",
101
+ proxyUrlHint: "HTTP or HTTPS proxy only (Clash/mihomo, v2rayN…); socks is not supported.",
102
+ proxyUsername: "Username (optional)",
103
+ proxyUsernamePlaceholder: "Proxy username",
104
+ proxyPassword: "Password",
105
+ proxyPasswordPlaceholder: "Leave blank to keep the saved password",
106
+ proxyClearPassword: "Clear the saved password",
107
+ proxyBypass: "Bypass hosts",
108
+ proxyBypassPlaceholder: "127.0.0.1, localhost, *.example.com",
109
+ proxyBypassHint: "Comma-separated hostnames that keep going direct.",
110
+ proxyTest: "Test",
111
+ proxyTesting: "Testing…",
112
+ proxyTestOk: "OK · HTTP {status} · {ms} ms",
113
+ proxyTestOkDirect: "OK (direct, bypassed) · HTTP {status} · {ms} ms",
114
+ proxyTestFail: "Failed: {message}",
115
+ proxySave: "Save",
116
+ proxyCancel: "Cancel",
117
+ proxySaving: "Saving…",
118
+ proxyLoading: "Loading proxy settings…",
119
+ proxyLoadFailed: "Failed to load proxy settings: {message}",
120
+ proxySaved: "Saved — new requests use the proxy.",
121
+ proxySaveFailed: "Save failed: {message}",
122
+ proxyNote: "Applies to token exchange, model APIs, usage lookups, image/video generation and x_search. The OAuth authorization page opens in your browser and follows the browser/system proxy, not this setting."
87
123
  };
88
124
  /** zh strings, one per {@link en} key. */
89
125
  const zh = {
@@ -92,15 +128,19 @@ const zh = {
92
128
  unavailable: "连接不可用,无法加载订阅状态。",
93
129
  checking: "查询中…",
94
130
  loginInProgress: "登录中…",
95
- loggedIn: "已登录",
96
- loggedInAccount: "已登录:{account}",
97
- loggedInExpires: "已登录 · 过期时间 {date}",
98
- loggedInAccountExpires: "已登录:{account} · 过期时间 {date}",
99
131
  notLoggedIn: "未登录",
132
+ loggedInCount: "已连接 {count} 个账号",
133
+ accountExpires: "过期时间 {date}",
134
+ defaultBadge: "默认",
135
+ setDefault: "设为默认",
136
+ addAccount: "添加账号",
137
+ addAccountOAuth: "浏览器授权",
138
+ addAccountKeychain: "导入 Claude Code",
139
+ addAccountHint: "浏览器授权以浏览器当前登录的账号为准;要添加不同账号,请先在浏览器里切换账号,或用无痕窗口走下方手动授权码。",
100
140
  login: "登录",
101
141
  cancel: "取消",
102
142
  logout: "退出登录",
103
- logoutConfirm: "确定退出 {provider} 的登录吗?",
143
+ logoutAccountConfirm: "确定退出 {provider} 的账号 {account} 吗?",
104
144
  manualSummary: "浏览器流程无法完成?粘贴回调 URL 或授权码",
105
145
  manualPlaceholder: "粘贴回调 URL 或授权码",
106
146
  submit: "提交",
@@ -136,7 +176,39 @@ const zh = {
136
176
  speedFast: "快速",
137
177
  speedFastDescription: "约 1.5 倍速度,消耗更多用量",
138
178
  commandFast: "切换 Codex 速度档(标准/快速)",
139
- commandFastUnavailable: "当前模型不支持快速档;/fast 仅对目录声明了 fast tier 的 Codex 模型可用"
179
+ commandFastUnavailable: "当前模型不支持快速档;/fast 仅对目录声明了 fast tier 的 Codex 模型可用",
180
+ proxyTitle: "代理",
181
+ proxyStatusNone: "未配置 —— 订阅请求直连。",
182
+ proxyStatusEnabled: "已启用 · {url}",
183
+ proxyStatusError: "配置错误:{message}",
184
+ proxyConfigure: "配置…",
185
+ proxyDialogTitle: "代理设置",
186
+ proxyDialogClose: "关闭",
187
+ proxyEnabled: "让订阅相关请求走代理",
188
+ proxyUrl: "代理地址",
189
+ proxyUrlPlaceholder: "http://localhost:7890",
190
+ proxyUrlHint: "仅支持 HTTP/HTTPS 代理(Clash/mihomo、v2rayN 等);不支持 socks。",
191
+ proxyUsername: "用户名(可选)",
192
+ proxyUsernamePlaceholder: "代理用户名",
193
+ proxyPassword: "密码",
194
+ proxyPasswordPlaceholder: "留空则保留已保存的密码",
195
+ proxyClearPassword: "清除已保存的密码",
196
+ proxyBypass: "绕过主机",
197
+ proxyBypassPlaceholder: "127.0.0.1, localhost, *.example.com",
198
+ proxyBypassHint: "逗号分隔的主机名,这些主机保持直连。",
199
+ proxyTest: "测试",
200
+ proxyTesting: "测试中…",
201
+ proxyTestOk: "成功 · HTTP {status} · {ms} ms",
202
+ proxyTestOkDirect: "成功(直连,已绕过)· HTTP {status} · {ms} ms",
203
+ proxyTestFail: "失败:{message}",
204
+ proxySave: "保存",
205
+ proxyCancel: "取消",
206
+ proxySaving: "保存中…",
207
+ proxyLoading: "代理设置加载中…",
208
+ proxyLoadFailed: "代理设置加载失败:{message}",
209
+ proxySaved: "已保存 —— 后续请求将走代理。",
210
+ proxySaveFailed: "保存失败:{message}",
211
+ proxyNote: "作用于 token 交换、模型 API、用量查询、图片/视频生成与 x_search。OAuth 授权页在浏览器中打开,走的是浏览器/系统代理,不受此设置影响。"
140
212
  };
141
213
 
142
214
  //#endregion
@@ -188,6 +260,14 @@ async function callSubscriptionsAuth(rpc, endpoint, payload) {
188
260
  function messageOf(error) {
189
261
  return error instanceof Error ? error.message : String(error);
190
262
  }
263
+ /** Copy a keyed map without the entries whose key is not in `live`. */
264
+ function dropStale(map, live) {
265
+ const stale = Object.keys(map).filter((key) => !live.has(key));
266
+ if (stale.length === 0) return map;
267
+ const next = { ...map };
268
+ for (const key of stale) delete next[key];
269
+ return next;
270
+ }
191
271
  /**
192
272
  * English-dictionary fallback for a missing inject `t` (standalone renders);
193
273
  * the slot inject always supplies the locale-bound one.
@@ -222,6 +302,13 @@ const styles$4 = {
222
302
  flexDirection: "column",
223
303
  gap: 6
224
304
  },
305
+ proxyCard: {
306
+ padding: "12px 14px",
307
+ display: "flex",
308
+ flexDirection: "column",
309
+ gap: 6
310
+ },
311
+ separator: { borderTop: "1px solid var(--dsw-alias-border-l2)" },
225
312
  cardHeader: {
226
313
  display: "flex",
227
314
  alignItems: "center",
@@ -328,6 +415,36 @@ const styles$4 = {
328
415
  lineHeight: "18px",
329
416
  color: "var(--dsw-alias-label-tertiary)"
330
417
  },
418
+ accountRow: {
419
+ display: "flex",
420
+ flexDirection: "column",
421
+ gap: 6,
422
+ border: "1px solid var(--dsw-alias-border-l2)",
423
+ borderRadius: 8,
424
+ padding: "8px 10px",
425
+ marginTop: 4
426
+ },
427
+ accountHeader: {
428
+ display: "flex",
429
+ alignItems: "center",
430
+ gap: 8
431
+ },
432
+ accountName: {
433
+ fontSize: 13,
434
+ lineHeight: "20px",
435
+ color: "var(--dsw-alias-label-primary)",
436
+ userSelect: "all"
437
+ },
438
+ starButton: {
439
+ border: "none",
440
+ background: "transparent",
441
+ padding: 0,
442
+ font: "inherit",
443
+ fontSize: 14,
444
+ lineHeight: "20px",
445
+ cursor: "pointer",
446
+ color: "var(--dsw-alias-state-warn-label)"
447
+ },
331
448
  usageTrack: {
332
449
  height: 6,
333
450
  borderRadius: 3,
@@ -380,12 +497,97 @@ const styles$4 = {
380
497
  letterSpacing: 2,
381
498
  color: "var(--dsw-alias-label-primary)",
382
499
  userSelect: "all"
500
+ },
501
+ proxyField: {
502
+ display: "flex",
503
+ flexDirection: "column",
504
+ gap: 4
505
+ },
506
+ proxyLabel: {
507
+ fontSize: 12,
508
+ lineHeight: "18px",
509
+ color: "var(--dsw-alias-label-secondary)"
510
+ },
511
+ proxyInput: {
512
+ height: 32,
513
+ width: "100%",
514
+ boxSizing: "border-box",
515
+ border: "1px solid var(--dsw-alias-border-l2)",
516
+ borderRadius: 8,
517
+ padding: "0 10px",
518
+ font: "inherit",
519
+ fontSize: 14,
520
+ lineHeight: "22px",
521
+ background: "var(--dsw-alias-bg-layer-1)",
522
+ color: "var(--dsw-alias-label-primary)"
523
+ },
524
+ proxyHint: {
525
+ margin: 0,
526
+ fontSize: 12,
527
+ lineHeight: "18px",
528
+ color: "var(--dsw-alias-label-tertiary)"
529
+ },
530
+ proxyCheck: {
531
+ display: "flex",
532
+ alignItems: "center",
533
+ gap: 8,
534
+ fontSize: 13,
535
+ lineHeight: "20px",
536
+ color: "var(--dsw-alias-label-primary)",
537
+ cursor: "pointer"
538
+ },
539
+ proxyMessage: {
540
+ margin: 0,
541
+ fontSize: 12,
542
+ lineHeight: "18px"
543
+ },
544
+ proxyActions: {
545
+ display: "flex",
546
+ gap: 8,
547
+ alignItems: "center",
548
+ justifyContent: "flex-end",
549
+ marginTop: 2
550
+ },
551
+ modalOverlay: {
552
+ position: "fixed",
553
+ inset: 0,
554
+ zIndex: 1e3,
555
+ display: "flex",
556
+ alignItems: "center",
557
+ justifyContent: "center",
558
+ padding: 16,
559
+ background: "rgba(0, 0, 0, 0.45)"
560
+ },
561
+ modal: {
562
+ width: 460,
563
+ maxWidth: "100%",
564
+ maxHeight: "90vh",
565
+ overflowY: "auto",
566
+ boxSizing: "border-box",
567
+ display: "flex",
568
+ flexDirection: "column",
569
+ gap: 12,
570
+ padding: "16px 18px",
571
+ borderRadius: 12,
572
+ background: "var(--dsw-alias-bg-layer-1)",
573
+ border: "1px solid var(--dsw-alias-border-l2)"
574
+ },
575
+ modalHeader: {
576
+ display: "flex",
577
+ alignItems: "center",
578
+ gap: 8
579
+ },
580
+ modalTitle: {
581
+ fontWeight: 600,
582
+ fontSize: 15,
583
+ lineHeight: "22px",
584
+ color: "var(--dsw-alias-label-primary)"
383
585
  }
384
586
  };
385
587
  /** Status dot color for one provider state. */
386
588
  function dotColor(status) {
387
589
  if (status?.busy === true) return "var(--dsw-alias-state-warn-label)";
388
- if (status?.loggedIn === true) return "var(--dsw-alias-state-success-primary)";
590
+ if ((status?.accounts.length ?? 0) > 0) return "var(--dsw-alias-state-success-primary)";
389
591
  return "var(--dsw-alias-label-dimmed)";
390
592
  }
391
593
  /**
@@ -397,15 +599,7 @@ function dotColor(status) {
397
599
  function statusText(t, status) {
398
600
  if (status === void 0) return t("checking");
399
601
  if (status.busy) return t("loginInProgress");
400
- if (status.loggedIn) {
401
- const params = {};
402
- if (status.account !== void 0) params.account = status.account;
403
- if (status.expiresAt !== void 0) params.date = new Date(status.expiresAt).toLocaleString();
404
- if (params.account !== void 0 && params.date !== void 0) return t("loggedInAccountExpires", params);
405
- if (params.account !== void 0) return t("loggedInAccount", params);
406
- if (params.date !== void 0) return t("loggedInExpires", params);
407
- return t("loggedIn");
408
- }
602
+ if (status.accounts.length > 0) return t("loggedInCount", { count: status.accounts.length });
409
603
  return t("notLoggedIn");
410
604
  }
411
605
  /**
@@ -424,6 +618,18 @@ function usageBarColor(usedPercent) {
424
618
  if (usedPercent >= 80) return "var(--dsw-alias-state-warn-label)";
425
619
  return "var(--dsw-alias-state-success-primary)";
426
620
  }
621
+ /** One-line status text of the proxy config card. */
622
+ function proxyStatusText(t, proxy, loadError) {
623
+ if (loadError !== void 0) return t("proxyLoadFailed", { message: loadError });
624
+ if (proxy === void 0) return t("proxyLoading");
625
+ if (proxy.error !== void 0) return t("proxyStatusError", { message: proxy.error });
626
+ if (proxy.enabled) return t("proxyStatusEnabled", { url: proxy.url });
627
+ return t("proxyStatusNone");
628
+ }
629
+ /** Feedback-line color of the proxy dialog. */
630
+ function messageColor(tone) {
631
+ return tone === "error" ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-state-success-primary)";
632
+ }
427
633
  /**
428
634
  * The Subscriptions settings page component.
429
635
  * @param props - the slot inject face ({@link SubscriptionsSectionInjected}).
@@ -443,13 +649,29 @@ function SubscriptionsSection(props) {
443
649
  /** Pending device-flow codes (copilot), shown while the attempt polls. */
444
650
  const [deviceCodes, setDeviceCodes] = (0, react.useState)({});
445
651
  const [copiedCode, setCopiedCode] = (0, react.useState)(void 0);
652
+ /** Usage snapshots keyed `${provider}:${accountKey}` — every account tracks its own windows. */
446
653
  const [usages, setUsages] = (0, react.useState)({});
447
654
  const [usageErrors, setUsageErrors] = (0, react.useState)({});
448
655
  const [usageLoading, setUsageLoading] = (0, react.useState)({});
449
656
  const mountedRef = (0, react.useRef)(true);
450
657
  const pollersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
451
- /** Providers with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
658
+ /** Accounts with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
452
659
  const usageInflightRef = (0, react.useRef)(/* @__PURE__ */ new Set());
660
+ /** Proxy config as last answered by `proxyGet`/`proxySet`. */
661
+ const [proxy, setProxy] = (0, react.useState)(void 0);
662
+ const [proxyLoadError, setProxyLoadError] = (0, react.useState)(void 0);
663
+ /** Proxy dialog state (draft fields; the password never pre-fills). */
664
+ const [proxyOpen, setProxyOpen] = (0, react.useState)(false);
665
+ const [proxyEnabled, setProxyEnabled] = (0, react.useState)(false);
666
+ const [proxyUrl, setProxyUrl] = (0, react.useState)("");
667
+ const [proxyUsername, setProxyUsername] = (0, react.useState)("");
668
+ const [proxyPassword, setProxyPassword] = (0, react.useState)("");
669
+ const [proxyClearPassword, setProxyClearPassword] = (0, react.useState)(false);
670
+ const [proxyBypass, setProxyBypass] = (0, react.useState)("");
671
+ const [proxySaving, setProxySaving] = (0, react.useState)(false);
672
+ const [proxyTesting, setProxyTesting] = (0, react.useState)(false);
673
+ const [proxyMessage, setProxyMessage] = (0, react.useState)(void 0);
674
+ const [proxyTestResult, setProxyTestResult] = (0, react.useState)(void 0);
453
675
  const setProviderError = (0, react.useCallback)((provider, message) => {
454
676
  if (!mountedRef.current) return;
455
677
  setErrors((prev) => {
@@ -479,7 +701,7 @@ function SubscriptionsSection(props) {
479
701
  setStatuses(response.providers);
480
702
  for (const { id } of PROVIDERS) {
481
703
  const status = response.providers[id];
482
- if (status.loggedIn || !status.busy) {
704
+ if (status.accounts.length > 0 || !status.busy) {
483
705
  stopPolling(id);
484
706
  setDeviceCodes((prev) => {
485
707
  if (prev[id] === void 0) return prev;
@@ -511,68 +733,65 @@ function SubscriptionsSection(props) {
511
733
  pollersRef.current.clear();
512
734
  };
513
735
  }, [refresh, startPolling]);
514
- const loadUsage = (0, react.useCallback)(async (provider) => {
515
- if (rpc === void 0 || usageInflightRef.current.has(provider)) return;
516
- usageInflightRef.current.add(provider);
736
+ const loadUsage = (0, react.useCallback)(async (provider, account) => {
737
+ const key = `${provider}:${account}`;
738
+ if (rpc === void 0 || usageInflightRef.current.has(key)) return;
739
+ usageInflightRef.current.add(key);
517
740
  setUsageLoading((prev) => ({
518
741
  ...prev,
519
- [provider]: true
742
+ [key]: true
520
743
  }));
521
744
  try {
522
- const usage = await callSubscriptionsAuth(rpc, "usage", { provider });
745
+ const usage = await callSubscriptionsAuth(rpc, "usage", {
746
+ provider,
747
+ account
748
+ });
523
749
  if (!mountedRef.current) return;
524
750
  setUsages((prev) => ({
525
751
  ...prev,
526
- [provider]: usage
752
+ [key]: usage
527
753
  }));
528
754
  setUsageErrors((prev) => {
529
755
  const next = { ...prev };
530
- delete next[provider];
756
+ delete next[key];
531
757
  return next;
532
758
  });
533
759
  } catch (error) {
534
760
  if (mountedRef.current) setUsageErrors((prev) => ({
535
761
  ...prev,
536
- [provider]: messageOf(error)
762
+ [key]: messageOf(error)
537
763
  }));
538
764
  } finally {
539
- usageInflightRef.current.delete(provider);
765
+ usageInflightRef.current.delete(key);
540
766
  if (mountedRef.current) setUsageLoading((prev) => ({
541
767
  ...prev,
542
- [provider]: false
768
+ [key]: false
543
769
  }));
544
770
  }
545
771
  }, [rpc]);
546
772
  (0, react.useEffect)(() => {
547
- for (const { id } of PROVIDERS) {
548
- const status = statuses[id];
549
- if (status === void 0) continue;
550
- if (status.loggedIn) {
551
- if (usages[id] === void 0 && usageErrors[id] === void 0) loadUsage(id);
552
- } else if (usages[id] !== void 0 || usageErrors[id] !== void 0) {
553
- setUsages((prev) => {
554
- const next = { ...prev };
555
- delete next[id];
556
- return next;
557
- });
558
- setUsageErrors((prev) => {
559
- const next = { ...prev };
560
- delete next[id];
561
- return next;
562
- });
563
- }
773
+ const live = /* @__PURE__ */ new Set();
774
+ for (const { id } of PROVIDERS) for (const account of statuses[id]?.accounts ?? []) {
775
+ const key = `${id}:${account.key}`;
776
+ live.add(key);
777
+ if (usages[key] === void 0 && usageErrors[key] === void 0) loadUsage(id, account.key);
564
778
  }
779
+ setUsages((prev) => dropStale(prev, live));
780
+ setUsageErrors((prev) => dropStale(prev, live));
565
781
  }, [
566
782
  statuses,
567
783
  usages,
568
784
  usageErrors,
569
785
  loadUsage
570
786
  ]);
571
- const login = (0, react.useCallback)(async (provider) => {
787
+ const login = (0, react.useCallback)(async (provider, method) => {
572
788
  if (rpc === void 0) return;
573
789
  setProviderError(provider, void 0);
574
790
  try {
575
- const response = await callSubscriptionsAuth(rpc, "login", { provider });
791
+ const response = await callSubscriptionsAuth(rpc, "login", {
792
+ provider,
793
+ ...method === void 0 ? {} : { method }
794
+ });
576
795
  if (typeof response.authorizeUrl === "string" && response.authorizeUrl === "") {
577
796
  await refresh();
578
797
  return;
@@ -582,9 +801,9 @@ function SubscriptionsSection(props) {
582
801
  setStatuses((prev) => ({
583
802
  ...prev,
584
803
  [provider]: {
804
+ accounts: prev[provider]?.accounts ?? [],
585
805
  ...prev[provider],
586
- busy: true,
587
- loggedIn: false
806
+ busy: true
588
807
  }
589
808
  }));
590
809
  if (typeof response.userCode === "string" && response.userCode.length > 0) setDeviceCodes((prev) => ({
@@ -644,12 +863,18 @@ function SubscriptionsSection(props) {
644
863
  setProviderError,
645
864
  refresh
646
865
  ]);
647
- const logout = (0, react.useCallback)(async (provider, name) => {
866
+ const logout = (0, react.useCallback)(async (provider, account, display, name) => {
648
867
  if (rpc === void 0) return;
649
- if (!window.confirm(t("logoutConfirm", { provider: name }))) return;
868
+ if (!window.confirm(t("logoutAccountConfirm", {
869
+ provider: name,
870
+ account: display
871
+ }))) return;
650
872
  setProviderError(provider, void 0);
651
873
  try {
652
- await callSubscriptionsAuth(rpc, "logout", { provider });
874
+ await callSubscriptionsAuth(rpc, "logout", {
875
+ provider,
876
+ account
877
+ });
653
878
  } catch (error) {
654
879
  setProviderError(provider, messageOf(error));
655
880
  }
@@ -660,6 +885,23 @@ function SubscriptionsSection(props) {
660
885
  setProviderError,
661
886
  refresh
662
887
  ]);
888
+ const setDefault = (0, react.useCallback)(async (provider, account) => {
889
+ if (rpc === void 0) return;
890
+ setProviderError(provider, void 0);
891
+ try {
892
+ await callSubscriptionsAuth(rpc, "setDefault", {
893
+ provider,
894
+ account
895
+ });
896
+ } catch (error) {
897
+ setProviderError(provider, messageOf(error));
898
+ }
899
+ await refresh();
900
+ }, [
901
+ rpc,
902
+ setProviderError,
903
+ refresh
904
+ ]);
663
905
  const copyDeviceCode = (0, react.useCallback)((provider, userCode) => {
664
906
  navigator.clipboard?.writeText(userCode).then(() => {
665
907
  if (!mountedRef.current) return;
@@ -669,194 +911,559 @@ function SubscriptionsSection(props) {
669
911
  }, 1500);
670
912
  }).catch(() => void 0);
671
913
  }, []);
914
+ (0, react.useEffect)(() => {
915
+ if (rpc === void 0) return;
916
+ let alive = true;
917
+ callSubscriptionsAuth(rpc, "proxyGet", {}).then((view) => {
918
+ if (!alive) return;
919
+ setProxy(view);
920
+ setProxyLoadError(void 0);
921
+ }).catch((error) => {
922
+ if (alive) setProxyLoadError(messageOf(error));
923
+ });
924
+ return () => {
925
+ alive = false;
926
+ };
927
+ }, [rpc]);
928
+ (0, react.useEffect)(() => {
929
+ if (!proxyOpen) return;
930
+ const onKey = (event) => {
931
+ if (event.key === "Escape") setProxyOpen(false);
932
+ };
933
+ window.addEventListener("keydown", onKey);
934
+ return () => window.removeEventListener("keydown", onKey);
935
+ }, [proxyOpen]);
936
+ const openProxyDialog = (0, react.useCallback)(() => {
937
+ if (proxy === void 0) return;
938
+ setProxyEnabled(proxy.enabled);
939
+ setProxyUrl(proxy.url);
940
+ setProxyUsername(proxy.username ?? "");
941
+ setProxyPassword("");
942
+ setProxyClearPassword(false);
943
+ setProxyBypass(proxy.bypass.join(", "));
944
+ setProxyMessage(void 0);
945
+ setProxyTestResult(void 0);
946
+ setProxyOpen(true);
947
+ }, [proxy]);
948
+ const saveProxy = (0, react.useCallback)(async () => {
949
+ if (rpc === void 0) return;
950
+ setProxySaving(true);
951
+ setProxyMessage(void 0);
952
+ try {
953
+ setProxy(await callSubscriptionsAuth(rpc, "proxySet", {
954
+ enabled: proxyEnabled,
955
+ url: proxyUrl.trim(),
956
+ username: proxyUsername,
957
+ ...proxyClearPassword ? { password: null } : proxyPassword !== "" ? { password: proxyPassword } : {},
958
+ bypass: proxyBypass.split(/[,\n]/).map((entry) => entry.trim()).filter((entry) => entry !== "")
959
+ }));
960
+ setProxyLoadError(void 0);
961
+ setProxyMessage({
962
+ tone: "success",
963
+ text: t("proxySaved")
964
+ });
965
+ setProxyOpen(false);
966
+ } catch (error) {
967
+ setProxyMessage({
968
+ tone: "error",
969
+ text: t("proxySaveFailed", { message: messageOf(error) })
970
+ });
971
+ } finally {
972
+ setProxySaving(false);
973
+ }
974
+ }, [
975
+ rpc,
976
+ proxyEnabled,
977
+ proxyUrl,
978
+ proxyUsername,
979
+ proxyPassword,
980
+ proxyClearPassword,
981
+ proxyBypass,
982
+ t
983
+ ]);
984
+ const testProxy = (0, react.useCallback)(async () => {
985
+ if (rpc === void 0 || proxyTesting) return;
986
+ setProxyTesting(true);
987
+ setProxyTestResult(void 0);
988
+ try {
989
+ setProxyTestResult(await callSubscriptionsAuth(rpc, "proxyTest", { proxy: {
990
+ url: proxyUrl.trim(),
991
+ ...proxyUsername.trim() !== "" ? { username: proxyUsername.trim() } : {},
992
+ ...proxyPassword !== "" ? { password: proxyPassword } : {}
993
+ } }));
994
+ } catch (error) {
995
+ setProxyTestResult({
996
+ ok: false,
997
+ viaProxy: false,
998
+ error: messageOf(error)
999
+ });
1000
+ } finally {
1001
+ setProxyTesting(false);
1002
+ }
1003
+ }, [
1004
+ rpc,
1005
+ proxyTesting,
1006
+ proxyUrl,
1007
+ proxyUsername,
1008
+ proxyPassword
1009
+ ]);
672
1010
  if (rpc === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
673
1011
  style: styles$4.intro,
674
1012
  children: t("unavailable")
675
1013
  });
676
1014
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
677
1015
  style: styles$4.section,
678
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
679
- style: styles$4.intro,
680
- children: t("intro")
681
- }), PROVIDERS.map(({ id, name }) => {
682
- const status = statuses[id];
683
- const busy = status?.busy === true;
684
- const deviceCode = deviceCodes[id];
685
- const usage = usages[id];
686
- const usageError = usageErrors[id];
687
- const showUsage = status?.loggedIn === true && usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[id] === true);
688
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
689
- style: styles$4.card,
690
- children: [
691
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
692
- style: styles$4.cardHeader,
693
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1016
+ children: [
1017
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1018
+ style: styles$4.intro,
1019
+ children: t("intro")
1020
+ }),
1021
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1022
+ style: styles$4.proxyCard,
1023
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1024
+ style: styles$4.cardHeader,
1025
+ children: [
1026
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
694
1027
  ...styles$4.dot,
695
- background: dotColor(status)
696
- } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1028
+ background: proxy?.enabled === true ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-label-dimmed)"
1029
+ } }),
1030
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
697
1031
  style: styles$4.name,
698
- children: name
699
- })]
700
- }),
701
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
702
- style: styles$4.statusLine,
703
- children: statusText(t, status)
704
- }),
705
- status?.detail !== void 0 && status.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
706
- style: styles$4.statusLine,
707
- children: status.detail
708
- }),
709
- errors[id] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
710
- style: styles$4.errorLine,
711
- children: errors[id]
712
- }),
713
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
714
- style: styles$4.actions,
715
- children: [
716
- !busy && status?.loggedIn !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
717
- type: "button",
718
- style: styles$4.button,
719
- onClick: () => {
720
- login(id);
721
- },
722
- children: t("login")
723
- }),
724
- busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
725
- type: "button",
726
- style: styles$4.button,
727
- onClick: () => {
728
- cancel(id);
729
- },
730
- children: t("cancel")
731
- }),
732
- status?.loggedIn === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
733
- type: "button",
734
- style: styles$4.button,
735
- onClick: () => {
736
- logout(id, name);
737
- },
738
- children: t("logout")
739
- })
740
- ]
741
- }),
742
- showUsage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
743
- style: styles$4.usage,
744
- children: [
745
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
746
- style: styles$4.usageHeader,
747
- children: [
748
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
749
- style: styles$4.usageTitle,
750
- children: t("usageTitle")
751
- }),
752
- usage?.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
753
- style: styles$4.usagePlan,
754
- children: t("usagePlan", { plan: usage.plan })
755
- }),
756
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1032
+ children: t("proxyTitle")
1033
+ }),
1034
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1035
+ type: "button",
1036
+ style: {
1037
+ ...styles$4.button,
1038
+ marginLeft: "auto",
1039
+ flexShrink: 0
1040
+ },
1041
+ onClick: openProxyDialog,
1042
+ children: t("proxyConfigure")
1043
+ })
1044
+ ]
1045
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1046
+ style: styles$4.statusLine,
1047
+ children: proxyStatusText(t, proxy, proxyLoadError)
1048
+ })]
1049
+ }),
1050
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: styles$4.separator }),
1051
+ PROVIDERS.map(({ id, name }) => {
1052
+ const status = statuses[id];
1053
+ const busy = status?.busy === true;
1054
+ const deviceCode = deviceCodes[id];
1055
+ const accounts = status?.accounts ?? [];
1056
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1057
+ style: styles$4.card,
1058
+ children: [
1059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1060
+ style: styles$4.cardHeader,
1061
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1062
+ ...styles$4.dot,
1063
+ background: dotColor(status)
1064
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1065
+ style: styles$4.name,
1066
+ children: name
1067
+ })]
1068
+ }),
1069
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1070
+ style: styles$4.statusLine,
1071
+ children: statusText(t, status)
1072
+ }),
1073
+ status?.detail !== void 0 && status.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1074
+ style: styles$4.statusLine,
1075
+ children: status.detail
1076
+ }),
1077
+ errors[id] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1078
+ style: styles$4.errorLine,
1079
+ children: errors[id]
1080
+ }),
1081
+ accounts.map((account) => {
1082
+ const usageKey = `${id}:${account.key}`;
1083
+ const usage = usages[usageKey];
1084
+ const usageError = usageErrors[usageKey];
1085
+ const display = account.account ?? account.key;
1086
+ const showUsage = usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[usageKey] === true);
1087
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1088
+ style: styles$4.accountRow,
1089
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1090
+ style: styles$4.accountHeader,
1091
+ children: [
1092
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1093
+ type: "button",
1094
+ style: styles$4.starButton,
1095
+ title: account.isDefault ? t("defaultBadge") : t("setDefault"),
1096
+ onClick: () => {
1097
+ if (!account.isDefault) setDefault(id, account.key);
1098
+ },
1099
+ children: account.isDefault ? "★" : "☆"
1100
+ }),
1101
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1102
+ style: styles$4.accountName,
1103
+ children: display
1104
+ }),
1105
+ account.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1106
+ style: styles$4.usagePlan,
1107
+ children: account.plan
1108
+ }),
1109
+ account.expiresAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1110
+ style: styles$4.statusLine,
1111
+ children: t("accountExpires", { date: new Date(account.expiresAt).toLocaleString() })
1112
+ }),
1113
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1114
+ type: "button",
1115
+ style: {
1116
+ ...styles$4.button,
1117
+ marginLeft: "auto",
1118
+ flexShrink: 0
1119
+ },
1120
+ onClick: () => {
1121
+ logout(id, account.key, display, name);
1122
+ },
1123
+ children: t("logout")
1124
+ })
1125
+ ]
1126
+ }), showUsage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1127
+ style: styles$4.usage,
1128
+ children: [
1129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1130
+ style: styles$4.usageHeader,
1131
+ children: [
1132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1133
+ style: styles$4.usageTitle,
1134
+ children: t("usageTitle")
1135
+ }),
1136
+ usage?.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1137
+ style: styles$4.usagePlan,
1138
+ children: t("usagePlan", { plan: usage.plan })
1139
+ }),
1140
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1141
+ type: "button",
1142
+ style: {
1143
+ ...styles$4.usageRefresh,
1144
+ ...usageLoading[usageKey] === true ? {
1145
+ opacity: .5,
1146
+ cursor: "default"
1147
+ } : {}
1148
+ },
1149
+ disabled: usageLoading[usageKey] === true,
1150
+ onClick: () => {
1151
+ loadUsage(id, account.key);
1152
+ },
1153
+ children: t("usageRefresh")
1154
+ })
1155
+ ]
1156
+ }),
1157
+ usage === void 0 && usageError === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1158
+ style: styles$4.statusLine,
1159
+ children: t("usageLoading")
1160
+ }),
1161
+ usageError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1162
+ style: styles$4.errorLine,
1163
+ children: t("usageError", { message: usageError })
1164
+ }),
1165
+ usage?.windows !== void 0 && usage.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1166
+ style: styles$4.statusLine,
1167
+ children: t("usageEmpty")
1168
+ }),
1169
+ (usage?.windows ?? []).map((window$1, index) => {
1170
+ const percent = Math.min(100, Math.max(0, window$1.usedPercent));
1171
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1172
+ style: styles$4.usageRow,
1173
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1174
+ style: styles$4.usageMeta,
1175
+ 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() })}`] })]
1176
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1177
+ style: styles$4.usageTrack,
1178
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1179
+ ...styles$4.usageFill,
1180
+ width: `${String(percent)}%`,
1181
+ background: usageBarColor(percent)
1182
+ } })
1183
+ })]
1184
+ }, index);
1185
+ })
1186
+ ]
1187
+ })]
1188
+ }, account.key);
1189
+ }),
1190
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1191
+ style: styles$4.actions,
1192
+ children: [
1193
+ !busy && accounts.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1194
+ type: "button",
1195
+ style: styles$4.button,
1196
+ onClick: () => {
1197
+ login(id);
1198
+ },
1199
+ children: t("login")
1200
+ }),
1201
+ !busy && accounts.length > 0 && id === "claude" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1202
+ type: "button",
1203
+ style: styles$4.button,
1204
+ onClick: () => {
1205
+ login(id, "oauth");
1206
+ },
1207
+ children: t("addAccountOAuth")
1208
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1209
+ type: "button",
1210
+ style: styles$4.button,
1211
+ onClick: () => {
1212
+ login(id, "keychain");
1213
+ },
1214
+ children: t("addAccountKeychain")
1215
+ })] }),
1216
+ !busy && accounts.length > 0 && id !== "claude" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1217
+ type: "button",
1218
+ style: styles$4.button,
1219
+ onClick: () => {
1220
+ login(id);
1221
+ },
1222
+ children: t("addAccount")
1223
+ }),
1224
+ busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1225
+ type: "button",
1226
+ style: styles$4.button,
1227
+ onClick: () => {
1228
+ cancel(id);
1229
+ },
1230
+ children: t("cancel")
1231
+ })
1232
+ ]
1233
+ }),
1234
+ !busy && accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1235
+ style: styles$4.statusLine,
1236
+ children: t("addAccountHint")
1237
+ }),
1238
+ busy && deviceCode !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1239
+ style: styles$4.deviceCode,
1240
+ children: [
1241
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1242
+ style: styles$4.statusLine,
1243
+ children: t("deviceCodePrompt")
1244
+ }),
1245
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1246
+ style: styles$4.deviceCodeText,
1247
+ children: deviceCode.userCode
1248
+ }),
1249
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1250
+ style: styles$4.actions,
1251
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
757
1252
  type: "button",
758
- style: {
759
- ...styles$4.usageRefresh,
760
- ...usageLoading[id] === true ? {
761
- opacity: .5,
762
- cursor: "default"
763
- } : {}
1253
+ style: styles$4.button,
1254
+ onClick: () => {
1255
+ copyDeviceCode(id, deviceCode.userCode);
764
1256
  },
765
- disabled: usageLoading[id] === true,
1257
+ children: copiedCode === id ? t("deviceCodeCopied") : t("deviceCodeCopy")
1258
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1259
+ type: "button",
1260
+ style: styles$4.button,
766
1261
  onClick: () => {
767
- loadUsage(id);
1262
+ window.open(deviceCode.verificationUrl, "_blank", "noopener");
768
1263
  },
769
- children: t("usageRefresh")
770
- })
771
- ]
772
- }),
773
- usage === void 0 && usageError === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
774
- style: styles$4.statusLine,
775
- children: t("usageLoading")
776
- }),
777
- usageError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
778
- style: styles$4.errorLine,
779
- children: t("usageError", { message: usageError })
780
- }),
781
- usage?.windows !== void 0 && usage.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
782
- style: styles$4.statusLine,
783
- children: t("usageEmpty")
784
- }),
785
- (usage?.windows ?? []).map((window$1, index) => {
786
- const percent = Math.min(100, Math.max(0, window$1.usedPercent));
787
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
788
- style: styles$4.usageRow,
789
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
790
- style: styles$4.usageMeta,
791
- 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() })}`] })]
792
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
793
- style: styles$4.usageTrack,
794
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
795
- ...styles$4.usageFill,
796
- width: `${String(percent)}%`,
797
- background: usageBarColor(percent)
798
- } })
1264
+ children: t("deviceCodeOpenPage")
799
1265
  })]
800
- }, index);
801
- })
802
- ]
803
- }),
804
- busy && deviceCode !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
805
- style: styles$4.deviceCode,
806
- children: [
807
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
808
- style: styles$4.statusLine,
809
- children: t("deviceCodePrompt")
810
- }),
811
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
812
- style: styles$4.deviceCodeText,
813
- children: deviceCode.userCode
814
- }),
815
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
816
- style: styles$4.actions,
817
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
818
- type: "button",
819
- style: styles$4.button,
820
- onClick: () => {
821
- copyDeviceCode(id, deviceCode.userCode);
822
- },
823
- children: copiedCode === id ? t("deviceCodeCopied") : t("deviceCodeCopy")
1266
+ })
1267
+ ]
1268
+ }),
1269
+ busy && deviceCode === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
1270
+ style: styles$4.manual,
1271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("manualSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1272
+ style: styles$4.manualRow,
1273
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1274
+ style: styles$4.manualInput,
1275
+ value: manualDrafts[id],
1276
+ placeholder: t("manualPlaceholder"),
1277
+ onChange: (event) => setManualDrafts((prev) => ({
1278
+ ...prev,
1279
+ [id]: event.target.value
1280
+ }))
824
1281
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
825
1282
  type: "button",
826
1283
  style: styles$4.button,
827
1284
  onClick: () => {
828
- window.open(deviceCode.verificationUrl, "_blank", "noopener");
1285
+ submitManual(id);
829
1286
  },
830
- children: t("deviceCodeOpenPage")
1287
+ children: t("submit")
831
1288
  })]
832
- })
833
- ]
834
- }),
835
- busy && deviceCode === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
836
- style: styles$4.manual,
837
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("manualSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
838
- style: styles$4.manualRow,
839
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
840
- style: styles$4.manualInput,
841
- value: manualDrafts[id],
842
- placeholder: t("manualPlaceholder"),
843
- onChange: (event) => setManualDrafts((prev) => ({
844
- ...prev,
845
- [id]: event.target.value
846
- }))
1289
+ })]
1290
+ })
1291
+ ]
1292
+ }, id);
1293
+ }),
1294
+ proxyOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1295
+ style: styles$4.modalOverlay,
1296
+ onClick: () => setProxyOpen(false),
1297
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1298
+ style: styles$4.modal,
1299
+ onClick: (event) => event.stopPropagation(),
1300
+ children: [
1301
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1302
+ style: styles$4.modalHeader,
1303
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1304
+ style: styles$4.modalTitle,
1305
+ children: t("proxyDialogTitle")
847
1306
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
848
1307
  type: "button",
849
- style: styles$4.button,
850
- onClick: () => {
851
- submitManual(id);
1308
+ style: {
1309
+ ...styles$4.button,
1310
+ marginLeft: "auto"
852
1311
  },
853
- children: t("submit")
1312
+ onClick: () => setProxyOpen(false),
1313
+ children: t("proxyDialogClose")
854
1314
  })]
855
- })]
856
- })
857
- ]
858
- }, id);
859
- })]
1315
+ }),
1316
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1317
+ style: styles$4.proxyCheck,
1318
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1319
+ type: "checkbox",
1320
+ checked: proxyEnabled,
1321
+ onChange: (event) => setProxyEnabled(event.target.checked)
1322
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("proxyEnabled") })]
1323
+ }),
1324
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1325
+ style: styles$4.proxyField,
1326
+ children: [
1327
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1328
+ style: styles$4.proxyLabel,
1329
+ children: t("proxyUrl")
1330
+ }),
1331
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1332
+ style: styles$4.proxyInput,
1333
+ value: proxyUrl,
1334
+ placeholder: t("proxyUrlPlaceholder"),
1335
+ onChange: (event) => setProxyUrl(event.target.value)
1336
+ }),
1337
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1338
+ style: styles$4.proxyHint,
1339
+ children: t("proxyUrlHint")
1340
+ })
1341
+ ]
1342
+ }),
1343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1344
+ style: styles$4.proxyField,
1345
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1346
+ style: styles$4.proxyLabel,
1347
+ children: t("proxyUsername")
1348
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1349
+ style: styles$4.proxyInput,
1350
+ value: proxyUsername,
1351
+ placeholder: t("proxyUsernamePlaceholder"),
1352
+ onChange: (event) => setProxyUsername(event.target.value)
1353
+ })]
1354
+ }),
1355
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1356
+ style: styles$4.proxyField,
1357
+ children: [
1358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1359
+ style: styles$4.proxyLabel,
1360
+ children: t("proxyPassword")
1361
+ }),
1362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1363
+ type: "password",
1364
+ style: styles$4.proxyInput,
1365
+ value: proxyPassword,
1366
+ placeholder: t("proxyPasswordPlaceholder"),
1367
+ onChange: (event) => setProxyPassword(event.target.value)
1368
+ }),
1369
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1370
+ style: styles$4.proxyCheck,
1371
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1372
+ type: "checkbox",
1373
+ checked: proxyClearPassword,
1374
+ onChange: (event) => setProxyClearPassword(event.target.checked)
1375
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("proxyClearPassword") })]
1376
+ })
1377
+ ]
1378
+ }),
1379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1380
+ style: styles$4.proxyField,
1381
+ children: [
1382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1383
+ style: styles$4.proxyLabel,
1384
+ children: t("proxyBypass")
1385
+ }),
1386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1387
+ style: styles$4.proxyInput,
1388
+ value: proxyBypass,
1389
+ placeholder: t("proxyBypassPlaceholder"),
1390
+ onChange: (event) => setProxyBypass(event.target.value)
1391
+ }),
1392
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1393
+ style: styles$4.proxyHint,
1394
+ children: t("proxyBypassHint")
1395
+ })
1396
+ ]
1397
+ }),
1398
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1399
+ style: styles$4.proxyHint,
1400
+ children: t("proxyNote")
1401
+ }),
1402
+ proxyMessage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1403
+ style: {
1404
+ ...styles$4.proxyMessage,
1405
+ color: messageColor(proxyMessage.tone)
1406
+ },
1407
+ children: proxyMessage.text
1408
+ }),
1409
+ proxyTestResult !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1410
+ style: {
1411
+ ...styles$4.proxyMessage,
1412
+ color: proxyTestResult.ok ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-state-error-primary)"
1413
+ },
1414
+ children: proxyTestResult.ok ? proxyTestResult.viaProxy ? t("proxyTestOk", {
1415
+ status: String(proxyTestResult.status),
1416
+ ms: String(proxyTestResult.latencyMs)
1417
+ }) : t("proxyTestOkDirect", {
1418
+ status: String(proxyTestResult.status),
1419
+ ms: String(proxyTestResult.latencyMs)
1420
+ }) : t("proxyTestFail", { message: proxyTestResult.error ?? "" })
1421
+ }),
1422
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1423
+ style: styles$4.proxyActions,
1424
+ children: [
1425
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1426
+ type: "button",
1427
+ style: {
1428
+ ...styles$4.button,
1429
+ ...proxyTesting ? {
1430
+ opacity: .5,
1431
+ cursor: "default"
1432
+ } : {}
1433
+ },
1434
+ disabled: proxyTesting,
1435
+ onClick: () => {
1436
+ testProxy();
1437
+ },
1438
+ children: proxyTesting ? t("proxyTesting") : t("proxyTest")
1439
+ }),
1440
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1441
+ type: "button",
1442
+ style: {
1443
+ ...styles$4.button,
1444
+ ...proxySaving ? {
1445
+ opacity: .5,
1446
+ cursor: "default"
1447
+ } : {}
1448
+ },
1449
+ disabled: proxySaving,
1450
+ onClick: () => {
1451
+ saveProxy();
1452
+ },
1453
+ children: proxySaving ? t("proxySaving") : t("proxySave")
1454
+ }),
1455
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1456
+ type: "button",
1457
+ style: styles$4.button,
1458
+ onClick: () => setProxyOpen(false),
1459
+ children: t("proxyCancel")
1460
+ })
1461
+ ]
1462
+ })
1463
+ ]
1464
+ })
1465
+ })
1466
+ ]
860
1467
  });
861
1468
  }
862
1469
 
@@ -1738,6 +2345,13 @@ function apply(ctx) {
1738
2345
  zh,
1739
2346
  en
1740
2347
  }), "dsh-plugin-subscriptions: copy dictionaries");
2348
+ ctx.effect(() => {
2349
+ const style = document.createElement("style");
2350
+ style.setAttribute("data-plugin", "dsh-plugin-subscriptions");
2351
+ style.textContent = "div[role=\"dialog\"][aria-modal=\"true\"]:has(> nav) { padding-top: 14px; }";
2352
+ document.head.appendChild(style);
2353
+ return () => style.remove();
2354
+ }, "dsh-plugin-subscriptions: settings panel breathing room");
1741
2355
  const connection = ctx.get("connection");
1742
2356
  const t = ctx.locale.bind(NS);
1743
2357
  const injected = () => ({