dsh-plugin-subscriptions 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -83,7 +83,39 @@ const en = {
83
83
  speedFast: "Fast",
84
84
  speedFastDescription: "1.5x speed, more usage",
85
85
  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"
86
+ commandFastUnavailable: "The current model has no fast tier; /fast only works on Codex models whose catalog advertises one",
87
+ proxyTitle: "Proxy",
88
+ proxyStatusNone: "Not configured — subscription requests go direct.",
89
+ proxyStatusEnabled: "Enabled · {url}",
90
+ proxyStatusError: "Config error: {message}",
91
+ proxyConfigure: "Configure…",
92
+ proxyDialogTitle: "Proxy settings",
93
+ proxyDialogClose: "Close",
94
+ proxyEnabled: "Route subscription requests through a proxy",
95
+ proxyUrl: "Proxy URL",
96
+ proxyUrlPlaceholder: "http://localhost:7890",
97
+ proxyUrlHint: "HTTP or HTTPS proxy only (Clash/mihomo, v2rayN…); socks is not supported.",
98
+ proxyUsername: "Username (optional)",
99
+ proxyUsernamePlaceholder: "Proxy username",
100
+ proxyPassword: "Password",
101
+ proxyPasswordPlaceholder: "Leave blank to keep the saved password",
102
+ proxyClearPassword: "Clear the saved password",
103
+ proxyBypass: "Bypass hosts",
104
+ proxyBypassPlaceholder: "127.0.0.1, localhost, *.example.com",
105
+ proxyBypassHint: "Comma-separated hostnames that keep going direct.",
106
+ proxyTest: "Test",
107
+ proxyTesting: "Testing…",
108
+ proxyTestOk: "OK · HTTP {status} · {ms} ms",
109
+ proxyTestOkDirect: "OK (direct, bypassed) · HTTP {status} · {ms} ms",
110
+ proxyTestFail: "Failed: {message}",
111
+ proxySave: "Save",
112
+ proxyCancel: "Cancel",
113
+ proxySaving: "Saving…",
114
+ proxyLoading: "Loading proxy settings…",
115
+ proxyLoadFailed: "Failed to load proxy settings: {message}",
116
+ proxySaved: "Saved — new requests use the proxy.",
117
+ proxySaveFailed: "Save failed: {message}",
118
+ 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
119
  };
88
120
  /** zh strings, one per {@link en} key. */
