@yezack/dsh-remote-mobile 1.10.0 → 1.10.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
@@ -47,10 +47,10 @@ __export(index_exports, {
47
47
  inject: () => inject
48
48
  });
49
49
  module.exports = __toCommonJS(index_exports);
50
- var import_react10 = __toESM(require("react"), 1);
50
+ var import_react11 = __toESM(require("react"), 1);
51
51
 
52
52
  // src/client/components/MainSection.jsx
53
- var import_react9 = __toESM(require("react"), 1);
53
+ var import_react10 = __toESM(require("react"), 1);
54
54
 
55
55
  // src/client/api.js
56
56
  var RM_API_BASE = (function() {
@@ -103,6 +103,13 @@ function updateAdvancedSecurityOptions(payload) {
103
103
  body: JSON.stringify(payload)
104
104
  });
105
105
  }
106
+ function updateProxyConfig(payload) {
107
+ return safeFetchJson(RM_API_BASE + "update-options", {
108
+ method: "POST",
109
+ headers: { "Content-Type": "application/json" },
110
+ body: JSON.stringify(payload)
111
+ });
112
+ }
106
113
  function updateSecret(secret) {
107
114
  return safeFetchJson(RM_API_BASE + "set-secret", {
108
115
  method: "POST",
@@ -172,6 +179,13 @@ function resetStylesApi() {
172
179
  headers: { "Content-Type": "application/json" }
173
180
  });
174
181
  }
182
+ function toggleDirectoryPickerApi(enabled) {
183
+ return safeFetchJson(RM_API_BASE + "directory-picker", {
184
+ method: "POST",
185
+ headers: { "Content-Type": "application/json" },
186
+ body: JSON.stringify({ enabled: Boolean(enabled) })
187
+ });
188
+ }
175
189
 
176
190
  // src/client/qrcode.js
177
191
  var generateQrSvg = (function() {
@@ -687,6 +701,18 @@ var generateQrSvg = (function() {
687
701
  };
688
702
  })();
689
703
 
704
+ // src/client/utils/access.js
705
+ function resolveAccessPort(status) {
706
+ const port = Number(status?.proxy?.activePort);
707
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
708
+ return null;
709
+ }
710
+ return port;
711
+ }
712
+ function buildDirectLink(host, port, token) {
713
+ return `http://${host}:${port}/auth?token=${token || ""}`;
714
+ }
715
+
690
716
  // src/client/components/NetworkCard.jsx
691
717
  var import_react = __toESM(require("react"), 1);
692
718
 
@@ -798,6 +824,30 @@ var translations = {
798
824
  saveConfigSuccessToast: "\u9AD8\u7EA7\u5B89\u5168\u53C2\u6570\u5DF2\u6210\u529F\u4FDD\u5B58\u5E76\u540C\u6B65\u5230 settings.json\uFF01",
799
825
  restoreDefaultsSuccessToast: "\u5DF2\u6210\u529F\u6062\u590D\u9ED8\u8BA4\u5B89\u5168\u53C2\u6570\u5E76\u7ACB\u5373\u751F\u6548\uFF01",
800
826
  saveConfigFailTip: "\u53C2\u6570\u5FC5\u987B\u4E3A\u5927\u4E8E 0 \u7684\u6709\u6548\u6574\u6570",
827
+ // 卡片 9: 接入代理配置
828
+ proxyConfigCardTitle: "\u63A5\u5165\u4EE3\u7406\u914D\u7F6E",
829
+ proxyConfigCardDesc: "\u63A7\u5236\u63A5\u5165\u6A21\u5F0F\u3001\u4EE3\u7406\u76D1\u542C\u5730\u5740\u4E0E\u53D7\u4FE1\u4EFB\u57DF\u540D\u3002\u4FDD\u5B58\u540E\u9700\u91CD\u542F DSH \u751F\u6548\u3002",
830
+ proxyModeLabel: "\u63A5\u5165\u6A21\u5F0F",
831
+ proxyModeHint: "proxy\uFF1A\u6838\u5FC3\u56DE\u73AF\uFF0C\u7531\u63D2\u4EF6\u8F6C\u53D1\uFF1Bdirect\uFF1A\u4E0D\u542F\u52A8\u4EE3\u7406\uFF0C\u76F4\u63A5\u8BBF\u95EE\u6838\u5FC3",
832
+ proxyModeProxy: "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\uFF08proxy\uFF09",
833
+ proxyModeDirect: "\u76F4\u8FDE\u6838\u5FC3\uFF08direct\uFF09",
834
+ proxyPortLabel: "\u4EE3\u7406\u7AEF\u53E3",
835
+ proxyPortHint: "\u9ED8\u8BA4 3081\uFF1B\u7AEF\u53E3\u5360\u7528\u65F6\u4F1A\u81EA\u52A8\u9012\u589E",
836
+ proxyHostLabel: "\u4EE3\u7406\u76D1\u542C\u5730\u5740",
837
+ proxyHostHint: "0.0.0.0 \u8868\u793A\u6240\u6709\u7F51\u5361\uFF1B127.0.0.1 \u4EC5\u672C\u673A",
838
+ proxyTrustedHostsLabel: "\u53D7\u4FE1\u4EFB\u57DF\u540D",
839
+ proxyTrustedHostsHint: "\u6BCF\u884C\u4E00\u4E2A\uFF0C\u4F8B\u5982 xxx.tailnet.ts.net\uFF1B\u81EA\u5B9A\u4E49\u53CD\u4EE3\u57DF\u540D\u5FC5\u987B\u52A0\u5165",
840
+ proxyRestartNote: "\u4FDD\u5B58\u540E\u9700\u91CD\u542F DSH \u624D\u4F1A\u91CD\u65B0\u7ED1\u5B9A\u76D1\u542C\u5730\u5740/\u7AEF\u53E3\u3002",
841
+ proxySaveBtn: "\u{1F4BE} \u4FDD\u5B58\u63A5\u5165\u914D\u7F6E",
842
+ proxySavingBtn: "\u6B63\u5728\u4FDD\u5B58...",
843
+ proxyResetBtn: "\u{1F504} \u6062\u590D\u9ED8\u8BA4",
844
+ proxySaveConfirm: "\u786E\u5B9A\u4FDD\u5B58\u63A5\u5165\u914D\u7F6E\u5417\uFF1F\u4FDD\u5B58\u540E\u9700\u91CD\u542F DSH \u751F\u6548\u3002",
845
+ proxyResetConfirm: "\u786E\u5B9A\u6062\u590D\u9ED8\u8BA4\u63A5\u5165\u914D\u7F6E\uFF08proxy\u30013081\u30010.0.0.0\u3001\u65E0\u53D7\u4FE1\u4EFB\u57DF\u540D\uFF09\u5417\uFF1F",
846
+ proxySavedToast: "\u63A5\u5165\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u91CD\u542F DSH \u540E\u751F\u6548\u3002",
847
+ proxyResetToast: "\u5DF2\u6062\u590D\u9ED8\u8BA4\u63A5\u5165\u914D\u7F6E\uFF1B\u91CD\u542F DSH \u540E\u751F\u6548\u3002",
848
+ proxySaveFailTip: "\u8BF7\u68C0\u67E5\u7AEF\u53E3\u3001\u76D1\u542C\u5730\u5740\u4E0E\u53D7\u4FE1\u4EFB\u57DF\u540D\u683C\u5F0F",
849
+ proxyCurrentRuntime: "\u5F53\u524D\u8FD0\u884C",
850
+ proxyNotReady: "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u672A\u5C31\u7EEA\uFF0C\u8BF7\u5148\u542F\u52A8\u4EE3\u7406",
801
851
  // 卡片 8: 移动端样式片段(样式小插件)
802
852
  tabAccess: "\u63A5\u5165",
803
853
  tabDevices: "\u8BBE\u5907\u4E0E\u5B89\u5168",
@@ -841,6 +891,26 @@ var translations = {
841
891
  conflictReportText: "[DSH \u63D2\u4EF6\u51B2\u7A81\u8BCA\u65AD\u62A5\u544A]\n\u73B0\u8C61\uFF1Adsh-remote-mobile \u8BBE\u7F6E\u9762\u677F\u63D0\u793A\u300C\u68C0\u6D4B\u5230\u5176\u4ED6\u8FDC\u7A0B\u63A5\u5165\u63D2\u4EF6\u300D\u3002\n\u539F\u56E0\uFF1A\u53E6\u4E00\u6B3E\u8FDC\u7A0B/Web \u63A5\u5165\u7C7B\u63D2\u4EF6\u5360\u7528\u4E86 Cordis \u5171\u4EAB\u670D\u52A1 remoteWebUiPairing\uFF0Cdsh-remote-mobile \u4E3A\u4FDD\u8BC1\u6B63\u5E38\u542F\u52A8\u5DF2\u81EA\u52A8\u8BA9\u51FA\u8BE5\u670D\u52A1\uFF0C\u5F53\u524D\u4E24\u5957\u8FDC\u7A0B\u63A5\u5165\u529F\u80FD\u5E76\u5B58\u3002\n\u5360\u7528\u65B9\u63D2\u4EF6\u5305\u540D\uFF1A{plugin}\n\u5360\u7528\u65B9 loader entry id\uFF1A{entryId}\n\u5904\u7406\u65B9\u6848\uFF08\u4E8C\u9009\u4E00\uFF09\uFF1A\n\u65B9\u6848 A\uFF08\u4FDD\u7559 dsh-remote-mobile\uFF0C\u7981\u7528\u5360\u7528\u65B9\uFF09\uFF1A\n1. \u6253\u5F00\u914D\u7F6E\u6587\u4EF6 ~/.dsh/profiles/web/cordis.patch.yml\n2. \u5728\u6587\u4EF6\u672B\u5C3E\u8FFD\u52A0\u4EE5\u4E0B\u4E24\u884C\uFF08\u6CE8\u610F YAML \u7F29\u8FDB\uFF0C\u7B2C\u4E8C\u884C\u524D\u6709\u4E24\u4E2A\u7A7A\u683C\uFF09\uFF1A\n- id: {entryId}\n disabled: true\n3. \u7ED3\u675F\u5F53\u524D\u6B63\u5728\u8FD0\u884C\u7684 dsh web \u8FDB\u7A0B\uFF0C\u91CD\u65B0\u6267\u884C dsh web --no-open\n\u65B9\u6848 B\uFF08\u4FDD\u7559\u5360\u7528\u65B9\uFF0C\u5378\u8F7D dsh-remote-mobile\uFF09\uFF1A\n\u6267\u884C\u547D\u4EE4 dsh plugin --profile web remove dsh-remote-mobile \u540E\u91CD\u542F dsh web",
842
892
  conflictReportNoEntryIdText: "[DSH \u63D2\u4EF6\u51B2\u7A81\u8BCA\u65AD\u62A5\u544A]\n\u73B0\u8C61\uFF1Adsh-remote-mobile \u8BBE\u7F6E\u9762\u677F\u63D0\u793A\u300C\u68C0\u6D4B\u5230\u5176\u4ED6\u8FDC\u7A0B\u63A5\u5165\u63D2\u4EF6\u300D\u3002\n\u539F\u56E0\uFF1A\u53E6\u4E00\u6B3E\u8FDC\u7A0B/Web \u63A5\u5165\u7C7B\u63D2\u4EF6\u5360\u7528\u4E86 Cordis \u5171\u4EAB\u670D\u52A1 remoteWebUiPairing\uFF0Cdsh-remote-mobile \u4E3A\u4FDD\u8BC1\u6B63\u5E38\u542F\u52A8\u5DF2\u81EA\u52A8\u8BA9\u51FA\u8BE5\u670D\u52A1\uFF0C\u5F53\u524D\u4E24\u5957\u8FDC\u7A0B\u63A5\u5165\u529F\u80FD\u5E76\u5B58\u3002\n\u5360\u7528\u65B9\u63D2\u4EF6\u5305\u540D\uFF1A{plugin}\n\u5360\u7528\u65B9 loader entry id\uFF1A\u672A\u80FD\u81EA\u52A8\u8BC6\u522B\n\u5904\u7406\u65B9\u6848\uFF08\u4E8C\u9009\u4E00\uFF09\uFF1A\n\u65B9\u6848 A\uFF08\u4FDD\u7559 dsh-remote-mobile\uFF0C\u7981\u7528\u5360\u7528\u65B9\uFF09\uFF1A\n1. \u6267\u884C dsh --dump-config \u67E5\u770B\u5168\u90E8 loader entry\uFF0C\u627E\u5230\u5360\u7528\u65B9\u63D2\u4EF6\u5BF9\u5E94\u7684\u6761\u76EE id\n2. \u6253\u5F00\u914D\u7F6E\u6587\u4EF6 ~/.dsh/profiles/web/cordis.patch.yml\uFF0C\u5728\u6587\u4EF6\u672B\u5C3E\u8FFD\u52A0\uFF08\u6CE8\u610F YAML \u7F29\u8FDB\uFF09\uFF1A\n- id: <\u5360\u7528\u65B9\u6761\u76EEid>\n disabled: true\n3. \u7ED3\u675F\u5F53\u524D\u6B63\u5728\u8FD0\u884C\u7684 dsh web \u8FDB\u7A0B\uFF0C\u91CD\u65B0\u6267\u884C dsh web --no-open\n\u65B9\u6848 B\uFF08\u4FDD\u7559\u5360\u7528\u65B9\uFF0C\u5378\u8F7D dsh-remote-mobile\uFF09\uFF1A\n\u6267\u884C\u547D\u4EE4 dsh plugin --profile web remove dsh-remote-mobile \u540E\u91CD\u542F dsh web",
843
893
  conflictBannerDismiss: "\u77E5\u9053\u4E86",
894
+ // 移动端目录选择器(添加工作区 / 选择文件夹)提醒与一键配置
895
+ dirPickerBannerTitle: "\u624B\u673A\u7AEF\u300C\u6DFB\u52A0\u5DE5\u4F5C\u533A / \u9009\u62E9\u6587\u4EF6\u5939\u300D\u53D7\u9650\u63D0\u9192",
896
+ dirPickerBannerBody: "\u5F53\u524D DSH \u6838\u5FC3\u7ED1\u5B9A\u5728\u672C\u5730\u56DE\u73AF\uFF08127.0.0.1\uFF09\uFF0C\u5B98\u65B9\u76EE\u5F55\u9009\u62E9\u5668\uFF08dsh-host-directory-picker-auto\uFF09\u4F1A\u5C06\u6765\u81EA 3081 \u4EE3\u7406\u7684\u624B\u673A\u8BF7\u6C42\u8BEF\u5224\u4E3A\u300C\u672C\u673A\u64CD\u4F5C\u300D\uFF0C\u4ECE\u800C\u628A\u6587\u4EF6\u5939\u9009\u62E9\u7A97\u53E3\u5F39\u5728\u7535\u8111\u5C4F\u5E55\u4E0A\uFF0C\u5BFC\u81F4\u624B\u673A\u7AEF\u4E00\u76F4\u7B49\u5F85\u3001\u65E0\u6CD5\u6DFB\u52A0\u5DE5\u4F5C\u533A\u3002\n\u53EF\u70B9\u51FB\u4E0B\u65B9\u6309\u94AE\u4E00\u952E\u5199\u5165\u7F51\u9875\u5185\u76EE\u5F55\u9009\u62E9\u5668\uFF08browse \u6A21\u5F0F\uFF0C\u5DF2\u5185\u7F6E\u9632\u91CD\u590D\u53BB\u91CD\uFF09\uFF0C\u5F00\u542F\u540E\u624B\u673A\u4E0E\u7535\u8111\u5747\u53EF\u76F4\u63A5\u5728\u7F51\u9875\u5185\u6D4F\u89C8\u5E76\u9009\u62E9\u6587\u4EF6\u5939\u3002",
897
+ dirPickerEnableBtn: "\u26A1 \u4E00\u952E\u5F00\u542F\u7F51\u9875\u76EE\u5F55\u9009\u62E9\u5668",
898
+ dirPickerEnablingBtn: "\u6B63\u5728\u5199\u5165\u914D\u7F6E...",
899
+ dirPickerCopyYamlBtn: "\u{1F4CB} \u590D\u5236 YAML \u914D\u7F6E",
900
+ dirPickerCopiedYamlTip: "\u5DF2\u590D\u5236\u76EE\u5F55\u9009\u62E9\u5668 YAML \u914D\u7F6E\uFF0C\u53EF\u7C98\u8D34\u5230 cordis.patch.yml",
901
+ dirPickerBannerDismiss: "\u6682\u4E0D\u5904\u7406",
902
+ dirPickerRestartBannerTitle: "\u7F51\u9875\u76EE\u5F55\u9009\u62E9\u5668\u914D\u7F6E\u5DF2\u5199\u5165\uFF08\u7B49\u5F85\u751F\u6548\uFF09",
903
+ dirPickerRestartBannerBody: "\u5DF2\u5C06\u7F51\u9875\u5185\u76EE\u5F55\u9009\u62E9\u5668\u914D\u7F6E\u5199\u5165 {path}\u3002\u82E5 DSH \u672A\u81EA\u52A8\u70ED\u91CD\u8F7D\uFF0C\u8BF7\u91CD\u542F dsh web \u5E76\u5237\u65B0\u624B\u673A\u9875\u9762\uFF0C\u5373\u53EF\u5728\u624B\u673A\u7AEF\u6B63\u5E38\u6DFB\u52A0\u5DE5\u4F5C\u533A\u3002",
904
+ dirPickerEnabledToast: "\u5DF2\u6210\u529F\u5199\u5165\u7F51\u9875\u76EE\u5F55\u9009\u62E9\u5668\u914D\u7F6E\uFF01\u82E5\u672A\u81EA\u52A8\u70ED\u91CD\u8F7D\uFF0C\u8BF7\u91CD\u542F dsh web \u751F\u6548\u3002",
905
+ dirPickerDisabledToast: "\u5DF2\u79FB\u9664\u7F51\u9875\u76EE\u5F55\u9009\u62E9\u5668\u914D\u7F6E\uFF08\u6062\u590D PC \u539F\u751F\u5F39\u7A97\uFF09\uFF0C\u91CD\u542F dsh web \u540E\u751F\u6548\u3002",
906
+ dirPickerDisableConfirm: "\u786E\u5B9A\u8981\u6062\u590D\u4E3A PC \u539F\u751F\u7CFB\u7EDF\u76EE\u5F55\u9009\u62E9\u5668\u5417\uFF1F\n\n\u6062\u590D\u540E\uFF0C\u7535\u8111\u7AEF\u5C06\u91CD\u65B0\u5F39\u51FA macOS / Windows \u539F\u751F\u6587\u4EF6\u5939\u7A97\u53E3\uFF0C\u4F46\u624B\u673A\u7AEF\u70B9\u51FB\u300C\u6DFB\u52A0\u5DE5\u4F5C\u533A / \u9009\u62E9\u6587\u4EF6\u5939\u300D\u65F6\u4F1A\u5F39\u5728\u7535\u8111\u684C\u9762\u4E0A\uFF0C\u5BFC\u81F4\u624B\u673A\u7AEF\u65E0\u6CD5\u72EC\u7ACB\u6DFB\u52A0\u5DE5\u4F5C\u533A\u3002",
907
+ dirPickerSectionTitle: "\u{1F4C1} \u79FB\u52A8\u7AEF\u6DFB\u52A0\u5DE5\u4F5C\u533A\uFF08\u76EE\u5F55\u9009\u62E9\u5668\u6A21\u5F0F\uFF09",
908
+ dirPickerSectionDesc: "\u63A7\u5236\u70B9\u51FB\u300C\u6DFB\u52A0\u5DE5\u4F5C\u533A / \u9009\u62E9\u6587\u4EF6\u5939\u300D\u65F6\u4F7F\u7528\u7F51\u9875\u5185\u76EE\u5F55\u6D4F\u89C8\u5668\uFF08\u624B\u673A+\u7535\u8111\u901A\u7528\uFF09\u8FD8\u662F\u7535\u8111\u7CFB\u7EDF\u539F\u751F\u5F39\u7A97\uFF08\u4EC5\u7535\u8111\u53EF\u7528\uFF09\u3002\u63D2\u4EF6\u5728\u5199\u5165 cordis.patch.yml \u65F6\u4F1A\u81EA\u52A8\u6267\u884C\u4E25\u683C\u5E42\u7B49\u53BB\u91CD\u3002",
909
+ dirPickerStatusActive: "\u{1F7E2} \u5DF2\u542F\u7528\u7F51\u9875\u5185\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u624B\u673A\u7AEF\u53EF\u6B63\u5E38\u6DFB\u52A0\u5DE5\u4F5C\u533A\uFF09",
910
+ dirPickerStatusPendingRestart: "\u23F3 \u5DF2\u5199\u5165\u914D\u7F6E\uFF08\u91CD\u542F DSH \u6216\u70ED\u91CD\u8F7D\u540E\u751F\u6548\uFF09",
911
+ dirPickerStatusNativeWarn: "\u26A0\uFE0F \u5F53\u524D\u4E3A\u7CFB\u7EDF\u539F\u751F\u5F39\u7A97\uFF08\u624B\u673A\u7AEF\u65E0\u6CD5\u9009\u62E9\u6587\u4EF6\u5939\uFF09",
912
+ dirPickerRestoreNativeBtn: "\u{1F504} \u6062\u590D PC \u539F\u751F\u76EE\u5F55\u9009\u62E9\u5668",
913
+ dirPickerPatchFileLabel: "\u76EE\u6807\u914D\u7F6E\u6587\u4EF6\uFF1A",
844
914
  // Toast 提示
845
915
  toastDeviceConnected: "\u{1F389} \u65B0\u8BBE\u5907\u63A5\u5165\u6210\u529F\uFF1A{name} ({ip})",
846
916
  toastDeviceOnline: "\u{1F4F6} \u8BBE\u5907\u91CD\u65B0\u4E0A\u7EBF\uFF1A{name} ({ip})",
@@ -952,6 +1022,30 @@ var translations = {
952
1022
  saveConfigSuccessToast: "Security policy successfully saved and synced to settings.json!",
953
1023
  restoreDefaultsSuccessToast: "Default security policies restored and applied immediately!",
954
1024
  saveConfigFailTip: "Parameters must be valid integers greater than 0",
1025
+ // Card 9: Access Proxy Configuration
1026
+ proxyConfigCardTitle: "Access Proxy Configuration",
1027
+ proxyConfigCardDesc: "Configure the access mode, proxy listener, and trusted hosts. Restart DSH after saving.",
1028
+ proxyModeLabel: "Access mode",
1029
+ proxyModeHint: "proxy: core stays on loopback and the plugin forwards; direct: no proxy, access the core directly",
1030
+ proxyModeProxy: "Secure access proxy (proxy)",
1031
+ proxyModeDirect: "Direct core (direct)",
1032
+ proxyPortLabel: "Proxy port",
1033
+ proxyPortHint: "Default 3081; automatically increments if occupied",
1034
+ proxyHostLabel: "Proxy listen address",
1035
+ proxyHostHint: "0.0.0.0 means all interfaces; 127.0.0.1 means local only",
1036
+ proxyTrustedHostsLabel: "Trusted hosts",
1037
+ proxyTrustedHostsHint: "One per line, e.g. xxx.tailnet.ts.net; custom reverse-proxy domains must be listed",
1038
+ proxyRestartNote: "Restart DSH after saving to rebind the listen address/port.",
1039
+ proxySaveBtn: "\u{1F4BE} Save Access Config",
1040
+ proxySavingBtn: "Saving...",
1041
+ proxyResetBtn: "\u{1F504} Restore Defaults",
1042
+ proxySaveConfirm: "Save the access configuration? A DSH restart is required.",
1043
+ proxyResetConfirm: "Restore defaults (proxy, 3081, 0.0.0.0, no trusted hosts)?",
1044
+ proxySavedToast: "Access configuration saved; restart DSH to apply.",
1045
+ proxyResetToast: "Default access configuration restored; restart DSH to apply.",
1046
+ proxySaveFailTip: "Check the port, listen address, and trusted-host format",
1047
+ proxyCurrentRuntime: "Current runtime",
1048
+ proxyNotReady: "Secure access proxy is not ready. Start the proxy first.",
955
1049
  // Card 8: Mobile Style Snippets (style mini-plugin)
956
1050
  tabAccess: "Access",
957
1051
  tabDevices: "Devices & Security",
@@ -995,6 +1089,26 @@ var translations = {
995
1089
  conflictReportText: '[DSH plugin conflict diagnosis]\nSymptom: the dsh-remote-mobile settings panel shows "Another remote-access plugin detected".\nCause: another remote/Web access plugin registered the shared Cordis service remoteWebUiPairing first; dsh-remote-mobile yielded it to keep startup safe, so both remote features now run in parallel.\nConflicting plugin package: {plugin}\nConflicting loader entry id: {entryId}\nFix (choose ONE):\nOption A (keep dsh-remote-mobile, disable the other):\n1. Open ~/.dsh/profiles/web/cordis.patch.yml\n2. Append these two lines at the end (mind YAML indentation, two spaces before disabled):\n- id: {entryId}\n disabled: true\n3. Stop the running dsh web process, then run dsh web --no-open again\nOption B (keep the other, remove dsh-remote-mobile):\nRun dsh plugin --profile web remove dsh-remote-mobile and restart dsh web',
996
1090
  conflictReportNoEntryIdText: '[DSH plugin conflict diagnosis]\nSymptom: the dsh-remote-mobile settings panel shows "Another remote-access plugin detected".\nCause: another remote/Web access plugin registered the shared Cordis service remoteWebUiPairing first; dsh-remote-mobile yielded it to keep startup safe, so both remote features now run in parallel.\nConflicting plugin package: {plugin}\nConflicting loader entry id: could not be detected automatically\nFix (choose ONE):\nOption A (keep dsh-remote-mobile, disable the other):\n1. Run dsh --dump-config to list all loader entries and find the entry id of the conflicting plugin\n2. Open ~/.dsh/profiles/web/cordis.patch.yml and append at the end (mind YAML indentation):\n- id: <the conflicting entry id>\n disabled: true\n3. Stop the running dsh web process, then run dsh web --no-open again\nOption B (keep the other, remove dsh-remote-mobile):\nRun dsh plugin --profile web remove dsh-remote-mobile and restart dsh web',
997
1091
  conflictBannerDismiss: "Got it",
1092
+ // Mobile directory picker (Add Workspace / Select Folder) reminder & one-click patch
1093
+ dirPickerBannerTitle: 'Mobile "Add Workspace / Select Folder" Limitation',
1094
+ dirPickerBannerBody: "Because the DSH core stays on loopback (127.0.0.1), the official auto directory picker (dsh-host-directory-picker-auto) mistakes mobile requests via proxy 3081 for local desktop sessions and opens the native OS folder dialog on your PC screen \u2014 leaving your phone stuck waiting and unable to add workspaces.\nClick below to enable the in-app web directory browser (browse mode, with strict deduplication) so both mobile and PC can pick folders directly inside the web UI.",
1095
+ dirPickerEnableBtn: "\u26A1 Enable In-App Directory Browser",
1096
+ dirPickerEnablingBtn: "Applying patch...",
1097
+ dirPickerCopyYamlBtn: "\u{1F4CB} Copy YAML Snippet",
1098
+ dirPickerCopiedYamlTip: "YAML snippet copied! Paste it into cordis.patch.yml",
1099
+ dirPickerBannerDismiss: "Dismiss",
1100
+ dirPickerRestartBannerTitle: "In-App Directory Browser Configured (Pending Reload)",
1101
+ dirPickerRestartBannerBody: "Configuration has been written to {path}. If DSH does not hot-reload automatically, please restart dsh web and refresh your mobile browser to add workspaces on mobile.",
1102
+ dirPickerEnabledToast: "In-app directory browser enabled! Restart dsh web if HMR did not reload automatically.",
1103
+ dirPickerDisabledToast: "Restored PC native directory picker; restart dsh web to apply.",
1104
+ dirPickerDisableConfirm: "Restore the PC native OS folder dialog?\n\nOnce restored, PC will open the native macOS / Windows folder window again, but mobile devices will NOT be able to pick folders or add workspaces.",
1105
+ dirPickerSectionTitle: "\u{1F4C1} Mobile Workspace Directory Picker Mode",
1106
+ dirPickerSectionDesc: "Choose between the in-app web directory browser (works on both mobile and PC) and the native OS folder dialog (PC only). Writing to cordis.patch.yml automatically deduplicates entries.",
1107
+ dirPickerStatusActive: "\u{1F7E2} In-App Web Directory Browser Enabled (Mobile Ready)",
1108
+ dirPickerStatusPendingRestart: "\u23F3 Configured in patch file (Restart DSH or wait for HMR)",
1109
+ dirPickerStatusNativeWarn: "\u26A0\uFE0F Native OS Dialog Active (Mobile cannot add workspaces)",
1110
+ dirPickerRestoreNativeBtn: "\u{1F504} Restore PC Native Picker",
1111
+ dirPickerPatchFileLabel: "Target patch file: ",
998
1112
  // Toast
999
1113
  toastDeviceConnected: "\u{1F389} New device connected: {name} ({ip})",
1000
1114
  toastDeviceOnline: "\u{1F4F6} Device reconnected: {name} ({ip})",
@@ -1065,7 +1179,10 @@ var import_jsx_runtime = require("react/jsx-runtime");
1065
1179
  function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1066
1180
  const tsHost = status.tailscaleIp || "100.x.y.z";
1067
1181
  const lanHost = status.lanIp || "127.0.0.1";
1068
- const port = status && status.gateway && status.gateway.activePort || typeof window !== "undefined" && window.location.port || "3080";
1182
+ const port = resolveAccessPort(status);
1183
+ const portReady = port !== null;
1184
+ const tailscaleAddress = portReady ? `http://${tsHost}:${port}` : null;
1185
+ const lanAddress = portReady ? `http://${lanHost}:${port}` : null;
1069
1186
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1070
1187
  "div",
1071
1188
  {
@@ -1099,7 +1216,7 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1099
1216
  children: t("netCardDesc", lang)
1100
1217
  }
1101
1218
  ),
1102
- status?.gateway?.mode === "gateway" && status.gateway.running === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1219
+ status?.proxy?.mode === "proxy" && status.proxy.running === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1103
1220
  "div",
1104
1221
  {
1105
1222
  style: {
@@ -1116,9 +1233,9 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1116
1233
  children: [
1117
1234
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
1118
1235
  "\u26A0\uFE0F ",
1119
- lang === "en" ? "Gateway Service Not Ready" : "\u5B89\u5168\u53CD\u5411\u4EE3\u7406\u7F51\u5173\u672A\u5C31\u7EEA"
1236
+ lang === "en" ? "Secure Proxy Not Ready" : "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u672A\u5C31\u7EEA"
1120
1237
  ] }),
1121
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: status.gateway.error ? `${lang === "en" ? "Failure reason: " : "\u542F\u52A8\u5931\u8D25\uFF1A"}${status.gateway.error}` : lang === "en" ? "Gateway is initializing or failed to bind port. External devices cannot connect currently." : "\u7F51\u5173\u6B63\u5728\u542F\u52A8\u6216\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\uFF0C\u5916\u90E8\u624B\u673A\u8BBE\u5907\u5F53\u524D\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u3002" })
1238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: status.proxy.error ? `${lang === "en" ? "Failure reason: " : "\u542F\u52A8\u5931\u8D25\uFF1A"}${status.proxy.error}` : lang === "en" ? "Secure proxy is initializing or failed to bind port. External devices cannot connect currently." : "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u6B63\u5728\u542F\u52A8\u6216\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\uFF0C\u5916\u90E8\u624B\u673A\u8BBE\u5907\u5F53\u524D\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u3002" })
1122
1239
  ]
