dsh-plugin-subscriptions 0.5.0 → 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.
Files changed (44) hide show
  1. package/README.md +21 -6
  2. package/README.zh.md +20 -6
  3. package/lib/auth/device-flow.d.ts +55 -0
  4. package/lib/auth/device-flow.js +177 -0
  5. package/lib/auth/oauth-flow.js +1 -1
  6. package/lib/auth/rpc.d.ts +18 -2
  7. package/lib/auth/rpc.js +98 -3
  8. package/lib/auth/store.d.ts +20 -2
  9. package/lib/auth/store.js +45 -9
  10. package/lib/client/SubscriptionsSection.d.ts +18 -1
  11. package/lib/client/SubscriptionsSection.js +216 -6
  12. package/lib/client/index.js +11 -0
  13. package/lib/client/locales.d.ts +72 -0
  14. package/lib/client/locales.js +72 -0
  15. package/lib/client.js +725 -144
  16. package/lib/client.js.map +1 -1
  17. package/lib/http.d.ts +114 -0
  18. package/lib/http.js +402 -0
  19. package/lib/index.d.ts +3 -2
  20. package/lib/index.js +2256 -226
  21. package/lib/providers/antigravity.d.ts +90 -0
  22. package/lib/providers/antigravity.js +392 -0
  23. package/lib/providers/catalog-store.js +15 -0
  24. package/lib/providers/claude.d.ts +20 -1
  25. package/lib/providers/claude.js +51 -33
  26. package/lib/providers/codex.js +58 -13
  27. package/lib/providers/common.d.ts +32 -1
  28. package/lib/providers/common.js +48 -1
  29. package/lib/providers/copilot.d.ts +315 -0
  30. package/lib/providers/copilot.js +787 -0
  31. package/lib/providers/grok.d.ts +7 -2
  32. package/lib/providers/grok.js +53 -24
  33. package/lib/tools/image-generate.js +2 -1
  34. package/lib/tools/video-generate.js +2 -1
  35. package/lib/tools/x-search.js +2 -1
  36. package/lib/translate/anthropic.d.ts +47 -6
  37. package/lib/translate/anthropic.js +135 -20
  38. package/lib/translate/antigravity.d.ts +110 -0
  39. package/lib/translate/antigravity.js +303 -0
  40. package/lib/translate/chat-completions.d.ts +120 -0
  41. package/lib/translate/chat-completions.js +363 -0
  42. package/lib/translate/responses.d.ts +49 -5
  43. package/lib/translate/responses.js +40 -7
  44. package/package.json +11 -7