89
121
  const zh = {
@@ -136,7 +168,39 @@ const zh = {
136
168
  speedFast: "快速",
137
169
  speedFastDescription: "约 1.5 倍速度,消耗更多用量",
138
170
  commandFast: "切换 Codex 速度档(标准/快速)",
139
- commandFastUnavailable: "当前模型不支持快速档;/fast 仅对目录声明了 fast tier 的 Codex 模型可用"
171
+ commandFastUnavailable: "当前模型不支持快速档;/fast 仅对目录声明了 fast tier 的 Codex 模型可用",
172
+ proxyTitle: "代理",
173
+ proxyStatusNone: "未配置 —— 订阅请求直连。",
174
+ proxyStatusEnabled: "已启用 · {url}",
175
+ proxyStatusError: "配置错误:{message}",
176
+ proxyConfigure: "配置…",
177
+ proxyDialogTitle: "代理设置",
178
+ proxyDialogClose: "关闭",
179
+ proxyEnabled: "让订阅相关请求走代理",
180
+ proxyUrl: "代理地址",
181
+ proxyUrlPlaceholder: "http://localhost:7890",
182
+ proxyUrlHint: "仅支持 HTTP/HTTPS 代理(Clash/mihomo、v2rayN 等);不支持 socks。",
183
+ proxyUsername: "用户名(可选)",
184
+ proxyUsernamePlaceholder: "代理用户名",
185
+ proxyPassword: "密码",
186
+ proxyPasswordPlaceholder: "留空则保留已保存的密码",
187
+ proxyClearPassword: "清除已保存的密码",
188
+ proxyBypass: "绕过主机",
189
+ proxyBypassPlaceholder: "127.0.0.1, localhost, *.example.com",
190
+ proxyBypassHint: "逗号分隔的主机名,这些主机保持直连。",
191
+ proxyTest: "测试",
192
+ proxyTesting: "测试中…",
193
+ proxyTestOk: "成功 · HTTP {status} · {ms} ms",
194
+ proxyTestOkDirect: "成功(直连,已绕过)· HTTP {status} · {ms} ms",
195
+ proxyTestFail: "失败:{message}",
196
+ proxySave: "保存",
197
+ proxyCancel: "取消",
198
+ proxySaving: "保存中…",
199
+ proxyLoading: "代理设置加载中…",
200
+ proxyLoadFailed: "代理设置加载失败:{message}",
201
+ proxySaved: "已保存 —— 后续请求将走代理。",
202
+ proxySaveFailed: "保存失败:{message}",
203
+ proxyNote: "作用于 token 交换、模型 API、用量查询、图片/视频生成与 x_search。OAuth 授权页在浏览器中打开,走的是浏览器/系统代理,不受此设置影响。"
140
204
  };
141
205
 
142
206
  //#endregion
@@ -222,6 +286,13 @@ const styles$4 = {
222
286
  flexDirection: "column",
223
287
  gap: 6
224
288
  },
289
+ proxyCard: {
290
+ padding: "12px 14px",
291
+ display: "flex",
292
+ flexDirection: "column",
293
+ gap: 6
294
+ },
295
+ separator: { borderTop: "1px solid var(--dsw-alias-border-l2)" },
225
296
  cardHeader: {
226
297
  display: "flex",
227
298
  alignItems: "center",
@@ -380,6 +451,91 @@ const styles$4 = {
380
451
  letterSpacing: 2,
381
452
  color: "var(--dsw-alias-label-primary)",
382
453
  userSelect: "all"
454
+ },
455
+ proxyField: {
456
+ display: "flex",
457
+ flexDirection: "column",
458
+ gap: 4
459
+ },
460
+ proxyLabel: {
461
+ fontSize: 12,
462
+ lineHeight: "18px",
463
+ color: "var(--dsw-alias-label-secondary)"
464
+ },
465
+ proxyInput: {
466
+ height: 32,
467
+ width: "100%",
468
+ boxSizing: "border-box",
469
+ border: "1px solid var(--dsw-alias-border-l2)",
470
+ borderRadius: 8,
471
+ padding: "0 10px",
472
+ font: "inherit",
473
+ fontSize: 14,
474
+ lineHeight: "22px",
475
+ background: "var(--dsw-alias-bg-layer-1)",
476
+ color: "var(--dsw-alias-label-primary)"
477
+ },
478
+ proxyHint: {
479
+ margin: 0,
480
+ fontSize: 12,
481
+ lineHeight: "18px",
482
+ color: "var(--dsw-alias-label-tertiary)"
483
+ },
484
+ proxyCheck: {
485
+ display: "flex",
486
+ alignItems: "center",
487
+ gap: 8,
488
+ fontSize: 13,
489
+ lineHeight: "20px",
490
+ color: "var(--dsw-alias-label-primary)",
491
+ cursor: "pointer"
492
+ },
493
+ proxyMessage: {
494
+ margin: 0,
495
+ fontSize: 12,
496
+ lineHeight: "18px"
497
+ },
498
+ proxyActions: {
499
+ display: "flex",
500
+ gap: 8,
501
+ alignItems: "center",
502
+ justifyContent: "flex-end",
503
+ marginTop: 2
504
+ },
505
+ modalOverlay: {
506
+ position: "fixed",
507
+ inset: 0,
508
+ zIndex: 1e3,
509
+ display: "flex",
510
+ alignItems: "center",
511
+ justifyContent: "center",
512
+ padding: 16,
513
+ background: "rgba(0, 0, 0, 0.45)"
514
+ },
515
+ modal: {
516
+ width: 460,
517
+ maxWidth: "100%",
518
+ maxHeight: "90vh",
519
+ overflowY: "auto",
520
+ boxSizing: "border-box",
521
+ display: "flex",
522
+ flexDirection: "column",
523
+ gap: 12,
524
+ padding: "16px 18px",
525
+ borderRadius: 12,
526
+ background: "var(--dsw-alias-bg-layer-1)",
527
+ border: "1px solid var(--dsw-alias-border-l2)"
528
+ },
529
+ modalHeader: {
530
+ display: "flex",
531
+ alignItems: "center",
532
+ gap: 8
533
+ },
534
+ modalTitle: {
535
+ fontWeight: 600,
536
+ fontSize: 15,
537
+ lineHeight: "22px",
538
+ color: "var(--dsw-alias-label-primary)"
383
539
  }
384
540
  };
385
541
  /** Status dot color for one provider state. */
@@ -424,6 +580,18 @@ function usageBarColor(usedPercent) {
424
580
  if (usedPercent >= 80) return "var(--dsw-alias-state-warn-label)";
425
581
  return "var(--dsw-alias-state-success-primary)";
426
582
  }
583
+ /** One-line status text of the proxy config card. */
584
+ function proxyStatusText(t, proxy, loadError) {
585
+ if (loadError !== void 0) return t("proxyLoadFailed", { message: loadError });
586
+ if (proxy === void 0) return t("proxyLoading");
587
+ if (proxy.error !== void 0) return t("proxyStatusError", { message: proxy.error });
588
+ if (proxy.enabled) return t("proxyStatusEnabled", { url: proxy.url });
589
+ return t("proxyStatusNone");
590
+ }
591
+ /** Feedback-line color of the proxy dialog. */
592
+ function messageColor(tone) {
593
+ return tone === "error" ? "var(--dsw-alias-state-error-primary)" : "var(--dsw-alias-state-success-primary)";
594
+ }
427
595
  /**
428
596
  * The Subscriptions settings page component.
429
597
  * @param props - the slot inject face ({@link SubscriptionsSectionInjected}).
@@ -450,6 +618,21 @@ function SubscriptionsSection(props) {
450
618
  const pollersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
451
619
  /** Providers with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
452
620
  const usageInflightRef = (0, react.useRef)(/* @__PURE__ */ new Set());