1123
1240
  }
1124
1241
  ) : null,
@@ -1260,7 +1377,7 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1260
1377
  style: {
1261
1378
  fontSize: "13px",
1262
1379
  fontFamily: "monospace",
1263
- color: "var(--dsw-alias-brand-primary, #3b82f6)",
1380
+ color: portReady ? "var(--dsw-alias-brand-primary, #3b82f6)" : "var(--dsw-alias-label-error, #ef4444)",
1264
1381
  display: "flex",
1265
1382
  alignItems: "center",
1266
1383
  justifyContent: "space-between",
@@ -1281,22 +1398,24 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1281
1398
  flex: 1,
1282
1399
  minWidth: 0
1283
1400
  },
1284
- children: `http://${tsHost}:${port}`
1401
+ children: tailscaleAddress || t("proxyNotReady", lang)
1285
1402
  }
1286
1403
  ),
1287
1404
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1288
1405
  "button",
1289
1406
  {
1290
1407
  type: "button",
1291
- onClick: () => copyText(`http://${tsHost}:${port}`, t("copiedTip", lang)),
1408
+ disabled: !portReady,
1409
+ onClick: () => tailscaleAddress && copyText(tailscaleAddress, t("copiedTip", lang)),
1292
1410
  style: {
1293
1411
  padding: "3px 10px",
1294
1412
  background: "var(--dsw-alias-bg-layer-3, rgba(255, 255, 255, 0.08))",
1295
1413
  border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
1296
1414
  borderRadius: "4px",
1297
1415
  fontSize: "11px",
1298
- cursor: "pointer",
1416
+ cursor: portReady ? "pointer" : "not-allowed",
1299
1417
  color: "var(--dsw-alias-label-secondary, inherit)",
1418
+ opacity: portReady ? 1 : 0.4,
1300
1419
  whiteSpace: "nowrap",
1301
1420
  flexShrink: 0
1302
1421
  },
@@ -1446,7 +1565,7 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1446
1565
  style: {
1447
1566
  fontSize: "13px",
1448
1567
  fontFamily: "monospace",
1449
- color: "var(--dsw-alias-brand-primary, #3b82f6)",
1568
+ color: portReady ? "var(--dsw-alias-brand-primary, #3b82f6)" : "var(--dsw-alias-label-error, #ef4444)",
1450
1569
  display: "flex",
1451
1570
  alignItems: "center",
1452
1571
  justifyContent: "space-between",
@@ -1467,22 +1586,24 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
1467
1586
  flex: 1,
1468
1587
  minWidth: 0
1469
1588
  },
1470
- children: `http://${lanHost}:${port}`
1589
+ children: lanAddress || t("proxyNotReady", lang)
1471
1590
  }