package/lib/client.js CHANGED
@@ -52,6 +52,10 @@ const en = {
52
52
  manualPlaceholder: "Paste the callback URL or code",
53
53
  submit: "Submit",
54
54
  loginMissingUrl: "login answered without an authorizeUrl",
55
+ deviceCodePrompt: "Enter this code on the GitHub verification page:",
56
+ deviceCodeCopy: "Copy code",
57
+ deviceCodeCopied: "Copied",
58
+ deviceCodeOpenPage: "Open GitHub verification page",
55
59
  usageTitle: "Usage",
56
60
  usageRefresh: "Refresh",
57
61
  usageLoading: "Loading usage…",
@@ -79,7 +83,39 @@ const en = {
79
83
  speedFast: "Fast",
80
84
  speedFastDescription: "1.5x speed, more usage",
81
85
  commandFast: "Switch the Codex speed tier (Standard/Fast)",
82
- 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."
83
119
  };
84
120
  /** zh strings, one per {@link en} key. */
85
121
  const zh = {
@@ -101,6 +137,10 @@ const zh = {
101
137
  manualPlaceholder: "粘贴回调 URL 或授权码",
102
138
  submit: "提交",
103
139
  loginMissingUrl: "login 响应缺少 authorizeUrl",
140
+ deviceCodePrompt: "在 GitHub 验证页面输入此验证码:",
141
+ deviceCodeCopy: "复制验证码",
142
+ deviceCodeCopied: "已复制",
143
+ deviceCodeOpenPage: "打开 GitHub 验证页面",
104
144
  usageTitle: "用量",
105
145
  usageRefresh: "刷新",
106
146
  usageLoading: "用量加载中…",
@@ -128,7 +168,39 @@ const zh = {
128
168
  speedFast: "快速",
129
169
  speedFastDescription: "约 1.5 倍速度,消耗更多用量",
130
170
  commandFast: "切换 Codex 速度档(标准/快速)",
131
- 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 授权页在浏览器中打开,走的是浏览器/系统代理,不受此设置影响。"
132
204
  };
133
205
 
134
206
  //#endregion
@@ -150,6 +222,10 @@ const PROVIDERS = [
150
222
  {
151
223
  id: "grok",
152
224
  name: "Grok (X Premium)"
225
+ },
226
+ {
227
+ id: "copilot",
228
+ name: "GitHub Copilot"
153
229
  }
154
230
  ];
155
231
  /** Business error returned by the `/subscriptions-auth` channel (error branch message). */
@@ -210,6 +286,13 @@ const styles$4 = {
210
286
  flexDirection: "column",
211
287
  gap: 6
212
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)" },
213
296
  cardHeader: {
214
297
  display: "flex",
215
298
  alignItems: "center",
@@ -350,6 +433,109 @@ const styles$4 = {
350
433
  lineHeight: "22px",
351
434
  background: "var(--dsw-alias-bg-layer-1)",
352
435
  color: "var(--dsw-alias-label-primary)"
436
+ },
437
+ deviceCode: {
438
+ marginTop: 4,
439
+ display: "flex",
440
+ flexDirection: "column",
441
+ gap: 6,
442
+ border: "1px solid var(--dsw-alias-border-l2)",
443
+ borderRadius: 8,
444
+ padding: "10px 12px",
445
+ background: "var(--dsw-alias-bg-layer-1)"
446
+ },
447
+ deviceCodeText: {
448
+ fontFamily: "monospace",
449
+ fontSize: 18,
450
+ lineHeight: "24px",
451
+ letterSpacing: 2,
452
+ color: "var(--dsw-alias-label-primary)",
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)"
353
539
  }
354
540
  };
355
541
  /** Status dot color for one provider state. */
@@ -394,6 +580,18 @@ function usageBarColor(usedPercent) {
394
580
  if (usedPercent >= 80) return "var(--dsw-alias-state-warn-label)";
395
581
  return "var(--dsw-alias-state-success-primary)";
396
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
+ }
397
595
  /**
398
596
  * The Subscriptions settings page component.
399
597
  * @param props - the slot inject face ({@link SubscriptionsSectionInjected}).
@@ -407,8 +605,12 @@ function SubscriptionsSection(props) {
407
605
  const [manualDrafts, setManualDrafts] = (0, react.useState)({
408
606
  codex: "",
409
607
  claude: "",
410
- grok: ""
608
+ grok: "",
609
+ copilot: ""
411
610
  });
611
+ /** Pending device-flow codes (copilot), shown while the attempt polls. */
612
+ const [deviceCodes, setDeviceCodes] = (0, react.useState)({});
613
+ const [copiedCode, setCopiedCode] = (0, react.useState)(void 0);
412
614
  const [usages, setUsages] = (0, react.useState)({});
413
615
  const [usageErrors, setUsageErrors] = (0, react.useState)({});
414
616
  const [usageLoading, setUsageLoading] = (0, react.useState)({});
@@ -416,6 +618,21 @@ function SubscriptionsSection(props) {
416
618
  const pollersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
417
619
  /** Providers with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
418
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);
419
636
  const setProviderError = (0, react.useCallback)((provider, message) => {
420
637
  if (!mountedRef.current) return;
421
638
  setErrors((prev) => {
@@ -445,7 +662,15 @@ function SubscriptionsSection(props) {
445
662
  setStatuses(response.providers);
446
663
  for (const { id } of PROVIDERS) {
447
664
  const status = response.providers[id];
448
- if (status.loggedIn || !status.busy) stopPolling(id);
665
+ if (status.loggedIn || !status.busy) {
666
+ stopPolling(id);
667
+ setDeviceCodes((prev) => {
668
+ if (prev[id] === void 0) return prev;
669
+ const next = { ...prev };
670
+ delete next[id];
671
+ return next;
672
+ });
673
+ }
449
674
  }
450
675
  }, [rpc, stopPolling]);
451
676
  const startPolling = (0, react.useCallback)((provider) => {
@@ -536,7 +761,6 @@ function SubscriptionsSection(props) {
536
761
  return;
537
762
  }
538
763
  if (typeof response.authorizeUrl !== "string") throw new SubscriptionsAuthError(t("loginMissingUrl"));
539
- window.open(response.authorizeUrl, "_blank", "noopener");
540
764
  if (!mountedRef.current) return;
541
765
  setStatuses((prev) => ({
542
766
  ...prev,
@@ -546,6 +770,14 @@ function SubscriptionsSection(props) {
546
770
  loggedIn: false
547
771
  }
548
772
  }));
773
+ if (typeof response.userCode === "string" && response.userCode.length > 0) setDeviceCodes((prev) => ({
774
+ ...prev,
775
+ [provider]: {
776
+ userCode: response.userCode,
777
+ verificationUrl: response.authorizeUrl
778
+ }
779
+ }));
780
+ else window.open(response.authorizeUrl, "_blank", "noopener");
549
781
  startPolling(provider);
550
782
  } catch (error) {
551
783
  setProviderError(provider, messageOf(error));
@@ -611,162 +843,504 @@ function SubscriptionsSection(props) {
611
843
  setProviderError,
612
844
  refresh
613
845
  ]);
846
+ const copyDeviceCode = (0, react.useCallback)((provider, userCode) => {
847
+ navigator.clipboard?.writeText(userCode).then(() => {
848
+ if (!mountedRef.current) return;
849
+ setCopiedCode(provider);
850
+ setTimeout(() => {
851
+ if (mountedRef.current) setCopiedCode((current) => current === provider ? void 0 : current);
852
+ }, 1500);
853
+ }).catch(() => void 0);
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
+ ]);
614
951
  if (rpc === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
615
952
  style: styles$4.intro,
616
953
  children: t("unavailable")
617
954
  });
618
955
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
619
956
  style: styles$4.section,
620
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
621
- style: styles$4.intro,
622
- children: t("intro")
623
- }), PROVIDERS.map(({ id, name }) => {
624
- const status = statuses[id];
625
- const busy = status?.busy === true;
626
- const usage = usages[id];
627
- const usageError = usageErrors[id];
628
- const showUsage = status?.loggedIn === true && usage?.supported !== false && (usage !== void 0 || usageError !== void 0 || usageLoading[id] === true);
629
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
630
- style: styles$4.card,
631
- children: [
632
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
633
- style: styles$4.cardHeader,
634
- 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: {
635
968
  ...styles$4.dot,
636
- background: dotColor(status)
637
- } }), /* @__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", {
638
972
  style: styles$4.name,
639
- children: name
640
- })]
641
- }),
642
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
643
- style: styles$4.statusLine,
644
- children: statusText(t, status)
645
- }),
646
- status?.detail !== void 0 && status.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
647
- style: styles$4.statusLine,
648
- children: status.detail
649
- }),
650
- errors[id] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
651
- style: styles$4.errorLine,
652
- children: errors[id]
653
- }),
654
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
655
- style: styles$4.actions,
656
- children: [
657
- !busy && status?.loggedIn !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
658
- type: "button",
659
- style: styles$4.button,
660
- onClick: () => {
661
- login(id);
662
- },
663
- children: t("login")
664
- }),
665
- busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
666
- type: "button",
667
- style: styles$4.button,
668
- onClick: () => {
669
- cancel(id);
670
- },
671
- children: t("cancel")
672
- }),
673
- status?.loggedIn === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
674
- type: "button",
675
- style: styles$4.button,
676
- onClick: () => {
677
- logout(id, name);
678
- },
679
- children: t("logout")
680
- })
681
- ]
682
- }),
683
- showUsage && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
684
- style: styles$4.usage,
685
- children: [
686
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
687
- style: styles$4.usageHeader,
688
- children: [
689
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
690
- style: styles$4.usageTitle,
691
- children: t("usageTitle")
692
- }),
693
- usage?.plan !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
694
- style: styles$4.usagePlan,
695
- children: t("usagePlan", { plan: usage.plan })
696
- }),
697
- /* @__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", {
698
1129
  type: "button",
699
- style: {
700
- ...styles$4.usageRefresh,
701
- ...usageLoading[id] === true ? {
702
- opacity: .5,
703
- cursor: "default"
704
- } : {}
1130
+ style: styles$4.button,
1131
+ onClick: () => {
1132
+ copyDeviceCode(id, deviceCode.userCode);
705
1133
  },
706
- 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,
707
1138
  onClick: () => {
708
- loadUsage(id);
1139
+ window.open(deviceCode.verificationUrl, "_blank", "noopener");
709
1140
  },
710
- children: t("usageRefresh")
711
- })
712
- ]
713
- }),
714
- usage === void 0 && usageError === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
715
- style: styles$4.statusLine,
716
- children: t("usageLoading")
717
- }),
718
- usageError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
719
- style: styles$4.errorLine,
720
- children: t("usageError", { message: usageError })
721
- }),
722
- usage?.windows !== void 0 && usage.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
723
- style: styles$4.statusLine,
724
- children: t("usageEmpty")
725
- }),
726
- (usage?.windows ?? []).map((window$1, index) => {
727
- const percent = Math.min(100, Math.max(0, window$1.usedPercent));
728
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
729
- style: styles$4.usageRow,
730
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
731
- style: styles$4.usageMeta,
732
- 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() })}`] })]
733
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
734
- style: styles$4.usageTrack,
735
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
736
- ...styles$4.usageFill,
737
- width: `${String(percent)}%`,
738
- background: usageBarColor(percent)
739
- } })
1141
+ children: t("deviceCodeOpenPage")
740
1142
  })]
741
- }, index);
742
- })
743
- ]
744
- }),
745
- busy && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
746
- style: styles$4.manual,
747
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("manualSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
748
- style: styles$4.manualRow,
749
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
750
- style: styles$4.manualInput,
751
- value: manualDrafts[id],
752
- placeholder: t("manualPlaceholder"),
753
- onChange: (event) => setManualDrafts((prev) => ({
754
- ...prev,
755
- [id]: event.target.value
756
- }))
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
+ }))
1158
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1159
+ type: "button",
1160
+ style: styles$4.button,
1161
+ onClick: () => {
1162
+ submitManual(id);
1163
+ },
1164
+ children: t("submit")
1165
+ })]
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")
757
1183
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
758
1184
  type: "button",
759
- style: styles$4.button,
760
- onClick: () => {
761
- submitManual(id);
1185
+ style: {
1186
+ ...styles$4.button,
1187
+ marginLeft: "auto"
762
1188
  },
763
- children: t("submit")
1189
+ onClick: () => setProxyOpen(false),
1190
+ children: t("proxyDialogClose")
764
1191
  })]
765
- })]
766
- })
767
- ]
768
- }, id);
769
- })]
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
+ ]
770
1344
  });
771
1345
  }
772
1346
 
@@ -1648,6 +2222,13 @@ function apply(ctx) {
1648
2222
  zh,
1649
2223
  en
1650
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");
1651
2232
  const connection = ctx.get("connection");
1652
2233
  const t = ctx.locale.bind(NS);
1653
2234
  const injected = () => ({