621
+ /** Proxy config as last answered by `proxyGet`/`proxySet`. */
622
+ const [proxy, setProxy] = (0, react.useState)(void 0);
623
+ const [proxyLoadError, setProxyLoadError] = (0, react.useState)(void 0);
624
+ /** Proxy dialog state (draft fields; the password never pre-fills). */
625
+ const [proxyOpen, setProxyOpen] = (0, react.useState)(false);
626
+ const [proxyEnabled, setProxyEnabled] = (0, react.useState)(false);
627
+ const [proxyUrl, setProxyUrl] = (0, react.useState)("");
628
+ const [proxyUsername, setProxyUsername] = (0, react.useState)("");
629
+ const [proxyPassword, setProxyPassword] = (0, react.useState)("");
630
+ const [proxyClearPassword, setProxyClearPassword] = (0, react.useState)(false);
631
+ const [proxyBypass, setProxyBypass] = (0, react.useState)("");
632
+ const [proxySaving, setProxySaving] = (0, react.useState)(false);
633
+ const [proxyTesting, setProxyTesting] = (0, react.useState)(false);
634
+ const [proxyMessage, setProxyMessage] = (0, react.useState)(void 0);
635
+ const [proxyTestResult, setProxyTestResult] = (0, react.useState)(void 0);
453
636
  const setProviderError = (0, react.useCallback)((provider, message) => {
454
637
  if (!mountedRef.current) return;
455
638
  setErrors((prev) => {
@@ -669,194 +852,495 @@ function SubscriptionsSection(props) {
669
852
  }, 1500);
670
853
  }).catch(() => void 0);
671
854
  }, []);