1472
1591
  ),
1473
1592
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1474
1593
  "button",
1475
1594
  {
1476
1595
  type: "button",
1477
- onClick: () => copyText(`http://${lanHost}:${port}`, t("copiedTip", lang)),
1596
+ disabled: !portReady,
1597
+ onClick: () => lanAddress && copyText(lanAddress, t("copiedTip", lang)),
1478
1598
  style: {
1479
1599
  padding: "3px 10px",
1480
1600
  background: "var(--dsw-alias-bg-layer-3, rgba(255, 255, 255, 0.08))",
1481
1601
  border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
1482
1602
  borderRadius: "4px",
1483
1603
  fontSize: "11px",
1484
- cursor: "pointer",
1604
+ cursor: portReady ? "pointer" : "not-allowed",
1485
1605
  color: "var(--dsw-alias-label-secondary, inherit)",
1606
+ opacity: portReady ? 1 : 0.4,
1486
1607
  whiteSpace: "nowrap",
1487
1608
  flexShrink: 0
1488
1609
  },
@@ -1591,10 +1712,12 @@ function QrPairingCard({
1591
1712
  copyDirectLink,
1592
1713
  lang
1593
1714
  }) {
1594
- const port = status && status.gateway && status.gateway.activePort || typeof window !== "undefined" && window.location.port || "3080";
1715
+ const port = resolveAccessPort(status);
1716
+ const portReady = port !== null;
1717
+ const proxyUnavailable = status?.proxy?.mode === "proxy" && status.proxy.running === false;
1595
1718
  const isExpired = timeLeft <= 0;
1596
1719
  const currentHost = selectedTab === "lan" && status.lanIp ? status.lanIp : status.tailscaleIp || status.lanIp || typeof window !== "undefined" && window.location.hostname || "127.0.0.1";
1597
- const directLink = `http://${currentHost}:${port}/auth?token=${status.code || ""}`;
1720
+ const directLink = portReady ? buildDirectLink(currentHost, port, status.code || "") : "";
1598
1721
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1599
1722
  "div",
1600
1723
  {
@@ -1629,7 +1752,7 @@ function QrPairingCard({
1629
1752
  children: t("pairingCardDesc", lang)
1630
1753
  }
1631
1754
  ),
1632
- status?.gateway?.mode === "gateway" && status.gateway.running === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1755
+ status?.proxy?.mode === "proxy" && status.proxy.running === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1633
1756
  "div",
1634
1757
  {
1635
1758
  style: {
@@ -1646,14 +1769,34 @@ function QrPairingCard({
1646
1769
  children: [
1647
1770
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
1648
1771
  "\u26A0\uFE0F ",
1649
- lang === "en" ? "Standalone Gateway Error" : "\u5B89\u5168\u53CD\u5411\u4EE3\u7406\u7F51\u5173\u672A\u5C31\u7EEA"
1772
+ lang === "en" ? "Secure Proxy Error" : "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u672A\u5C31\u7EEA"
1650
1773
  ] }),
1651
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "4px", fontSize: "12px" }, children: status.gateway.error ? `${lang === "en" ? "Failure reason: " : "\u542F\u52A8\u5931\u8D25\uFF1A"}${status.gateway.error}` : lang === "en" ? "Gateway is initializing or port binding failed. External devices cannot connect currently." : "\u7F51\u5173\u6B63\u5728\u542F\u52A8\u6216\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\uFF0C\u5916\u90E8\u624B\u673A\u8BBE\u5907\u6682\u65F6\u65E0\u6CD5\u626B\u7801\u8FDE\u63A5\u3002" }),
1652
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "4px", fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)" }, children: lang === "en" ? "Tip: Check if port 3081 is in use or inspect background logs." : "\u6392\u67E5\u5EFA\u8BAE\uFF1A\u8BF7\u68C0\u67E5 3081 \u7AEF\u53E3\u662F\u5426\u88AB\u5176\u4ED6\u8FDB\u7A0B\u5360\u7528\uFF0C\u6216\u68C0\u67E5\u540E\u53F0\u7EC8\u7AEF\u65E5\u5FD7\u3002" })
1774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "4px", fontSize: "12px" }, children: status.proxy.error ? `${lang === "en" ? "Failure reason: " : "\u542F\u52A8\u5931\u8D25\uFF1A"}${status.proxy.error}` : lang === "en" ? "Secure proxy is initializing or port binding failed. External devices cannot connect currently." : "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u6B63\u5728\u542F\u52A8\u6216\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\uFF0C\u5916\u90E8\u624B\u673A\u8BBE\u5907\u6682\u65F6\u65E0\u6CD5\u626B\u7801\u8FDE\u63A5\u3002" }),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: "4px", fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)" }, children: lang === "en" ? "Tip: Check whether the secure proxy port (default 3081) is in use or inspect background logs." : "\u6392\u67E5\u5EFA\u8BAE\uFF1A\u8BF7\u68C0\u67E5\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u7AEF\u53E3\uFF08\u9ED8\u8BA4 3081\uFF09\u662F\u5426\u88AB\u5176\u4ED6\u8FDB\u7A0B\u5360\u7528\uFF0C\u6216\u68C0\u67E5\u540E\u53F0\u7EC8\u7AEF\u65E5\u5FD7\u3002" })
1653
1776
  ]
1654
1777
  }
1655
1778
  ) : null,
1656
- showQr && !(status?.gateway?.mode === "gateway" && status.gateway.running === false) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1779
+ !portReady && !status.loading && !proxyUnavailable ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1780
+ "div",
1781
+ {
1782
+ style: {
1783
+ background: "rgba(245, 158, 11, 0.12)",
1784
+ border: "1px solid rgba(245, 158, 11, 0.35)",
1785
+ borderRadius: "8px",
1786
+ padding: "12px 14px",
1787
+ marginBottom: "14px",
1788
+ fontSize: "13px",
1789
+ color: "var(--dsw-alias-label-secondary, inherit)",
1790
+ lineHeight: "1.6",
1791
+ textAlign: "left"
1792
+ },
1793
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
1794
+ "\u26A0\uFE0F ",
1795
+ t("proxyNotReady", lang)
1796
+ ] })
1797
+ }
1798
+ ) : null,
1799
+ showQr && portReady && !proxyUnavailable ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1657
1800
  "div",