855
+ (0, react.useEffect)(() => {
856
+ if (rpc === void 0) return;
857
+ let alive = true;
858
+ callSubscriptionsAuth(rpc, "proxyGet", {}).then((view) => {
859
+ if (!alive) return;
860
+ setProxy(view);
861
+ setProxyLoadError(void 0);
862
+ }).catch((error) => {
863
+ if (alive) setProxyLoadError(messageOf(error));
864
+ });
865
+ return () => {
866
+ alive = false;
867
+ };
868
+ }, [rpc]);
869
+ (0, react.useEffect)(() => {
870
+ if (!proxyOpen) return;
871
+ const onKey = (event) => {
872
+ if (event.key === "Escape") setProxyOpen(false);
873
+ };
874
+ window.addEventListener("keydown", onKey);
875
+ return () => window.removeEventListener("keydown", onKey);
876
+ }, [proxyOpen]);
877
+ const openProxyDialog = (0, react.useCallback)(() => {
878
+ if (proxy === void 0) return;
879
+ setProxyEnabled(proxy.enabled);
880
+ setProxyUrl(proxy.url);
881
+ setProxyUsername(proxy.username ?? "");
882
+ setProxyPassword("");
883
+ setProxyClearPassword(false);
884
+ setProxyBypass(proxy.bypass.join(", "));
885
+ setProxyMessage(void 0);
886
+ setProxyTestResult(void 0);
887
+ setProxyOpen(true);
888
+ }, [proxy]);
889
+ const saveProxy = (0, react.useCallback)(async () => {
890
+ if (rpc === void 0) return;
891
+ setProxySaving(true);
892
+ setProxyMessage(void 0);
893
+ try {
894
+ setProxy(await callSubscriptionsAuth(rpc, "proxySet", {
895
+ enabled: proxyEnabled,
896
+ url: proxyUrl.trim(),
897
+ username: proxyUsername,
898
+ ...proxyClearPassword ? { password: null } : proxyPassword !== "" ? { password: proxyPassword } : {},
899
+ bypass: proxyBypass.split(/[,\n]/).map((entry) => entry.trim()).filter((entry) => entry !== "")
900
+ }));
901
+ setProxyLoadError(void 0);
902
+ setProxyMessage({
903
+ tone: "success",
904
+ text: t("proxySaved")
905
+ });
906
+ setProxyOpen(false);
907
+ } catch (error) {
908
+ setProxyMessage({
909
+ tone: "error",
910
+ text: t("proxySaveFailed", { message: messageOf(error) })
911
+ });
912
+ } finally {
913
+ setProxySaving(false);
914
+ }
915
+ }, [
916
+ rpc,
917
+ proxyEnabled,
918
+ proxyUrl,
919
+ proxyUsername,
920
+ proxyPassword,
921
+ proxyClearPassword,
922
+ proxyBypass,
923
+ t
924
+ ]);
925
+ const testProxy = (0, react.useCallback)(async () => {
926
+ if (rpc === void 0 || proxyTesting) return;
927
+ setProxyTesting(true);
928
+ setProxyTestResult(void 0);
929
+ try {
930
+ setProxyTestResult(await callSubscriptionsAuth(rpc, "proxyTest", { proxy: {
931
+ url: proxyUrl.trim(),
932
+ ...proxyUsername.trim() !== "" ? { username: proxyUsername.trim() } : {},
933
+ ...proxyPassword !== "" ? { password: proxyPassword } : {}
934
+ } }));
935
+ } catch (error) {
936
+ setProxyTestResult({
937
+ ok: false,
938
+ viaProxy: false,
939
+ error: messageOf(error)
940
+ });
941
+ } finally {
942
+ setProxyTesting(false);
943
+ }
944
+ }, [
945
+ rpc,
946
+ proxyTesting,
947
+ proxyUrl,
948
+ proxyUsername,
949
+ proxyPassword
950
+ ]);
672
951
  if (rpc === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
673
952
  style: styles$4.intro,
674
953
  children: t("unavailable")
675
954
  });
676
955
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
677
956
  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: {
957
+ children: [
958
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
959
+ style: styles$4.intro,
960
+ children: t("intro")
961
+ }),
962
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
963
+ style: styles$4.proxyCard,
964
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
965
+ style: styles$4.cardHeader,
966
+ children: [
967
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
694
968
  ...styles$4.dot,
695
- background: dotColor(status)
696
- } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
969
+ background: proxy?.enabled === true ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-label-dimmed)"
970
+ } }),
971
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
697
972
  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", {
973
+ children: t("proxyTitle")
974
+ }),
975
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
976
+ type: "button",
977
+ style: {
978
+ ...styles$4.button,
979
+ marginLeft: "auto",
980
+ flexShrink: 0
981
+ },
982
+ onClick: openProxyDialog,
983
+ children: t("proxyConfigure")
984
+ })
985
+ ]
986
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
987
+ style: styles$4.statusLine,
988
+ children: proxyStatusText(t, proxy, proxyLoadError)
989
+ })]
990
+ }),
991
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: styles$4.separator }),
992
+ PROVIDERS.map(({ id, name }) => {
993
+ const status = statuses[id];
994
+ const busy = status?.busy === true;
995
+ const deviceCode = deviceCodes[id];
996
+ const usage = usages[id];
997
+ const usageError = usageErrors[id];
998
+ const showUsage = status?.loggedIn === true && usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[id] === true);
999
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1000
+ style: styles$4.card,
1001
+ children: [
1002
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1003
+ style: styles$4.cardHeader,
1004
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1005
+ ...styles$4.dot,
1006
+ background: dotColor(status)
1007
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1008
+ style: styles$4.name,
1009
+ children: name
1010
+ })]
1011
+ }),
1012
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1013
+ style: styles$4.statusLine,
1014
+ children: statusText(t, status)
1015
+ }),
1016
+ status?.detail !== void 0 && status.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1017
+ style: styles$4.statusLine,
1018
+ children: status.detail
1019
+ }),
1020
+ errors[id] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1021
+ style: styles$4.errorLine,
1022
+ children: errors[id]
1023
+ }),
1024
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1025
+ style: styles$4.actions,
1026
+ children: [
1027
+ !busy && status?.loggedIn !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1028
+ type: "button",
1029
+ style: styles$4.button,
1030
+ onClick: () => {
1031
+ login(id);
1032
+ },
1033
+ children: t("login")
1034
+ }),
1035
+ busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1036
+ type: "button",
1037
+ style: styles$4.button,
1038
+ onClick: () => {
1039
+ cancel(id);
1040
+ },
1041
+ children: t("cancel")
1042
+ }),
1043
+ status?.loggedIn === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1044
+ type: "button",
1045
+ style: styles$4.button,
1046
+ onClick: () => {
1047
+ logout(id, name);
1048
+ },
1049
+ children: t("logout")
1050
+ })
1051
+ ]
1052
+ }),
1053
+ showUsage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1054
+ style: styles$4.usage,
1055
+ children: [
1056
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1057
+ style: styles$4.usageHeader,
1058
+ children: [
1059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1060
+ style: styles$4.usageTitle,
1061
+ children: t("usageTitle")
1062
+ }),
1063
+ usage?.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1064
+ style: styles$4.usagePlan,
1065
+ children: t("usagePlan", { plan: usage.plan })
1066
+ }),
1067
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1068
+ type: "button",
1069
+ style: {
1070
+ ...styles$4.usageRefresh,
1071
+ ...usageLoading[id] === true ? {
1072
+ opacity: .5,
1073
+ cursor: "default"
1074
+ } : {}
1075
+ },
1076
+ disabled: usageLoading[id] === true,
1077
+ onClick: () => {
1078
+ loadUsage(id);
1079
+ },
1080
+ children: t("usageRefresh")
1081
+ })
1082
+ ]
1083
+ }),
1084
+ usage === void 0 && usageError === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1085
+ style: styles$4.statusLine,
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
+ }),
1115
+ busy && deviceCode !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1116
+ style: styles$4.deviceCode,
1117
+ children: [
1118
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1119
+ style: styles$4.statusLine,
1120
+ children: t("deviceCodePrompt")
1121
+ }),
1122
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1123
+ style: styles$4.deviceCodeText,
1124
+ children: deviceCode.userCode
1125
+ }),
1126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1127
+ style: styles$4.actions,
1128
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
757
1129
  type: "button",