1658
1801
  {
1659
1802
  style: {
@@ -1814,30 +1957,30 @@ function QrPairingCard({
1814
1957
  fontSize: "32px",
1815
1958
  fontWeight: "800",
1816
1959
  letterSpacing: showCode ? "6px" : "4px",
1817
- color: status?.gateway?.mode === "gateway" && status.gateway.running === false || isExpired ? "var(--dsw-alias-label-error, #ef4444)" : "var(--dsw-alias-brand-primary, #3b82f6)",
1960
+ color: status?.proxy?.mode === "proxy" && status.proxy.running === false || isExpired ? "var(--dsw-alias-label-error, #ef4444)" : "var(--dsw-alias-brand-primary, #3b82f6)",
1818
1961
  fontFamily: "monospace",
1819
1962
  userSelect: "all"
1820
1963
  },
1821
- children: status?.gateway?.mode === "gateway" && status.gateway.running === false ? lang === "en" ? "DISABLED" : "\u5DF2\u505C\u7528" : isExpired ? lang === "en" ? "EXPIRED" : "\u5DF2\u8FC7\u671F" : showCode ? status.code : "\u2022\u2022\u2022\u2022\u2022\u2022"
1964
+ children: status?.proxy?.mode === "proxy" && status.proxy.running === false ? lang === "en" ? "DISABLED" : "\u5DF2\u505C\u7528" : isExpired ? lang === "en" ? "EXPIRED" : "\u5DF2\u8FC7\u671F" : showCode ? status.code : "\u2022\u2022\u2022\u2022\u2022\u2022"
1822
1965
  }
1823
1966
  ),
1824
1967
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1825
1968
  "button",
1826
1969
  {
1827
1970
  type: "button",
1828
- disabled: status?.gateway?.mode === "gateway" && status.gateway.running === false,
1971
+ disabled: status?.proxy?.mode === "proxy" && status.proxy.running === false,
1829
1972
  onClick: () => setShowCode(!showCode),
1830
1973
  title: showCode ? t("clickToHide", lang) : t("clickToReveal", lang),
1831
1974
  style: {
1832
1975
  background: "transparent",
1833
1976
  border: "none",
1834
1977
  color: "var(--dsw-alias-label-secondary, #888)",
1835
- cursor: status?.gateway?.mode === "gateway" && status.gateway.running === false ? "not-allowed" : "pointer",
1978
+ cursor: status?.proxy?.mode === "proxy" && status.proxy.running === false ? "not-allowed" : "pointer",
1836
1979
  display: "flex",
1837
1980
  alignItems: "center",
1838
1981
  justifyContent: "center",
1839
1982
  padding: "4px",
1840
- opacity: status?.gateway?.mode === "gateway" && status.gateway.running === false ? 0.4 : 1
1983
+ opacity: status?.proxy?.mode === "proxy" && status.proxy.running === false ? 0.4 : 1
1841
1984
  },
1842
1985
  children: showCode ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { style: { width: "20px", height: "20px", fill: "currentColor" }, viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { style: { width: "20px", height: "20px", fill: "currentColor" }, viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" }) })
1843
1986
  }
@@ -1850,7 +1993,7 @@ function QrPairingCard({
1850
1993
  {
1851
1994
  style: {
1852
1995
  fontSize: "12px",
1853
- color: status?.gateway?.mode === "gateway" && status.gateway.running === false || isExpired ? "var(--dsw-alias-label-error, #ef4444)" : "var(--dsw-alias-label-secondary, #888)",
1996
+ color: status?.proxy?.mode === "proxy" && status.proxy.running === false || isExpired ? "var(--dsw-alias-label-error, #ef4444)" : "var(--dsw-alias-label-secondary, #888)",
1854
1997
  margin: "4px 0 16px 0",
1855
1998
  display: "flex",
1856
1999
  alignItems: "center",
@@ -1866,11 +2009,11 @@ function QrPairingCard({
1866
2009
  width: "8px",
1867
2010
  height: "8px",
1868
2011
  borderRadius: "50%",
1869
- background: status?.gateway?.mode === "gateway" && status.gateway.running === false || isExpired ? "#ef4444" : "#10b981"
2012
+ background: status?.proxy?.mode === "proxy" && status.proxy.running === false || isExpired ? "#ef4444" : "#10b981"
1870
2013
  }
1871
2014
  }
1872
2015
  ),
1873
- status?.gateway?.mode === "gateway" && status.gateway.running === false ? `\u26A0\uFE0F ${lang === "en" ? "Gateway Service Error" : "\u7F51\u5173\u670D\u52A1\u5F02\u5E38"}` : isExpired ? `\u26A0\uFE0F ${t("codeExpired", lang)}` : `${t("codeValidUntil", lang)}${formatCountdown(timeLeft, lang)}`
2016
+ status?.proxy?.mode === "proxy" && status.proxy.running === false ? `\u26A0\uFE0F ${lang === "en" ? "Proxy Service Error" : "\u5B89\u5168\u63A5\u5165\u4EE3\u7406\u670D\u52A1\u5F02\u5E38"}` : isExpired ? `\u26A0\uFE0F ${t("codeExpired", lang)}` : `${t("codeValidUntil", lang)}${formatCountdown(timeLeft, lang)}`
1874
2017
  ]
1875
2018
  }
1876
2019
  ),
@@ -1888,7 +2031,7 @@ function QrPairingCard({
1888
2031
  "button",
1889
2032
  {
1890
2033
  type: "button",
1891
- disabled: status?.gateway?.mode === "gateway" && status.gateway.running === false,
2034
+ disabled: proxyUnavailable || !portReady,
1892
2035
  onClick: () => setShowQr(!showQr),
1893
2036
  style: {
1894
2037
  padding: "7px 16px",
@@ -1898,8 +2041,8 @@ function QrPairingCard({
1898
2041
  borderRadius: "8px",
1899
2042
  fontSize: "13px",
1900
2043
  fontWeight: "600",
1901
- cursor: status?.gateway?.mode === "gateway" && status.gateway.running === false ? "not-allowed" : "pointer",
1902
- opacity: status?.gateway?.mode === "gateway" && status.gateway.running === false ? 0.4 : 1,
2044
+ cursor: proxyUnavailable || !portReady ? "not-allowed" : "pointer",
2045
+ opacity: proxyUnavailable || !portReady ? 0.4 : 1,
1903
2046
  whiteSpace: "nowrap",
1904
2047
  flexShrink: 0
1905
2048
  },
@@ -1910,7 +2053,7 @@ function QrPairingCard({
1910
2053
  "button",
1911
2054
  {
1912
2055
  type: "button",
1913
- disabled: status?.gateway?.mode === "gateway" && status.gateway.running === false,
2056
+ disabled: status?.proxy?.mode === "proxy" && status.proxy.running === false,
1914
2057
  onClick: refreshStatusAndCode,
1915
2058
  style: {
1916
2059
  padding: "7px 16px",
@@ -1920,8 +2063,8 @@ function QrPairingCard({
1920
2063
  borderRadius: "8px",
1921
2064
  fontSize: "13px",
1922
2065
  fontWeight: "600",
1923
- cursor: status?.gateway?.mode === "gateway" && status.gateway.running === false ? "not-allowed" : "pointer",
1924
- opacity: status?.gateway?.mode === "gateway" && status.gateway.running === false ? 0.4 : 1,
2066
+ cursor: status?.proxy?.mode === "proxy" && status.proxy.running === false ? "not-allowed" : "pointer",
2067
+ opacity: status?.proxy?.mode === "proxy" && status.proxy.running === false ? 0.4 : 1,
1925
2068
  whiteSpace: "nowrap",
1926
2069
  flexShrink: 0
1927
2070
  },
@@ -3090,21 +3233,365 @@ function ConfigCard({
3090
3233
  );
3091
3234
  }
3092
3235
 
3093
- // src/client/components/StylesCard.jsx
3236
+ // src/client/components/ProxyConfigCard.jsx
3094
3237
  var import_react8 = __toESM(require("react"), 1);
3095
3238
  var import_jsx_runtime8 = require("react/jsx-runtime");
3239
+ function ProxyConfigCard({
3240
+ status,
3241
+ modeInput,
3242
+ setModeInput,
3243
+ proxyPortInput,
3244
+ setProxyPortInput,
3245
+ proxyHostInput,
3246
+ setProxyHostInput,
3247
+ trustedHostsInput,
3248
+ setTrustedHostsInput,
3249
+ saveProxyConfig,
3250
+ resetProxyConfigDefaults,
3251
+ toggleDirectoryPicker,
3252
+ isPatchingDirPicker,
3253
+ copyText,
3254
+ isSaving,
3255
+ lang
3256
+ }) {
3257
+ const isDirect = modeInput === "direct";
3258
+ const dirPicker = status && status.directoryPicker || {};
3259
+ const isBrowseEnabled = Boolean(dirPicker.patchEnabled || dirPicker.runtimeActive);
3260
+ const fieldLabelStyle = {
3261
+ fontSize: "12px",
3262
+ fontWeight: "600",
3263
+ color: "var(--dsw-alias-label-primary, inherit)"
3264
+ };
3265
+ const fieldHintStyle = {
3266
+ fontSize: "11px",
3267
+ color: "var(--dsw-alias-label-tertiary, #888)",
3268
+ lineHeight: 1.5
3269
+ };
3270
+ const inputStyle = {
3271
+ height: "34px",
3272
+ background: "var(--dsw-alias-bg-layer-1, rgba(0,0,0,0.15))",
3273
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
3274
+ borderRadius: "6px",
3275
+ padding: "0 10px",
3276
+ color: "inherit",
3277
+ fontSize: "13px",
3278
+ outline: "none",
3279
+ boxSizing: "border-box",
3280
+ width: "100%"
3281
+ };
3282
+ const fieldBoxStyle = {
3283
+ display: "flex",
3284
+ flexDirection: "column",
3285
+ gap: "6px",
3286
+ background: "var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.06))",
3287
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.12))",
3288
+ borderRadius: "8px",
3289
+ padding: "10px 12px"
3290
+ };
3291
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3292
+ "div",
3293
+ {
3294
+ style: {
3295
+ background: "var(--dsw-alias-bg-layer-3, rgba(128,128,128,0.06))",
3296
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.15))",
3297
+ borderRadius: "12px",
3298
+ padding: "14px",
3299
+ display: "flex",
3300
+ flexDirection: "column",
3301
+ gap: "12px"
3302
+ },
3303
+ children: [
3304
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px 12px", flexWrap: "wrap" }, children: [
3305
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3306
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: "16px" }, children: "\u{1F50C}" }),
3307
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: "15px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: t("proxyConfigCardTitle", lang) })
3308
+ ] }),
3309
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3310
+ "span",
3311
+ {
3312
+ style: {
3313
+ fontSize: "11px",
3314
+ color: "var(--dsw-alias-label-tertiary, #888)",
3315
+ background: "var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.08))",
3316
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.15))",
3317
+ borderRadius: "999px",
3318
+ padding: "3px 8px",
3319
+ whiteSpace: "nowrap"
3320
+ },
3321
+ children: [
3322
+ t("proxyCurrentRuntime", lang),
3323
+ ": ",
3324
+ status?.proxy?.mode || "-",
3325
+ " / ",
3326
+ status?.proxy?.activePort || "-"
3327
+ ]
3328
+ }
3329
+ )
3330
+ ] }),
3331
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)", lineHeight: 1.5 }, children: t("proxyConfigCardDesc", lang) }),
3332
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "12px" }, children: [
3333
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: fieldBoxStyle, children: [
3334
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { style: fieldLabelStyle, children: t("proxyModeLabel", lang) }),
3335
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3336
+ "select",
3337
+ {
3338
+ value: modeInput,
3339
+ onChange: (e) => setModeInput(e.target.value),
3340
+ style: { ...inputStyle, cursor: "pointer" },
3341
+ children: [
3342
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "proxy", children: t("proxyModeProxy", lang) }),
3343
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "direct", children: t("proxyModeDirect", lang) })
3344
+ ]
3345
+ }
3346
+ ),
3347
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: fieldHintStyle, children: t("proxyModeHint", lang) })
3348
+ ] }),
3349
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: fieldBoxStyle, children: [
3350
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { style: fieldLabelStyle, children: t("proxyPortLabel", lang) }),
3351
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3352
+ "input",
3353
+ {
3354
+ type: "number",
3355
+ min: "1",
3356
+ max: "65535",
3357
+ value: proxyPortInput,
3358
+ onChange: (e) => setProxyPortInput(e.target.value),
3359
+ disabled: isDirect,
3360
+ style: { ...inputStyle, opacity: isDirect ? 0.55 : 1 }
3361
+ }
3362
+ ),
3363
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: fieldHintStyle, children: t("proxyPortHint", lang) })
3364
+ ] }),
3365
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: fieldBoxStyle, children: [
3366
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { style: fieldLabelStyle, children: t("proxyHostLabel", lang) }),
3367
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3368
+ "input",
3369
+ {
3370
+ type: "text",
3371
+ value: proxyHostInput,
3372
+ onChange: (e) => setProxyHostInput(e.target.value),
3373
+ placeholder: "0.0.0.0",
3374
+ disabled: isDirect,
3375
+ style: { ...inputStyle, opacity: isDirect ? 0.55 : 1 }
3376
+ }
3377
+ ),
3378
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: fieldHintStyle, children: t("proxyHostHint", lang) })
3379
+ ] })
3380
+ ] }),
3381
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: fieldBoxStyle, children: [
3382
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { style: fieldLabelStyle, children: t("proxyTrustedHostsLabel", lang) }),
3383
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3384
+ "textarea",
3385
+ {
3386
+ value: trustedHostsInput,
3387
+ onChange: (e) => setTrustedHostsInput(e.target.value),
3388
+ placeholder: "xxx.tailnet.ts.net\ndsh.example.com",
3389
+ rows: 3,
3390
+ spellCheck: false,
3391
+ style: {
3392
+ ...inputStyle,
3393
+ height: "auto",
3394
+ minHeight: "70px",
3395
+ padding: "8px 10px",
3396
+ resize: "vertical",
3397
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
3398
+ }
3399
+ }
3400
+ ),
3401
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: fieldHintStyle, children: t("proxyTrustedHostsHint", lang) })
3402
+ ] }),
3403
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontSize: "11px", color: "#f59e0b", lineHeight: 1.5 }, children: [
3404
+ "\u26A0\uFE0F ",
3405
+ t("proxyRestartNote", lang)
3406
+ ] }),
3407
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "flex-end", gap: "10px", flexWrap: "wrap" }, children: [
3408
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3409
+ "button",
3410
+ {
3411
+ type: "button",
3412
+ onClick: resetProxyConfigDefaults,
3413
+ disabled: isSaving,
3414
+ style: {
3415
+ padding: "6px 14px",
3416
+ background: "transparent",
3417
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
3418
+ color: "var(--dsw-alias-label-secondary, inherit)",
3419
+ borderRadius: "6px",
3420
+ fontSize: "12px",
3421
+ cursor: isSaving ? "not-allowed" : "pointer",
3422
+ opacity: isSaving ? 0.6 : 1,
3423
+ whiteSpace: "nowrap"
3424
+ },
3425
+ children: t("proxyResetBtn", lang)
3426
+ }
3427
+ ),
3428
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3429
+ "button",
3430
+ {
3431
+ type: "button",
3432
+ onClick: saveProxyConfig,
3433
+ disabled: isSaving,
3434
+ style: {
3435
+ padding: "6px 18px",
3436
+ background: "#2563eb",
3437
+ border: "none",
3438
+ color: "#ffffff",
3439
+ borderRadius: "6px",
3440
+ fontSize: "12px",
3441
+ fontWeight: "600",
3442
+ cursor: isSaving ? "not-allowed" : "pointer",
3443
+ opacity: isSaving ? 0.7 : 1,
3444
+ whiteSpace: "nowrap",
3445
+ boxShadow: "0 2px 6px rgba(37, 99, 235, 0.3)"
3446
+ },
3447
+ children: isSaving ? t("proxySavingBtn", lang) : t("proxySaveBtn", lang)
3448
+ }
3449
+ )
3450
+ ] }),
3451
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3452
+ "div",
3453
+ {
3454
+ style: {
3455
+ ...fieldBoxStyle,
3456
+ marginTop: "4px",
3457
+ border: isBrowseEnabled ? "1px solid rgba(16, 185, 129, 0.3)" : "1px solid rgba(245, 158, 11, 0.35)",
3458
+ background: isBrowseEnabled ? "rgba(16, 185, 129, 0.06)" : "rgba(245, 158, 11, 0.08)"
3459
+ },
3460
+ children: [
3461
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3462
+ "div",
3463
+ {
3464
+ style: {
3465
+ display: "flex",
3466
+ alignItems: "center",
3467
+ justifyContent: "space-between",
3468
+ gap: "8px",
3469
+ flexWrap: "wrap"
3470
+ },
3471
+ children: [
3472
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: fieldLabelStyle, children: t("dirPickerSectionTitle", lang) }),
3473
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3474
+ "span",
3475
+ {
3476
+ style: {
3477
+ fontSize: "11px",
3478
+ fontWeight: "600",
3479
+ color: isBrowseEnabled ? "#10b981" : "#f59e0b"
3480
+ },
3481
+ children: dirPicker.restartRequired ? t("dirPickerStatusPendingRestart", lang) : isBrowseEnabled ? t("dirPickerStatusActive", lang) : t("dirPickerStatusNativeWarn", lang)
3482
+ }
3483
+ )
3484
+ ]
3485
+ }
3486
+ ),
3487
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: fieldHintStyle, children: t("dirPickerSectionDesc", lang) }),
3488
+ dirPicker.patchFilePath ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3489
+ "div",
3490
+ {
3491
+ style: {
3492
+ fontSize: "11px",
3493
+ color: "var(--dsw-alias-label-tertiary, #888)",
3494
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3495
+ wordBreak: "break-all"
3496
+ },
3497
+ children: [
3498
+ t("dirPickerPatchFileLabel", lang),
3499
+ dirPicker.patchFilePath
3500
+ ]
3501
+ }
3502
+ ) : null,
3503
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3504
+ "div",
3505
+ {
3506
+ style: {
3507
+ display: "flex",
3508
+ alignItems: "center",
3509
+ gap: "8px",
3510
+ flexWrap: "wrap",
3511
+ marginTop: "4px"
3512
+ },
3513
+ children: [
3514
+ typeof toggleDirectoryPicker === "function" ? isBrowseEnabled ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3515
+ "button",
3516
+ {
3517
+ type: "button",
3518
+ disabled: isPatchingDirPicker,
3519
+ onClick: () => toggleDirectoryPicker(false),
3520
+ style: {
3521
+ padding: "5px 12px",
3522
+ background: "transparent",
3523
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.3))",
3524
+ color: "var(--dsw-alias-label-secondary, inherit)",
3525
+ borderRadius: "6px",
3526
+ fontSize: "12px",
3527
+ cursor: isPatchingDirPicker ? "not-allowed" : "pointer",
3528
+ opacity: isPatchingDirPicker ? 0.6 : 1
3529
+ },
3530
+ children: t("dirPickerRestoreNativeBtn", lang)
3531
+ }
3532
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3533
+ "button",
3534
+ {
3535
+ type: "button",
3536
+ disabled: isPatchingDirPicker,
3537
+ onClick: () => toggleDirectoryPicker(true),
3538
+ style: {
3539
+ padding: "5px 12px",
3540
+ background: "#2563eb",
3541
+ border: "none",
3542
+ color: "#ffffff",
3543
+ borderRadius: "6px",
3544
+ fontSize: "12px",
3545
+ fontWeight: "600",
3546
+ cursor: isPatchingDirPicker ? "not-allowed" : "pointer",
3547
+ opacity: isPatchingDirPicker ? 0.7 : 1
3548
+ },
3549
+ children: isPatchingDirPicker ? t("dirPickerEnablingBtn", lang) : t("dirPickerEnableBtn", lang)
3550
+ }
3551
+ ) : null,
3552
+ typeof copyText === "function" && dirPicker.yamlSnippet ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3553
+ "button",
3554
+ {
3555
+ type: "button",
3556
+ onClick: () => copyText(dirPicker.yamlSnippet, t("dirPickerCopiedYamlTip", lang)),
3557
+ style: {
3558
+ padding: "5px 10px",
3559
+ background: "transparent",
3560
+ border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
3561
+ color: "var(--dsw-alias-label-secondary, inherit)",
3562
+ borderRadius: "6px",
3563
+ fontSize: "12px",
3564
+ cursor: "pointer"
3565
+ },
3566
+ children: t("dirPickerCopyYamlBtn", lang)
3567
+ }
3568
+ ) : null
3569
+ ]
3570
+ }
3571
+ )
3572
+ ]
3573
+ }
3574
+ )
3575
+ ]
3576
+ }
3577
+ );
3578
+ }
3579
+
3580
+ // src/client/components/StylesCard.jsx
3581
+ var import_react9 = __toESM(require("react"), 1);
3582
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3096
3583
  function StylesCard({ lang, showToast }) {
3097
- const [snippets, setSnippets] = (0, import_react8.useState)([]);
3098
- const [loading, setLoading] = (0, import_react8.useState)(true);
3099
- const [editingId, setEditingId] = (0, import_react8.useState)(null);
3100
- const [showForm, setShowForm] = (0, import_react8.useState)(false);
3101
- const [name, setName] = (0, import_react8.useState)("");
3102
- const [css, setCss] = (0, import_react8.useState)("");
3103
- const [pcEnabled, setPcEnabled] = (0, import_react8.useState)(false);
3104
- const [mobileEnabled, setMobileEnabled] = (0, import_react8.useState)(true);
3105
- const [expanded, setExpanded] = (0, import_react8.useState)({});
3106
- const [feedback, setFeedback] = (0, import_react8.useState)("");
3107
- const showFeedback = (0, import_react8.useCallback)((msg, type = "success") => {
3584
+ const [snippets, setSnippets] = (0, import_react9.useState)([]);
3585
+ const [loading, setLoading] = (0, import_react9.useState)(true);
3586
+ const [editingId, setEditingId] = (0, import_react9.useState)(null);
3587
+ const [showForm, setShowForm] = (0, import_react9.useState)(false);
3588
+ const [name, setName] = (0, import_react9.useState)("");
3589
+ const [css, setCss] = (0, import_react9.useState)("");
3590
+ const [pcEnabled, setPcEnabled] = (0, import_react9.useState)(false);
3591
+ const [mobileEnabled, setMobileEnabled] = (0, import_react9.useState)(true);
3592
+ const [expanded, setExpanded] = (0, import_react9.useState)({});
3593
+ const [feedback, setFeedback] = (0, import_react9.useState)("");
3594
+ const showFeedback = (0, import_react9.useCallback)((msg, type = "success") => {
3108
3595
  if (typeof showToast === "function") {
3109
3596
  showToast(msg, type);
3110
3597
  } else {
@@ -3112,7 +3599,7 @@ function StylesCard({ lang, showToast }) {
3112
3599
  setTimeout(() => setFeedback(""), 3500);
3113
3600
  }
3114
3601
  }, [showToast]);
3115
- const load = (0, import_react8.useCallback)(() => {
3602
+ const load = (0, import_react9.useCallback)(() => {
3116
3603
  fetchStyles().then((data) => {
3117
3604
  setSnippets(data.snippets || []);
3118
3605
  setLoading(false);
@@ -3121,7 +3608,7 @@ function StylesCard({ lang, showToast }) {
3121
3608
  showFeedback(t("stylesLoadFail", lang), "danger");
3122
3609
  });
3123
3610
  }, [lang, showFeedback]);
3124
- (0, import_react8.useEffect)(() => {
3611
+ (0, import_react9.useEffect)(() => {
3125
3612
  load();
3126
3613
  }, [load]);
3127
3614
  const toggle = (id, scope, enabled) => {
@@ -3263,7 +3750,7 @@ function StylesCard({ lang, showToast }) {
3263
3750
  color: "var(--dsw-alias-brand-primary, #3b82f6)",
3264
3751
  border: "1px solid rgba(59,130,246,0.3)"
3265
3752
  };
3266
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3753
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3267
3754
  "div",
3268
3755
  {
3269
3756
  style: {
@@ -3276,15 +3763,15 @@ function StylesCard({ lang, showToast }) {
3276
3763
  gap: "14px"
3277
3764
  },
3278
3765
  children: [
3279
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "10px", flexWrap: "wrap" }, children: [
3280
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3281
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: "16px" }, children: "\u{1F3A8}" }),
3282
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: "15px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: t("stylesCardTitle", lang) })
3766
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "10px", flexWrap: "wrap" }, children: [
3767
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
3768
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: "16px" }, children: "\u{1F3A8}" }),
3769
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: "15px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: t("stylesCardTitle", lang) })
3283
3770
  ] }),
3284
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: resetAll, style: miniBtnStyle, children: t("stylesResetAll", lang) })
3771
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: resetAll, style: miniBtnStyle, children: t("stylesResetAll", lang) })
3285
3772
  ] }),