758
- style: {
759
- ...styles$4.usageRefresh,
760
- ...usageLoading[id] === true ? {
761
- opacity: .5,
762
- cursor: "default"
763
- } : {}
1130
+ style: styles$4.button,
1131
+ onClick: () => {
1132
+ copyDeviceCode(id, deviceCode.userCode);
764
1133
  },
765
- disabled: usageLoading[id] === true,
1134
+ children: copiedCode === id ? t("deviceCodeCopied") : t("deviceCodeCopy")
1135
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1136
+ type: "button",
1137
+ style: styles$4.button,
766
1138
  onClick: () => {
767
- loadUsage(id);
1139
+ window.open(deviceCode.verificationUrl, "_blank", "noopener");
768
1140
  },
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
- } })
1141
+ children: t("deviceCodeOpenPage")
799
1142
  })]
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")
1143
+ })
1144
+ ]
1145
+ }),
1146
+ busy && deviceCode === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
1147
+ style: styles$4.manual,
1148
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("manualSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1149
+ style: styles$4.manualRow,
1150
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1151
+ style: styles$4.manualInput,
1152
+ value: manualDrafts[id],
1153
+ placeholder: t("manualPlaceholder"),
1154
+ onChange: (event) => setManualDrafts((prev) => ({
1155
+ ...prev,
1156
+ [id]: event.target.value
1157
+ }))
824
1158
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
825
1159
  type: "button",
826
1160
  style: styles$4.button,
827
1161
  onClick: () => {
828
- window.open(deviceCode.verificationUrl, "_blank", "noopener");
1162
+ submitManual(id);
829
1163
  },
830
- children: t("deviceCodeOpenPage")
1164
+ children: t("submit")
831
1165
  })]
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
- }))
1166
+ })]
1167
+ })
1168
+ ]
1169
+ }, id);
1170
+ }),
1171
+ proxyOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1172
+ style: styles$4.modalOverlay,
1173
+ onClick: () => setProxyOpen(false),
1174
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1175
+ style: styles$4.modal,
1176
+ onClick: (event) => event.stopPropagation(),
1177
+ children: [
1178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1179
+ style: styles$4.modalHeader,
1180
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1181
+ style: styles$4.modalTitle,
1182
+ children: t("proxyDialogTitle")
847
1183
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
848
1184
  type: "button",
849
- style: styles$4.button,
850
- onClick: () => {
851
- submitManual(id);
1185
+ style: {
1186
+ ...styles$4.button,
1187
+ marginLeft: "auto"
852
1188
  },
853
- children: t("submit")
1189
+ onClick: () => setProxyOpen(false),
1190
+ children: t("proxyDialogClose")
854
1191
  })]
855
- })]
856
- })
857
- ]
858
- }, id);
859
- })]
1192
+ }),
1193
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1194
+ style: styles$4.proxyCheck,
1195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1196
+ type: "checkbox",
1197
+ checked: proxyEnabled,
1198
+ onChange: (event) => setProxyEnabled(event.target.checked)
1199
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("proxyEnabled") })]
1200
+ }),
1201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1202
+ style: styles$4.proxyField,
1203
+ children: [
1204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1205
+ style: styles$4.proxyLabel,
1206
+ children: t("proxyUrl")
1207
+ }),
1208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1209
+ style: styles$4.proxyInput,
1210
+ value: proxyUrl,
1211
+ placeholder: t("proxyUrlPlaceholder"),
1212
+ onChange: (event) => setProxyUrl(event.target.value)
1213
+ }),
1214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1215
+ style: styles$4.proxyHint,
1216
+ children: t("proxyUrlHint")
1217
+ })
1218
+ ]
1219
+ }),
1220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1221
+ style: styles$4.proxyField,
1222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1223
+ style: styles$4.proxyLabel,
1224
+ children: t("proxyUsername")
1225
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1226
+ style: styles$4.proxyInput,
1227
+ value: proxyUsername,
1228
+ placeholder: t("proxyUsernamePlaceholder"),
1229
+ onChange: (event) => setProxyUsername(event.target.value)
1230
+ })]
1231
+ }),
1232
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1233
+ style: styles$4.proxyField,
1234
+ children: [
1235
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1236
+ style: styles$4.proxyLabel,
1237
+ children: t("proxyPassword")
1238
+ }),
1239
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1240
+ type: "password",
1241
+ style: styles$4.proxyInput,
1242
+ value: proxyPassword,
1243
+ placeholder: t("proxyPasswordPlaceholder"),
1244
+ onChange: (event) => setProxyPassword(event.target.value)
1245
+ }),
1246
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1247
+ style: styles$4.proxyCheck,
1248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1249
+ type: "checkbox",
1250
+ checked: proxyClearPassword,
1251
+ onChange: (event) => setProxyClearPassword(event.target.checked)
1252
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("proxyClearPassword") })]
1253
+ })
1254
+ ]
1255
+ }),
1256
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1257
+ style: styles$4.proxyField,
1258
+ children: [
1259
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1260
+ style: styles$4.proxyLabel,
1261
+ children: t("proxyBypass")
1262
+ }),
1263
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1264
+ style: styles$4.proxyInput,
1265
+ value: proxyBypass,
1266
+ placeholder: t("proxyBypassPlaceholder"),
1267
+ onChange: (event) => setProxyBypass(event.target.value)
1268
+ }),
1269
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1270
+ style: styles$4.proxyHint,
1271
+ children: t("proxyBypassHint")
1272
+ })
1273
+ ]
1274
+ }),
1275
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1276
+ style: styles$4.proxyHint,
1277
+ children: t("proxyNote")
1278
+ }),
1279
+ proxyMessage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1280
+ style: {
1281
+ ...styles$4.proxyMessage,
1282
+ color: messageColor(proxyMessage.tone)
1283
+ },
1284
+ children: proxyMessage.text
1285
+ }),
1286
+ proxyTestResult !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1287
+ style: {
1288
+ ...styles$4.proxyMessage,
1289
+ color: proxyTestResult.ok ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-state-error-primary)"
1290
+ },
1291
+ children: proxyTestResult.ok ? proxyTestResult.viaProxy ? t("proxyTestOk", {
1292
+ status: String(proxyTestResult.status),
1293
+ ms: String(proxyTestResult.latencyMs)
1294
+ }) : t("proxyTestOkDirect", {
1295
+ status: String(proxyTestResult.status),
1296
+ ms: String(proxyTestResult.latencyMs)
1297
+ }) : t("proxyTestFail", { message: proxyTestResult.error ?? "" })
1298
+ }),
1299
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1300
+ style: styles$4.proxyActions,
1301
+ children: [
1302
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1303
+ type: "button",
1304
+ style: {
1305
+ ...styles$4.button,
1306
+ ...proxyTesting ? {
1307
+ opacity: .5,
1308
+ cursor: "default"
1309
+ } : {}
1310
+ },
1311
+ disabled: proxyTesting,
1312
+ onClick: () => {
1313
+ testProxy();
1314
+ },
1315
+ children: proxyTesting ? t("proxyTesting") : t("proxyTest")
1316
+ }),
1317
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1318
+ type: "button",
1319
+ style: {
1320
+ ...styles$4.button,
1321
+ ...proxySaving ? {
1322
+ opacity: .5,
1323
+ cursor: "default"
1324
+ } : {}
1325
+ },
1326
+ disabled: proxySaving,
1327
+ onClick: () => {
1328
+ saveProxy();
1329
+ },
1330
+ children: proxySaving ? t("proxySaving") : t("proxySave")
1331
+ }),
1332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1333
+ type: "button",
1334
+ style: styles$4.button,
1335
+ onClick: () => setProxyOpen(false),
1336
+ children: t("proxyCancel")
1337
+ })
1338
+ ]
1339
+ })
1340
+ ]
1341
+ })
1342
+ })
1343
+ ]
860
1344
  });
861
1345
  }
862
1346
 
@@ -1738,6 +2222,13 @@ function apply(ctx) {
1738
2222
  zh,
1739
2223
  en
1740
2224
  }), "dsh-plugin-subscriptions: copy dictionaries");
2225
+ ctx.effect(() => {
2226
+ const style = document.createElement("style");
2227
+ style.setAttribute("data-plugin", "dsh-plugin-subscriptions");
2228
+ style.textContent = "div[role=\"dialog\"][aria-modal=\"true\"]:has(> nav) { padding-top: 14px; }";
2229
+ document.head.appendChild(style);
2230
+ return () => style.remove();
2231
+ }, "dsh-plugin-subscriptions: settings panel breathing room");
1741
2232
  const connection = ctx.get("connection");
1742
2233
  const t = ctx.locale.bind(NS);
1743
2234
  const injected = () => ({