3286
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)", lineHeight: "1.5" }, children: t("stylesCardDesc", lang) }),
3287
- feedback && typeof showToast !== "function" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3773
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)", lineHeight: "1.5" }, children: t("stylesCardDesc", lang) }),
3774
+ feedback && typeof showToast !== "function" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3288
3775
  "div",
3289
3776
  {
3290
3777
  style: {
@@ -3298,15 +3785,15 @@ function StylesCard({ lang, showToast }) {
3298
3785
  children: feedback
3299
3786
  }
3300
3787
  ) : null,
3301
- loading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)" }, children: lang === "en" ? "Loading style snippets..." : "\u6B63\u5728\u52A0\u8F7D\u6837\u5F0F\u7247\u6BB5..." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3302
- snippets.some((s) => s.builtin) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3303
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "13px", fontWeight: "600", color: "var(--dsw-alias-label-secondary, inherit)" }, children: t("stylesPresetsTitle", lang) }),
3304
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: snippets.filter((s) => s.builtin).map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
3305
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "8px", flexWrap: "wrap" }, children: [
3306
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: "1 1 180px", minWidth: 0, maxWidth: "100%" }, children: [
3307
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3308
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: "12px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: s.name }),
3309
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3788
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)" }, children: lang === "en" ? "Loading style snippets..." : "\u6B63\u5728\u52A0\u8F7D\u6837\u5F0F\u7247\u6BB5..." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
3789
+ snippets.some((s) => s.builtin) ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
3790
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "13px", fontWeight: "600", color: "var(--dsw-alias-label-secondary, inherit)" }, children: t("stylesPresetsTitle", lang) }),
3791
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: snippets.filter((s) => s.builtin).map((s) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: rowStyle, children: [
3792
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "8px", flexWrap: "wrap" }, children: [
3793
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { flex: "1 1 180px", minWidth: 0, maxWidth: "100%" }, children: [
3794
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3795
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: "12px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: s.name }),
3796
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3310
3797
  "span",
3311
3798
  {
3312
3799
  style: {
@@ -3320,7 +3807,7 @@ function StylesCard({ lang, showToast }) {
3320
3807
  children: "Preset"
3321
3808
  }
3322
3809
  ),
3323
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3810
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3324
3811
  "span",
3325
3812
  {
3326
3813
  style: {
@@ -3335,11 +3822,11 @@ function StylesCard({ lang, showToast }) {
3335
3822
  }
3336
3823
  )
3337
3824
  ] }),
3338
- s.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)", marginTop: "3px" }, children: s.description }) : null
3825
+ s.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)", marginTop: "3px" }, children: s.description }) : null
3339
3826
  ] }),
3340
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px 8px", flexWrap: "wrap", maxWidth: "100%" }, children: [
3341
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3342
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3827
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px 8px", flexWrap: "wrap", maxWidth: "100%" }, children: [
3828
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3829
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3343
3830
  "input",
3344
3831
  {
3345
3832
  type: "checkbox",
@@ -3351,8 +3838,8 @@ function StylesCard({ lang, showToast }) {
3351
3838
  "\u{1F5A5}\uFE0F ",
3352
3839
  t("stylesPcShort", lang)
3353
3840
  ] }),
3354
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3355
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3841
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3842
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3356
3843
  "input",
3357
3844
  {
3358
3845
  type: "checkbox",
@@ -3364,13 +3851,13 @@ function StylesCard({ lang, showToast }) {
3364
3851
  "\u{1F4F1} ",
3365
3852
  t("stylesMobileShort", lang)
3366
3853
  ] }),
3367
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "inline-flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3368
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => toggleExpand(s.id), style: miniBtnStyle, children: expanded[s.id] ? t("stylesHideCss", lang) : t("stylesViewCss", lang) }),
3369
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => copyCss(s.css), style: miniBtnStyle, children: t("stylesCopyCss", lang) })
3854
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "inline-flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3855
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: () => toggleExpand(s.id), style: miniBtnStyle, children: expanded[s.id] ? t("stylesHideCss", lang) : t("stylesViewCss", lang) }),
3856
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: () => copyCss(s.css), style: miniBtnStyle, children: t("stylesCopyCss", lang) })
3370
3857
  ] })
3371
3858
  ] })
3372
3859
  ] }),
3373
- expanded[s.id] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3860
+ expanded[s.id] ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3374
3861
  "pre",
3375
3862
  {
3376
3863
  style: {
@@ -3393,8 +3880,8 @@ function StylesCard({ lang, showToast }) {
3393
3880
  ) : null
3394
3881
  ] }, s.id)) })
3395
3882
  ] }) : null,
3396
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "13px", fontWeight: "600", color: "var(--dsw-alias-label-secondary, inherit)", marginTop: "4px" }, children: t("stylesCustomTitle", lang) }),
3397
- !showForm ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3883
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "13px", fontWeight: "600", color: "var(--dsw-alias-label-secondary, inherit)", marginTop: "4px" }, children: t("stylesCustomTitle", lang) }),
3884
+ !showForm ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3398
3885
  "button",
3399
3886
  {
3400
3887
  type: "button",
@@ -3412,7 +3899,7 @@ function StylesCard({ lang, showToast }) {
3412
3899
  },
3413
3900
  children: t("stylesAddNew", lang)
3414
3901
  }
3415
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3902
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3416
3903
  "div",
3417
3904
  {
3418
3905
  style: {
@@ -3425,8 +3912,8 @@ function StylesCard({ lang, showToast }) {
3425
3912
  padding: "12px"
3426
3913
  },
3427
3914
  children: [
3428
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: [
3429
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3915
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: [
3916
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3430
3917
  "input",
3431
3918
  {
3432
3919
  type: "text",
@@ -3446,8 +3933,8 @@ function StylesCard({ lang, showToast }) {
3446
3933
  }
3447
3934
  }
3448
3935
  ),
3449
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3450
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3936
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3937
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3451
3938
  "input",
3452
3939
  {
3453
3940
  type: "checkbox",
@@ -3459,8 +3946,8 @@ function StylesCard({ lang, showToast }) {
3459
3946
  "\u{1F5A5}\uFE0F ",
3460
3947
  t("stylesPcShort", lang)
3461
3948
  ] }),
3462
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3463
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3949
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3950
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3464
3951
  "input",
3465
3952
  {
3466
3953
  type: "checkbox",
@@ -3473,7 +3960,7 @@ function StylesCard({ lang, showToast }) {
3473
3960
  t("stylesMobileShort", lang)
3474
3961
  ] })
3475
3962
  ] }),
3476
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3963
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3477
3964
  "textarea",
3478
3965
  {
3479
3966
  value: css,
@@ -3496,20 +3983,20 @@ function StylesCard({ lang, showToast }) {
3496
3983
  }
3497
3984
  }
3498
3985
  ),
3499
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: [
3500
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: save, style: primaryBtnStyle, children: t("stylesSave", lang) }),
3501
- editingId !== null ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: cancelEdit, style: miniBtnStyle, children: t("stylesCancelEdit", lang) }) : null,
3502
- showForm && !editingId ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: cancelEdit, style: miniBtnStyle, children: t("stylesCancelEdit", lang) }) : null
3986
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: [
3987
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: save, style: primaryBtnStyle, children: t("stylesSave", lang) }),
3988
+ editingId !== null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: cancelEdit, style: miniBtnStyle, children: t("stylesCancelEdit", lang) }) : null,
3989
+ showForm && !editingId ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: cancelEdit, style: miniBtnStyle, children: t("stylesCancelEdit", lang) }) : null
3503
3990
  ] })
3504
3991
  ]
3505
3992
  }
3506
3993
  ),
3507
- snippets.filter((s) => !s.builtin).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)" }, children: t("stylesEmptyCustom", lang) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: snippets.filter((s) => !s.builtin).map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
3508
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "8px", flexWrap: "wrap" }, children: [
3509
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: "1 1 180px", minWidth: 0, maxWidth: "100%" }, children: [
3510
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3511
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: "12px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: s.name }),
3512
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3994
+ snippets.filter((s) => !s.builtin).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "12px", color: "var(--dsw-alias-label-tertiary, #888)" }, children: t("stylesEmptyCustom", lang) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: snippets.filter((s) => !s.builtin).map((s) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: rowStyle, children: [
3995
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "8px", flexWrap: "wrap" }, children: [
3996
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { flex: "1 1 180px", minWidth: 0, maxWidth: "100%" }, children: [
3997
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3998
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: "12px", fontWeight: "600", color: "var(--dsw-alias-label-primary, inherit)" }, children: s.name }),
3999
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3513
4000
  "span",
3514
4001
  {
3515
4002
  style: {
@@ -3524,11 +4011,11 @@ function StylesCard({ lang, showToast }) {
3524
4011
  }
3525
4012
  )
3526
4013
  ] }),
3527
- s.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)", marginTop: "3px" }, children: s.description }) : null
4014
+ s.description ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)", marginTop: "3px" }, children: s.description }) : null
3528
4015
  ] }),
3529
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px 8px", flexWrap: "wrap", maxWidth: "100%" }, children: [
3530
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3531
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4016
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px 8px", flexWrap: "wrap", maxWidth: "100%" }, children: [
4017
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
4018
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3532
4019
  "input",
3533
4020
  {
3534
4021
  type: "checkbox",
@@ -3540,8 +4027,8 @@ function StylesCard({ lang, showToast }) {
3540
4027
  "\u{1F5A5}\uFE0F ",
3541
4028
  t("stylesPcShort", lang)
3542
4029
  ] }),
3543
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
3544
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4030
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: "4px", fontSize: "11px", cursor: "pointer", whiteSpace: "nowrap" }, children: [
4031
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3545
4032
  "input",
3546
4033
  {
3547
4034
  type: "checkbox",
@@ -3553,15 +4040,15 @@ function StylesCard({ lang, showToast }) {
3553
4040
  "\u{1F4F1} ",
3554
4041
  t("stylesMobileShort", lang)
3555
4042
  ] }),
3556
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "inline-flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
3557
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => startEdit(s), style: miniBtnStyle, children: t("stylesEditBtn", lang) }),
3558
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => remove(s), style: { ...miniBtnStyle, color: "#ef4444" }, children: t("stylesDeleteBtn", lang) }),
3559
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => toggleExpand(s.id), style: miniBtnStyle, children: expanded[s.id] ? t("stylesHideCss", lang) : t("stylesViewCss", lang) }),
3560
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: () => copyCss(s.css), style: miniBtnStyle, children: t("stylesCopyCss", lang) })
4043
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "inline-flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
4044
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: () => startEdit(s), style: miniBtnStyle, children: t("stylesEditBtn", lang) }),
4045
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: () => remove(s), style: { ...miniBtnStyle, color: "#ef4444" }, children: t("stylesDeleteBtn", lang) }),
4046
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: () => toggleExpand(s.id), style: miniBtnStyle, children: expanded[s.id] ? t("stylesHideCss", lang) : t("stylesViewCss", lang) }),
4047
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", onClick: () => copyCss(s.css), style: miniBtnStyle, children: t("stylesCopyCss", lang) })
3561
4048
  ] })
3562
4049
  ] })
3563
4050
  ] }),
3564
- expanded[s.id] ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4051
+ expanded[s.id] ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3565
4052
  "pre",
3566
4053
  {
3567
4054
  style: {
@@ -3583,7 +4070,7 @@ function StylesCard({ lang, showToast }) {
3583
4070
  }
3584
4071
  ) : null
3585
4072
  ] }, s.id)) }),
3586
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)", lineHeight: "1.6" }, children: t("stylesHint", lang) })
4073
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary, #888)", lineHeight: "1.6" }, children: t("stylesHint", lang) })
3587
4074
  ] })
3588
4075
  ]
3589
4076
  }
@@ -3591,14 +4078,14 @@ function StylesCard({ lang, showToast }) {
3591
4078
  }
3592
4079
 
3593
4080
  // src/client/version.js
3594
- var PLUGIN_VERSION = "v1.10.0";
3595
- var BUILD_TIME = "2026-10-22 13:21:34";
4081
+ var PLUGIN_VERSION = "v1.10.2";
4082
+ var BUILD_TIME = "2026-11-02 23:13:58";
3596
4083
 
3597
4084
  // src/client/components/MainSection.jsx
3598
- var import_jsx_runtime9 = require("react/jsx-runtime");
4085
+ var import_jsx_runtime10 = require("react/jsx-runtime");
3599
4086
  function TailscaleMobileSection(props) {
3600
4087
  const ctx = props && props.ctx || typeof window !== "undefined" && window.__DSH_CLIENT_CTX__ || null;
3601
- const [status, setStatus] = (0, import_react9.useState)({
4088
+ const [status, setStatus] = (0, import_react10.useState)({
3602
4089
  tailscaleIp: "",
3603
4090
  lanIp: "",
3604
4091
  code: "",
@@ -3620,36 +4107,53 @@ function TailscaleMobileSection(props) {
3620
4107
  locale: "zh",
3621
4108
  pairingBridgeMode: "pending",
3622
4109
  pairingBridgeConflict: "",
3623
- pairingBridgeConflictId: ""
4110
+ pairingBridgeConflictId: "",
4111
+ directoryPicker: {
4112
+ patchEnabled: false,
4113
+ patchPartial: false,
4114
+ runtimeActive: false,
4115
+ restartRequired: false,
4116
+ needsWarning: false,
4117
+ patchFilePath: "",
4118
+ yamlSnippet: ""
4119
+ },
4120
+ proxy: null
3624
4121
  });
3625
- const [secretInput, setSecretInput] = (0, import_react9.useState)("");
3626
- const [showSecret, setShowSecret] = (0, import_react9.useState)(false);
3627
- const [showCode, setShowCode] = (0, import_react9.useState)(false);
3628
- const [showQr, setShowQr] = (0, import_react9.useState)(false);
3629
- const [selectedTab, setSelectedTab] = (0, import_react9.useState)("lan");
3630
- const [timeLeft, setTimeLeft] = (0, import_react9.useState)(300);
3631
- const [toast, setToast] = (0, import_react9.useState)(null);
3632
- const [maxVisitsInput, setMaxVisitsInput] = (0, import_react9.useState)("60");
3633
- const [maxFailedInput, setMaxFailedInput] = (0, import_react9.useState)("5");
3634
- const [lockDurationMinsInput, setLockDurationMinsInput] = (0, import_react9.useState)("15");
3635
- const [isSaving, setIsSaving] = (0, import_react9.useState)(false);
3636
- const [showVersionTip, setShowVersionTip] = (0, import_react9.useState)(false);
3637
- const [currentLang, setCurrentLang] = (0, import_react9.useState)(resolveLocale(ctx, status));
3638
- const [npmLatestVersion, setNpmLatestVersion] = (0, import_react9.useState)("");
3639
- const [hasUpdate, setHasUpdate] = (0, import_react9.useState)(false);
3640
- const [conflictBannerDismissed, setConflictBannerDismissed] = (0, import_react9.useState)(false);
3641
- const qrTabTouchedRef = (0, import_react9.useRef)(false);
3642
- const toastTimerRef = (0, import_react9.useRef)(null);
3643
- const showToast = (0, import_react9.useCallback)((message, type = "success", duration = 2500) => {
4122
+ const [secretInput, setSecretInput] = (0, import_react10.useState)("");
4123
+ const [showSecret, setShowSecret] = (0, import_react10.useState)(false);
4124
+ const [showCode, setShowCode] = (0, import_react10.useState)(false);
4125
+ const [showQr, setShowQr] = (0, import_react10.useState)(false);
4126
+ const [selectedTab, setSelectedTab] = (0, import_react10.useState)("lan");
4127
+ const [timeLeft, setTimeLeft] = (0, import_react10.useState)(300);
4128
+ const [toast, setToast] = (0, import_react10.useState)(null);
4129
+ const [maxVisitsInput, setMaxVisitsInput] = (0, import_react10.useState)("60");
4130
+ const [maxFailedInput, setMaxFailedInput] = (0, import_react10.useState)("5");
4131
+ const [lockDurationMinsInput, setLockDurationMinsInput] = (0, import_react10.useState)("15");
4132
+ const [modeInput, setModeInput] = (0, import_react10.useState)("proxy");
4133
+ const [proxyPortInput, setProxyPortInput] = (0, import_react10.useState)("3081");
4134
+ const [proxyHostInput, setProxyHostInput] = (0, import_react10.useState)("0.0.0.0");
4135
+ const [trustedHostsInput, setTrustedHostsInput] = (0, import_react10.useState)("");
4136
+ const proxyConfigLoadedRef = (0, import_react10.useRef)(false);
4137
+ const [isSaving, setIsSaving] = (0, import_react10.useState)(false);
4138
+ const [showVersionTip, setShowVersionTip] = (0, import_react10.useState)(false);
4139
+ const [currentLang, setCurrentLang] = (0, import_react10.useState)(resolveLocale(ctx, status));
4140
+ const [npmLatestVersion, setNpmLatestVersion] = (0, import_react10.useState)("");
4141
+ const [hasUpdate, setHasUpdate] = (0, import_react10.useState)(false);
4142
+ const [conflictBannerDismissed, setConflictBannerDismissed] = (0, import_react10.useState)(false);
4143
+ const [dirPickerBannerDismissed, setDirPickerBannerDismissed] = (0, import_react10.useState)(false);
4144
+ const [isPatchingDirPicker, setIsPatchingDirPicker] = (0, import_react10.useState)(false);
4145
+ const qrTabTouchedRef = (0, import_react10.useRef)(false);
4146
+ const toastTimerRef = (0, import_react10.useRef)(null);
4147
+ const showToast = (0, import_react10.useCallback)((message, type = "success", duration = 2500) => {
3644
4148
  if (toastTimerRef.current) clearTimeout(toastTimerRef.current);
3645
4149
  setToast({ message, type });
3646
4150
  toastTimerRef.current = setTimeout(() => setToast(null), duration);
3647
4151
  }, []);
3648
- const updateQrTab = (0, import_react9.useCallback)((tab) => {
4152
+ const updateQrTab = (0, import_react10.useCallback)((tab) => {
3649
4153
  qrTabTouchedRef.current = true;
3650
4154
  setSelectedTab(tab);
3651
4155
  }, []);
3652
- const [activeTab, setActiveTab] = (0, import_react9.useState)(() => {
4156
+ const [activeTab, setActiveTab] = (0, import_react10.useState)(() => {
3653
4157
  try {
3654
4158
  const saved = localStorage.getItem("dsh-rm-settings-tab");
3655
4159
  if (saved === "access" || saved === "devices" || saved === "style" || saved === "storage") return saved;
@@ -3664,7 +4168,7 @@ function TailscaleMobileSection(props) {
3664
4168
  } catch (e) {
3665
4169
  }
3666
4170
  };
3667
- const refreshStatusOnly = (0, import_react9.useCallback)(() => {
4171
+ const refreshStatusOnly = (0, import_react10.useCallback)(() => {
3668
4172
  fetchStatus().then((data) => {
3669
4173
  if (data.tailscaleIp && !qrTabTouchedRef.current) {
3670
4174
  setSelectedTab("tailscale");
@@ -3676,6 +4180,14 @@ function TailscaleMobileSection(props) {
3676
4180
  if (data.locale) {
3677
4181
  setCurrentLang(resolveLocale(ctx, data));
3678
4182
  }
4183
+ if (!proxyConfigLoadedRef.current && data.proxy) {
4184
+ setModeInput(data.proxy.configuredMode === "direct" ? "direct" : "proxy");
4185
+ setProxyPortInput(String(data.proxy.proxyPort || 3081));
4186
+ setProxyHostInput(data.proxy.proxyHost || "0.0.0.0");
4187
+ setTrustedHostsInput(Array.isArray(data.proxy.trustedHosts) ? data.proxy.trustedHosts.join("\n") : "");
4188
+ proxyConfigLoadedRef.current = true;
4189
+ }
4190
+ const dp = data.directoryPicker || {};
3679
4191
  setStatus((prev) => ({
3680
4192
  ...prev,
3681
4193
  tailscaleIp: data.tailscaleIp || "",
@@ -3696,12 +4208,22 @@ function TailscaleMobileSection(props) {
3696
4208
  pairingBridgeMode: data.pairingBridgeMode || "pending",
3697
4209
  pairingBridgeConflict: data.pairingBridgeConflict || "",
3698
4210
  pairingBridgeConflictId: data.pairingBridgeConflictId || "",
4211
+ directoryPicker: {
4212
+ patchEnabled: Boolean(dp.patchEnabled),
4213
+ patchPartial: Boolean(dp.patchPartial),
4214
+ runtimeActive: Boolean(dp.runtimeActive),
4215
+ restartRequired: Boolean(dp.restartRequired),
4216
+ needsWarning: Boolean(dp.needsWarning),
4217
+ patchFilePath: dp.patchFilePath || "",
4218
+ yamlSnippet: dp.yamlSnippet || ""
4219
+ },
4220
+ proxy: data.proxy || null,
3699
4221
  loading: false
3700
4222
  }));
3701
4223
  }).catch(() => {
3702
4224
  });
3703
4225
  }, [ctx]);
3704
- const refreshStatusAndCode = (0, import_react9.useCallback)(() => {
4226
+ const refreshStatusAndCode = (0, import_react10.useCallback)(() => {
3705
4227
  refreshStatusOnly();
3706
4228
  generatePairCode().then((data) => {
3707
4229
  if (data.success) {
@@ -3716,7 +4238,7 @@ function TailscaleMobileSection(props) {
3716
4238
  }).catch(() => {
3717
4239
  });
3718
4240
  }, [refreshStatusOnly]);
3719
- (0, import_react9.useEffect)(() => {
4241
+ (0, import_react10.useEffect)(() => {
3720
4242
  let cancelled = false;
3721
4243
  fetch("https://registry.npmjs.org/dsh-remote-mobile/latest", { cache: "no-store" }).then((res) => res.ok ? res.json() : Promise.reject(new Error("npm registry error"))).then((data) => {
3722
4244
  if (cancelled) return;
@@ -3744,7 +4266,7 @@ function TailscaleMobileSection(props) {
3744
4266
  }
3745
4267
  return 0;
3746
4268
  };
3747
- (0, import_react9.useEffect)(() => {
4269
+ (0, import_react10.useEffect)(() => {
3748
4270
  refreshStatusAndCode();
3749
4271
  const localeUnsubs = [];
3750
4272
  try {
@@ -3812,7 +4334,7 @@ function TailscaleMobileSection(props) {
3812
4334
  }
3813
4335
  };
3814
4336
  }, [refreshStatusAndCode, refreshStatusOnly, currentLang, ctx]);
3815
- (0, import_react9.useEffect)(() => {
4337
+ (0, import_react10.useEffect)(() => {
3816
4338
  if (timeLeft <= 0) return;
3817
4339
  const timer = setInterval(() => {
3818
4340
  setTimeLeft((prev) => {
@@ -3998,14 +4520,118 @@ function TailscaleMobileSection(props) {
3998
4520
  showToast((currentLang === "en" ? "Reset failed: " : "\u91CD\u7F6E\u5931\u8D25\uFF1A") + (err?.message || err), "danger", 3500);
3999
4521
  });
4000
4522
  };
4523
+ const persistProxyConfig = (mode, port, host, hosts) => {
4524
+ setIsSaving(true);
4525
+ updateProxyConfig({
4526
+ mode,
4527
+ proxyPort: port,
4528
+ proxyHost: host,
4529
+ trustedHosts: hosts
4530
+ }).then((data) => {
4531
+ setIsSaving(false);
4532
+ if (data.success) {
4533
+ setStatus((prev) => ({
4534
+ ...prev,
4535
+ proxy: prev.proxy ? {
4536
+ ...prev.proxy,
4537
+ configuredMode: data.mode,
4538
+ proxyPort: data.proxyPort,
4539
+ proxyHost: data.proxyHost,
4540
+ trustedHosts: data.trustedHosts || [],
4541
+ mode: data.activeMode || prev.proxy.mode,
4542
+ activePort: data.activePort || prev.proxy.activePort
4543
+ } : prev.proxy
4544
+ }));
4545
+ refreshStatusOnly();
4546
+ showToast(t("proxySavedToast", currentLang), "success", 5e3);
4547
+ } else {
4548
+ showToast((currentLang === "en" ? "Save failed: " : "\u4FDD\u5B58\u5931\u8D25\uFF1A") + (data.reason || ""), "danger", 3500);
4549
+ }
4550
+ }).catch((err) => {
4551
+ setIsSaving(false);
4552
+ showToast((currentLang === "en" ? "Save failed: " : "\u4FDD\u5B58\u5931\u8D25\uFF1A") + (err?.message || err), "danger", 3500);
4553
+ });
4554
+ };
4555
+ const saveProxyConfig = () => {
4556
+ const port = parseInt(proxyPortInput, 10);
4557
+ const host = proxyHostInput.trim();
4558
+ const hosts = trustedHostsInput.split(/\r?\n/).map((h) => h.trim()).filter(Boolean);
4559
+ if (!Number.isInteger(port) || port < 1 || port > 65535 || !host) {
4560
+ alert(t("proxySaveFailTip", currentLang));
4561
+ return;
4562
+ }
4563
+ if (!confirm(t("proxySaveConfirm", currentLang))) {
4564
+ return;
4565
+ }
4566
+ persistProxyConfig(modeInput === "direct" ? "direct" : "proxy", port, host, hosts);
4567
+ };
4568
+ const resetProxyConfigDefaults = () => {
4569
+ if (!confirm(t("proxyResetConfirm", currentLang))) {
4570
+ return;
4571
+ }
4572
+ setModeInput("proxy");
4573
+ setProxyPortInput("3081");
4574
+ setProxyHostInput("0.0.0.0");
4575
+ setTrustedHostsInput("");
4576
+ persistProxyConfig("proxy", 3081, "0.0.0.0", []);
4577
+ };
4578
+ const toggleDirectoryPicker = (enable) => {
4579
+ if (!enable) {
4580
+ if (!confirm(t("dirPickerDisableConfirm", currentLang))) {
4581
+ return;
4582
+ }
4583
+ }
4584
+ setIsPatchingDirPicker(true);
4585
+ toggleDirectoryPickerApi(enable).then((res) => {
4586
+ setIsPatchingDirPicker(false);
4587
+ if (res && res.success) {
4588
+ const dp = res.directoryPicker || {};
4589
+ setStatus((prev) => ({
4590
+ ...prev,
4591
+ directoryPicker: {
4592
+ patchEnabled: Boolean(dp.patchEnabled),
4593
+ patchPartial: Boolean(dp.patchPartial),
4594
+ runtimeActive: Boolean(dp.runtimeActive),
4595
+ restartRequired: Boolean(dp.restartRequired),
4596
+ needsWarning: Boolean(dp.needsWarning),
4597
+ patchFilePath: dp.patchFilePath || "",
4598
+ yamlSnippet: dp.yamlSnippet || ""
4599
+ }
4600
+ }));
4601
+ refreshStatusOnly();
4602
+ showToast(
4603
+ t(enable ? "dirPickerEnabledToast" : "dirPickerDisabledToast", currentLang),
4604
+ "success",
4605
+ 4500
4606
+ );
4607
+ } else {
4608
+ showToast(
4609
+ (currentLang === "en" ? "Operation failed: " : "\u64CD\u4F5C\u5931\u8D25\uFF1A") + (res && res.reason || ""),
4610
+ "danger",
4611
+ 3500
4612
+ );
4613
+ }
4614
+ }).catch((err) => {
4615
+ setIsPatchingDirPicker(false);
4616
+ showToast(
4617
+ (currentLang === "en" ? "Operation failed: " : "\u64CD\u4F5C\u5931\u8D25\uFF1A") + (err?.message || err),
4618
+ "danger",
4619
+ 3500
4620
+ );
4621
+ });
4622
+ };
4001
4623
  const copyDirectLink = () => {
4002
4624
  if (timeLeft <= 0) {
4003
4625
  alert(t("codeExpired", currentLang));
4004
4626
  return;
4005
4627
  }
4006
4628
  const effectiveQrIp = selectedTab === "tailscale" && status.tailscaleIp ? status.tailscaleIp : status.lanIp || "127.0.0.1";
4007
- const port = typeof window !== "undefined" && window.location.port || "3080";
4008
- const directLink = `http://${effectiveQrIp}:${port}/auth?token=${status.code || ""}`;
4629
+ const port = resolveAccessPort(status);
4630
+ if (port === null) {
4631
+ showToast(t("proxyNotReady", currentLang), "danger", 3e3);
4632
+ return;
4633
+ }
4634
+ const directLink = buildDirectLink(effectiveQrIp, port, status.code || "");
4009
4635
  copyText(directLink, t("copiedTip", currentLang));
4010
4636
  setStatus((prev) => ({ ...prev, copied: true }));
4011
4637
  setTimeout(() => {
@@ -4050,7 +4676,7 @@ function TailscaleMobileSection(props) {
4050
4676
  }
4051
4677
  }
4052
4678
  };
4053
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4679
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4054
4680
  "div",
4055
4681
  {
4056
4682
  style: {
@@ -4062,7 +4688,7 @@ function TailscaleMobileSection(props) {
4062
4688
  position: "relative"
4063
4689
  },
4064
4690
  children: [
4065
- toast ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4691
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4066
4692
  "div",
4067
4693
  {
4068
4694
  style: {
@@ -4087,12 +4713,12 @@ function TailscaleMobileSection(props) {
4087
4713
  pointerEvents: "none"
4088
4714
  },
4089
4715
  children: [
4090
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: toast.type === "danger" ? "\u{1F6A8}" : toast.type === "success" ? "\u2705" : "\u2139\uFE0F" }),
4091
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: toast.message })
4716
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: toast.type === "danger" ? "\u{1F6A8}" : toast.type === "success" ? "\u2705" : "\u2139\uFE0F" }),
4717
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: toast.message })
4092
4718
  ]
4093
4719
  }
4094
4720
  ) : null,
4095
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4721
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4096
4722
  "div",
4097
4723
  {
4098
4724
  style: {
@@ -4104,8 +4730,8 @@ function TailscaleMobileSection(props) {
4104
4730
  marginBottom: "4px"
4105
4731
  },
4106
4732
  children: [
4107
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { minWidth: "220px", flex: 1 }, children: [
4108
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4733
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { minWidth: "220px", flex: 1 }, children: [
4734
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4109
4735
  "div",
4110
4736
  {
4111
4737
  style: {
@@ -4116,7 +4742,7 @@ function TailscaleMobileSection(props) {
4116
4742
  margin: "0 0 6px 0"
4117
4743
  },
4118
4744
  children: [
4119
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4745
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4120
4746
  "h3",
4121
4747
  {
4122
4748
  style: {
@@ -4128,7 +4754,7 @@ function TailscaleMobileSection(props) {
4128
4754
  children: t("title", currentLang)
4129
4755
  }
4130
4756
  ),
4131
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4757
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4132
4758
  "div",
4133
4759
  {
4134
4760
  style: { position: "relative", display: "inline-flex", alignItems: "center" },
@@ -4142,7 +4768,7 @@ function TailscaleMobileSection(props) {
4142
4768
  );
4143
4769
  },
4144
4770
  children: [
4145
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4771
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4146
4772
  "span",
4147
4773
  {
4148
4774
  style: {
@@ -4159,7 +4785,7 @@ function TailscaleMobileSection(props) {
4159
4785
  children: PLUGIN_VERSION
4160
4786
  }
4161
4787
  ),
4162
- showVersionTip ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4788
+ showVersionTip ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4163
4789
  "div",
4164
4790
  {
4165
4791
  style: {
@@ -4187,7 +4813,7 @@ function TailscaleMobileSection(props) {
4187
4813
  ]
4188
4814
  }
4189
4815
  ),
4190
- hasUpdate && npmLatestVersion ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4816
+ hasUpdate && npmLatestVersion ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4191
4817
  "a",
4192
4818
  {
4193
4819
  href: `https://www.npmjs.com/package/dsh-remote-mobile/v/${npmLatestVersion}`,
@@ -4217,7 +4843,7 @@ function TailscaleMobileSection(props) {
4217
4843
  ]
4218
4844
  }
4219
4845
  ),
4220
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4846
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4221
4847
  "div",
4222
4848
  {
4223
4849
  style: {
@@ -4228,7 +4854,7 @@ function TailscaleMobileSection(props) {
4228
4854
  }
4229
4855
  )
4230
4856
  ] }),
4231
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4857
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4232
4858
  "a",
4233
4859
  {
4234
4860
  href: "https://github.com/IceApriler/dsh-remote-mobile",
@@ -4250,15 +4876,15 @@ function TailscaleMobileSection(props) {
4250
4876
  cursor: "pointer"
4251
4877
  },
4252
4878
  children: [
4253
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { style: { width: "16px", height: "16px", fill: "currentColor" }, viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" }) }),
4254
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "dsh-remote-mobile" })
4879
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { style: { width: "16px", height: "16px", fill: "currentColor" }, viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" }) }),
4880
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "dsh-remote-mobile" })
4255
4881
  ]
4256
4882
  }
4257
4883
  )
4258
4884
  ]
4259
4885
  }
4260
4886
  ),
4261
- status.pairingBridgeConflict && !conflictBannerDismissed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4887
+ status.pairingBridgeConflict && !conflictBannerDismissed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4262
4888
  "div",
4263
4889
  {
4264
4890
  style: {
@@ -4271,9 +4897,9 @@ function TailscaleMobileSection(props) {
4271
4897
  background: "rgba(245, 158, 11, 0.12)"
4272
4898
  },
4273
4899
  children: [
4274
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: "16px", lineHeight: "20px" }, children: "\u26A0\uFE0F" }),
4275
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
4276
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4900
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: "16px", lineHeight: "20px" }, children: "\u26A0\uFE0F" }),
4901
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
4902
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4277
4903
  "div",
4278
4904
  {
4279
4905
  style: {
@@ -4285,7 +4911,7 @@ function TailscaleMobileSection(props) {
4285
4911
  children: t("conflictBannerTitle", currentLang)
4286
4912
  }
4287
4913
  ),
4288
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4914
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4289
4915
  "div",
4290
4916
  {
4291
4917
  style: {
@@ -4299,7 +4925,7 @@ function TailscaleMobileSection(props) {
4299
4925
  })
4300
4926
  }
4301
4927
  ),
4302
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4928
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4303
4929
  "button",
4304
4930
  {
4305
4931
  type: "button",
@@ -4331,7 +4957,7 @@ function TailscaleMobileSection(props) {
4331
4957
  }
4332
4958
  )
4333
4959
  ] }),
4334
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4960
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4335
4961
  "button",
4336
4962
  {
4337
4963
  type: "button",
@@ -4357,7 +4983,192 @@ function TailscaleMobileSection(props) {
4357
4983
  ]
4358
4984
  }
4359
4985
  ) : null,
4360
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4986
+ !status.loading && status.directoryPicker.needsWarning && !dirPickerBannerDismissed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4987
+ "div",
4988
+ {
4989
+ style: {
4990
+ display: "flex",
4991
+ alignItems: "flex-start",
4992
+ gap: "10px",
4993
+ padding: "12px 14px",
4994
+ borderRadius: "10px",
4995
+ border: "1px solid rgba(245, 158, 11, 0.45)",
4996
+ background: "rgba(245, 158, 11, 0.12)"
4997
+ },
4998
+ children: [
4999
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: "16px", lineHeight: "20px" }, children: "\u{1F4C1}" }),
5000
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
5001
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5002
+ "div",
5003
+ {
5004
+ style: {
5005
+ fontSize: "13px",
5006
+ fontWeight: "700",
5007
+ marginBottom: "4px",
5008
+ color: "var(--dsw-alias-label-primary, inherit)"
5009
+ },
5010
+ children: t("dirPickerBannerTitle", currentLang)
5011
+ }
5012
+ ),
5013
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5014
+ "div",
5015
+ {
5016
+ style: {
5017
+ fontSize: "12px",
5018
+ lineHeight: 1.6,
5019
+ whiteSpace: "pre-line",
5020
+ color: "var(--dsw-alias-label-secondary, inherit)"
5021
+ },
5022
+ children: t("dirPickerBannerBody", currentLang)
5023
+ }
5024
+ ),
5025
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { marginTop: "10px", display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [
5026
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5027
+ "button",
5028
+ {
5029
+ type: "button",
5030
+ disabled: isPatchingDirPicker,
5031
+ onClick: () => toggleDirectoryPicker(true),
5032
+ style: {
5033
+ display: "inline-flex",
5034
+ alignItems: "center",
5035
+ gap: "6px",
5036
+ padding: "6px 12px",
5037
+ fontSize: "12px",
5038
+ fontWeight: "600",
5039
+ borderRadius: "7px",
5040
+ cursor: isPatchingDirPicker ? "not-allowed" : "pointer",
5041
+ opacity: isPatchingDirPicker ? 0.7 : 1,
5042
+ border: "none",
5043
+ background: "#2563eb",
5044
+ color: "#ffffff",
5045
+ boxShadow: "0 2px 6px rgba(37, 99, 235, 0.3)"
5046
+ },
5047
+ children: isPatchingDirPicker ? t("dirPickerEnablingBtn", currentLang) : t("dirPickerEnableBtn", currentLang)
5048
+ }
5049
+ ),
5050
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5051
+ "button",
5052
+ {
5053
+ type: "button",
5054
+ onClick: () => {
5055
+ const yaml = status.directoryPicker.yamlSnippet || "";
5056
+ if (yaml) {
5057
+ copyText(yaml, t("dirPickerCopiedYamlTip", currentLang));
5058
+ }
5059
+ },
5060
+ style: {
5061
+ display: "inline-flex",
5062
+ alignItems: "center",
5063
+ gap: "6px",
5064
+ padding: "5px 10px",
5065
+ fontSize: "12px",
5066
+ fontWeight: "600",
5067
+ borderRadius: "7px",
5068
+ cursor: "pointer",
5069
+ border: "1px solid rgba(245, 158, 11, 0.55)",
5070
+ background: "rgba(245, 158, 11, 0.18)",
5071
+ color: "var(--dsw-alias-label-primary, inherit)"
5072
+ },
5073
+ children: t("dirPickerCopyYamlBtn", currentLang)
5074
+ }
5075
+ )
5076
+ ] })
5077
+ ] }),
5078
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5079
+ "button",
5080
+ {
5081
+ type: "button",
5082
+ onClick: () => setDirPickerBannerDismissed(true),
5083
+ title: t("dirPickerBannerDismiss", currentLang),
5084
+ style: {
5085
+ flexShrink: 0,
5086
+ width: "24px",
5087
+ height: "24px",
5088
+ display: "inline-flex",
5089
+ alignItems: "center",
5090
+ justifyContent: "center",
5091
+ border: "none",
5092
+ borderRadius: "6px",
5093
+ cursor: "pointer",
5094
+ fontSize: "12px",
5095
+ background: "rgba(128, 128, 128, 0.15)",
5096
+ color: "var(--dsw-alias-label-secondary, inherit)"
5097
+ },
5098
+ children: "\u2715"
5099
+ }
5100
+ )
5101
+ ]
5102
+ }
5103
+ ) : null,
5104
+ !status.loading && status.directoryPicker.patchEnabled && status.directoryPicker.restartRequired && !dirPickerBannerDismissed ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
5105
+ "div",
5106
+ {
5107
+ style: {
5108
+ display: "flex",
5109
+ alignItems: "flex-start",
5110
+ gap: "10px",
5111
+ padding: "12px 14px",
5112
+ borderRadius: "10px",
5113
+ border: "1px solid rgba(59, 130, 246, 0.45)",
5114
+ background: "rgba(59, 130, 246, 0.1)"
5115
+ },
5116
+ children: [
5117
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: "16px", lineHeight: "20px" }, children: "\u2705" }),
5118
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
5119
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5120
+ "div",
5121
+ {
5122
+ style: {
5123
+ fontSize: "13px",
5124
+ fontWeight: "700",
5125
+ marginBottom: "4px",
5126
+ color: "var(--dsw-alias-label-primary, inherit)"
5127
+ },
5128
+ children: t("dirPickerRestartBannerTitle", currentLang)
5129
+ }
5130
+ ),
5131
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5132
+ "div",
5133
+ {
5134
+ style: {
5135
+ fontSize: "12px",
5136
+ lineHeight: 1.6,
5137
+ color: "var(--dsw-alias-label-secondary, inherit)"
5138
+ },
5139
+ children: t("dirPickerRestartBannerBody", currentLang, {
5140
+ path: status.directoryPicker.patchFilePath || "~/.dsh/profiles/web/cordis.patch.yml"
5141
+ })
5142
+ }
5143
+ )
5144
+ ] }),
5145
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5146
+ "button",
5147
+ {
5148
+ type: "button",
5149
+ onClick: () => setDirPickerBannerDismissed(true),
5150
+ title: t("dirPickerBannerDismiss", currentLang),
5151
+ style: {
5152
+ flexShrink: 0,
5153
+ width: "24px",
5154
+ height: "24px",
5155
+ display: "inline-flex",
5156
+ alignItems: "center",
5157
+ justifyContent: "center",
5158
+ border: "none",
5159
+ borderRadius: "6px",
5160
+ cursor: "pointer",
5161
+ fontSize: "12px",
5162
+ background: "rgba(128, 128, 128, 0.15)",
5163
+ color: "var(--dsw-alias-label-secondary, inherit)"
5164
+ },
5165
+ children: "\u2715"
5166
+ }
5167
+ )
5168
+ ]
5169
+ }
5170
+ ) : null,
5171
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4361
5172
  "div",
4362
5173
  {
4363
5174
  style: {
@@ -4378,7 +5189,7 @@ function TailscaleMobileSection(props) {
4378
5189
  { id: "storage", label: t("tabStorage", currentLang) }
4379
5190
  ].map((item) => {
4380
5191
  const isActive = activeTab === item.id;
4381
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5192
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4382
5193
  "button",
4383
5194
  {
4384
5195
  type: "button",
@@ -4405,8 +5216,8 @@ function TailscaleMobileSection(props) {
4405
5216
  })
4406
5217
  }
4407
5218
  ),
4408
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: activeTab === "access" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: [
4409
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5219
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: activeTab === "access" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: [
5220
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4410
5221
  NetworkCard,
4411
5222
  {
4412
5223
  status,
@@ -4416,7 +5227,28 @@ function TailscaleMobileSection(props) {
4416
5227
  lang: currentLang
4417
5228
  }
4418
5229
  ),
4419
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5230
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5231
+ ProxyConfigCard,
5232
+ {
5233
+ status,
5234
+ modeInput,
5235
+ setModeInput,
5236
+ proxyPortInput,
5237
+ setProxyPortInput,
5238
+ proxyHostInput,
5239
+ setProxyHostInput,
5240
+ trustedHostsInput,
5241
+ setTrustedHostsInput,
5242
+ saveProxyConfig,
5243
+ resetProxyConfigDefaults,
5244
+ toggleDirectoryPicker,
5245
+ isPatchingDirPicker,
5246
+ copyText,
5247
+ isSaving,
5248
+ lang: currentLang
5249
+ }
5250
+ ),
5251
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4420
5252
  QrPairingCard,
4421
5253
  {
4422
5254
  status,
@@ -4433,7 +5265,7 @@ function TailscaleMobileSection(props) {
4433
5265
  lang: currentLang
4434
5266
  }
4435
5267
  ),
4436
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5268
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4437
5269
  SecretCard,
4438
5270
  {
4439
5271
  status,
@@ -4447,8 +5279,8 @@ function TailscaleMobileSection(props) {
4447
5279
  }
4448
5280
  )
4449
5281
  ] }),
4450
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: activeTab === "devices" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: [
4451
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5282
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: activeTab === "devices" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: [
5283
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4452
5284
  DeviceListCard,
4453
5285
  {
4454
5286
  status,
@@ -4457,7 +5289,7 @@ function TailscaleMobileSection(props) {
4457
5289
  lang: currentLang
4458
5290
  }
4459
5291
  ),
4460
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5292
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4461
5293
  SecurityCard,
4462
5294
  {
4463
5295
  status,
@@ -4466,7 +5298,7 @@ function TailscaleMobileSection(props) {
4466
5298
  lang: currentLang
4467
5299
  }
4468
5300
  ),
4469
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5301
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4470
5302
  ConfigCard,
4471
5303
  {
4472
5304
  maxVisitsInput,
@@ -4483,8 +5315,8 @@ function TailscaleMobileSection(props) {
4483
5315
  }
4484
5316
  )
4485
5317
  ] }),
4486
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: activeTab === "style" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StylesCard, { lang: currentLang, showToast }) }),
4487
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: activeTab === "storage" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StorageCard, { status, lang: currentLang, copyText }) })
5318
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: activeTab === "style" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StylesCard, { lang: currentLang, showToast }) }),
5319
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: activeTab === "storage" ? "flex" : "none", flexDirection: "column", gap: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StorageCard, { status, lang: currentLang, copyText }) })
4488
5320
  ]
4489
5321
  }
4490
5322
  );