dsh-deeppilot 0.9.13 → 0.9.15

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
@@ -4,14 +4,14 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- //#region \0rolldown/runtime.js
7
+ // \0rolldown/runtime.js
8
8
  var __create = Object.create;
9
9
  var __defProp = Object.defineProperty;
10
10
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
11
  var __getOwnPropNames = Object.getOwnPropertyNames;
12
12
  var __getProtoOf = Object.getPrototypeOf;
13
13
  var __hasOwnProp = Object.prototype.hasOwnProperty;
14
- var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
14
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
15
15
  var __copyProps = (to, from, except, desc) => {
16
16
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
17
17
  key = keys[i];
@@ -26,9 +26,9 @@ window.__ModuleLoader__.load({
26
26
  value: mod,
27
27
  enumerable: true
28
28
  }) : target, mod));
29
- //#endregion
30
29
  let react = require("react");
31
- //#region src/client/snapshot-store.ts
30
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
31
+ // src/client/snapshot-store.ts
32
32
  function createSnapshotStore(initial) {
33
33
  let snapshot = initial;
34
34
  const listeners = /* @__PURE__ */ new Set();
@@ -45,8 +45,7 @@ window.__ModuleLoader__.load({
45
45
  }
46
46
  };
47
47
  }
48
- //#endregion
49
- //#region src/tailscale-version.ts
48
+ // src/tailscale-version.ts
50
49
  /**
51
50
  * 内嵌 helper 上报的 tailscale.com 模块版本。
52
51
  *
@@ -69,8 +68,7 @@ window.__ModuleLoader__.load({
69
68
  function isTailscaleVersion(value) {
70
69
  return typeof value === "string" && value.length <= MAX_TAILSCALE_VERSION_LENGTH && TAILSCALE_VERSION_PATTERN.test(value);
71
70
  }
72
- //#endregion
73
- //#region src/device-auth.ts
71
+ // src/device-auth.ts
74
72
  const DEVICE_SCOPES = [
75
73
  "sessions.read",
76
74
  "prompt.send",
@@ -85,8 +83,7 @@ window.__ModuleLoader__.load({
85
83
  const allowed = new Set(DEVICE_SCOPES);
86
84
  return [...new Set(value.filter((scope) => typeof scope === "string" && allowed.has(scope)))];
87
85
  }
88
- //#endregion
89
- //#region src/report-wire.ts
86
+ // src/report-wire.ts
90
87
  /** The npm package identity both contribution registrations claim. */
91
88
  const REPORT_REMOTE_PACKAGE = "dsh-deeppilot";
92
89
  /** Canonical `<namespace>/<method>` endpoint of the report Remote. */
@@ -464,8 +461,7 @@ window.__ModuleLoader__.load({
464
461
  }
465
462
  ]
466
463
  };
467
- //#endregion
468
- //#region src/client/report-mount.ts
464
+ // src/client/report-mount.ts
469
465
  /** Mount the contribution and resolve the namespace service it installs. */
470
466
  async function mountReportRemote(remote, resolveNamespace) {
471
467
  const dispose = await remote.$mount(REPORT_REMOTE_CONTRIBUTION);
@@ -479,8 +475,7 @@ window.__ModuleLoader__.load({
479
475
  dispose
480
476
  };
481
477
  }
482
- //#endregion
483
- //#region src/client/i18n.ts
478
+ // src/client/i18n.ts
484
479
  /** Single namespace shared with the host's locale registry, so other
485
480
  * host-side modules (if any) can translate the same keys. */
486
481
  const DEEPPILOT_LOCALE_NS = "settings.deeppilot";
@@ -498,11 +493,39 @@ window.__ModuleLoader__.load({
498
493
  */
499
494
  const TABLES = {
500
495
  zh: {
496
+ "local.options": "局域网连接选项",
497
+ "remote.options": "远程连接选项",
498
+ "local.errorDetails": "局域网连接异常,请检查连接选项({message})。",
499
+ "remote.errorDetails": "远程连接异常,请检查授权与连接选项({message})。",
500
+ "pair.add": "添加手机",
501
+ "pair.close": "收起",
502
+ "devices.count": "{count} 台",
503
+ "devices.moreAria": "设备“{name}”的更多操作",
504
+ "tools.title": "工具与信息",
505
+ "diag.reportFailed": "读取连接状态失败",
506
+ "diagnostics.historyCount": "{count} 条历史失败记录",
507
+ "diagnostics.kind.upgrade": "连接请求被拒绝",
508
+ "diagnostics.kind.pair": "配对请求被拒绝",
509
+ "diagnostics.kind.auth": "设备认证失败",
510
+ "diagnostics.kind.opened": "连接已建立",
511
+ "diagnostics.kind.closed": "连接异常断开",
512
+ "diagnostics.kind.error": "连接发生错误",
513
+ "diagnostics.kind.identity": "设备身份异常",
514
+ "diagnostics.kind.storage": "设备信息保存失败",
515
+ "diagnostics.kind.unknown": "连接异常",
516
+ "push.outcome.sent": "已发送",
517
+ "push.outcome.failed": "发送失败",
518
+ "push.outcome.invalid": "设备推送凭据无效",
519
+ "push.environment.development": "开发环境",
520
+ "push.environment.production": "正式环境",
521
+ "push.environment.other": "其他环境({environment})",
522
+ "push.outcome.other": "推送状态({outcome})",
523
+ "push.resultDetail": "推送结果({message})",
501
524
  "nav": "DeepPilot",
502
525
  "meta.title": "DeepPilot",
503
- "meta.intro": "把 iPhone 与这台电脑上的 DeepSeek Harness(DSH)连接起来(协议 v2)。",
526
+ "meta.intro": "在手机上使用这台电脑的 DSH。",
504
527
  "meta.refresh": "刷新",
505
- "master.title": "DeepPilot 连接",
528
+ "master.title": "允许手机连接",
506
529
  "master.loading": "正在读取配置…",
507
530
  "master.on": "已开启:接受手机连接。",
508
531
  "master.off": "已关闭:不接受手机连接。",
@@ -515,11 +538,11 @@ window.__ModuleLoader__.load({
515
538
  "connection.optionsSummary": "连接选项",
516
539
  "pair.description": "选择连接方式并生成一次性配对信息。",
517
540
  "devices.description": "管理已经配对的 iPhone。",
518
- "troubleshooting.summary": "测试与故障排查",
541
+ "troubleshooting.summary": "诊断工具",
519
542
  "troubleshooting.title": "诊断工具",
520
543
  "troubleshooting.description": "仅在排查连接或推送问题时使用。",
521
544
  "troubleshooting.debugTitle": "调试日志",
522
- "troubleshooting.debugDescription": "打开后在 DSH host 的终端输出里打印握手、推送等诊断信息;不会打印配对令牌、APNs token 或消息内容。改动立即生效,无需重启。",
545
+ "troubleshooting.debugDescription": "开启后,新的手机连接会在 DSH 终端输出诊断日志;已连接手机需重连后生效。日志不包含配对码、推送凭据或消息内容。",
523
546
  "diagnostics.title": "连接诊断",
524
547
  "diagnostics.description": "最近的连接失败事件;可一键复制后发给维护者排查。",
525
548
  "diagnostics.recent": "最近失败",
@@ -563,7 +586,7 @@ window.__ModuleLoader__.load({
563
586
  "local.portApplied": "局域网端口已保存,监听器正在重新加载。",
564
587
  "local.portInvalid": "请输入 1024–65535 之间的整数。",
565
588
  "local.portFailed": "局域网端口保存失败:",
566
- "remote.title": "远程连接(Tailscale Funnel)",
589
+ "remote.title": "远程连接",
567
590
  "remote.on": "已配置:内嵌 Funnel 会自动启动并同步状态。",
568
591
  "remote.off": "关闭:仅保留局域网连接。",
569
592
  "remote.openAuth": "打开授权页面",
@@ -611,12 +634,12 @@ window.__ModuleLoader__.load({
611
634
  "panel.tokenRotateWarning": "更换会使当前 Token 立即失效、断开所有手机连接;5 秒内再次点击确认。",
612
635
  "panel.tokenRotated": "新 Token 已生效,旧 Token 已失效;请重新配对所有设备。",
613
636
  "panel.tokenRotateFailed": "更换失败:",
614
- "pair.qrPanelTitle": "扫码添加当前手机",
637
+ "pair.qrPanelTitle": "添加手机",
615
638
  "pair.kind.public": "公网",
616
639
  "pair.kind.lan": "内网",
617
640
  "pair.noAddress": "没有可用地址",
618
641
  "pair.noAddressHelp": "没有可用的局域网或远程入口。请确认局域网连接已启动,或启用远程连接。",
619
- "pair.disabledHelp": "DeepPilot 连接已关闭。先在上方开启连接,再添加手机。",
642
+ "pair.disabledHelp": "请先开启“允许手机连接”,再添加手机。",
620
643
  "pair.qrAlt": "DeepPilot 配对二维码",
621
644
  "pair.qrShow": "显示二维码",
622
645
  "pair.qrHide": "隐藏二维码",
@@ -627,7 +650,7 @@ window.__ModuleLoader__.load({
627
650
  "pair.infoLabel": "配对信息(复制一次,粘贴到 App 即可)",
628
651
  "pair.infoCopyDone": "配对信息已复制",
629
652
  "pair.infoCopyFailed": "配对信息复制失败:",
630
- "pair.qrHint": "二维码与下面的「配对信息」内容相同:包含{kind}地址、一次性配对码、证书指纹(内网时)与主机标识。在局域网与公网之间切换会立即生成对应的新配对信息,配对码 5 分钟后失效。",
653
+ "pair.qrHint": "二维码与「配对信息」内容相同:包含{kind}地址、一次性配对码、证书指纹(内网时)与主机标识。在局域网与公网之间切换会立即生成对应的新配对信息,配对码 5 分钟后失效。",
631
654
  "advanced.summary": "高级信息",
632
655
  "advanced.protocolVersion": "协议版本",
633
656
  "advanced.serverVersion": "服务器版本",
@@ -653,9 +676,9 @@ window.__ModuleLoader__.load({
653
676
  "push.pushNotEnabled": "推送未启用",
654
677
  "push.relayStep.health": "服务可达",
655
678
  "push.relayStep.enroll": "自动注册",
656
- "push.prefix.ok": "✓ ",
657
- "push.prefix.fail": "✗ ",
658
- "devices.title": "设备",
679
+ "push.prefix.ok": "通过:",
680
+ "push.prefix.fail": "未通过:",
681
+ "devices.title": "已配对设备",
659
682
  "devices.col.name": "设备",
660
683
  "devices.col.appVersion": "App 版本",
661
684
  "devices.col.push": "离线推送",
@@ -686,13 +709,13 @@ window.__ModuleLoader__.load({
686
709
  "devices.pushNotRegistered": "未注册",
687
710
  "devices.pushEnvProduction": "生产",
688
711
  "devices.pushEnvDevelopment": "开发",
689
- "devices.empty": "还没有 v2 设备。请在 iPhone 上扫描一次性配对二维码。",
712
+ "devices.empty": "还没有配对设备。点击“添加手机”,然后在 DeepPilot 中扫码连接。",
690
713
  "help.remoteTitle": "远程连接帮助",
691
714
  "help.recommended": "推荐设置流程",
692
- "help.step1": "先打开\"DeepPilot 连接\",再打开\"远程连接(Tailscale Funnel)\"。",
715
+ "help.step1": "先打开“允许手机连接”,再打开“远程连接”。",
693
716
  "help.step2": "状态变为\"等待 Tailscale 授权\"后,点击\"打开授权页面\"。",
694
717
  "help.step3": "使用有权管理此 Tailnet 的账号登录,并按授权页提示启用 Funnel。通常需要 Owner、Admin 或 Network admin 权限。",
695
- "help.step4": "返回此页面并点击\"刷新\"。远程连接标题旁出现绿点后,即可扫描下方二维码添加手机。",
718
+ "help.step4": "返回此页面并点击“刷新”。远程连接显示“已就绪”后,点击“添加手机”并扫码。",
696
719
  "help.funnelHint": "插件已内嵌 Tailscale 网络组件,这台电脑和手机都不需要另外安装 Tailscale App;但首次启用仍需由 Tailnet 管理员授权。",
697
720
  "help.httpsTitle": "授权页未自动完成时:开启 HTTPS",
698
721
  "help.httpsStep1": "打开 Tailscale 管理后台的 Network → DNS。",
@@ -751,11 +774,39 @@ window.__ModuleLoader__.load({
751
774
  "update.badge": "有新版本"
752
775
  },
753
776
  en: {
777
+ "local.options": "LAN connection options",
778
+ "remote.options": "Remote connection options",
779
+ "local.errorDetails": "LAN connection failed. Check the connection options ({message}).",
780
+ "remote.errorDetails": "Remote connection failed. Check authorization and connection options ({message}).",
781
+ "pair.add": "Add phone",
782
+ "pair.close": "Collapse",
783
+ "devices.count": "{count} devices",
784
+ "devices.moreAria": "More actions for “{name}”",
785
+ "tools.title": "Tools and information",
786
+ "diag.reportFailed": "Failed to read connection status",
787
+ "diagnostics.historyCount": "{count} past failures",
788
+ "diagnostics.kind.upgrade": "Connection request rejected",
789
+ "diagnostics.kind.pair": "Pairing request rejected",
790
+ "diagnostics.kind.auth": "Device authentication failed",
791
+ "diagnostics.kind.opened": "Connection opened",
792
+ "diagnostics.kind.closed": "Connection closed unexpectedly",
793
+ "diagnostics.kind.error": "Connection error",
794
+ "diagnostics.kind.identity": "Device identity error",
795
+ "diagnostics.kind.storage": "Saving device information failed",
796
+ "diagnostics.kind.unknown": "Connection problem",
797
+ "push.outcome.sent": "Sent",
798
+ "push.outcome.failed": "Failed to send",
799
+ "push.outcome.invalid": "Invalid push credentials",
800
+ "push.environment.development": "Development",
801
+ "push.environment.production": "Production",
802
+ "push.environment.other": "Other environment ({environment})",
803
+ "push.outcome.other": "Push status ({outcome})",
804
+ "push.resultDetail": "Push result ({message})",
754
805
  "nav": "DeepPilot",
755
806
  "meta.title": "DeepPilot",
756
- "meta.intro": "Connect your iPhone to DeepSeek Harness (DSH) on this computer (protocol v2).",
807
+ "meta.intro": "Use this computer’s DSH from your phone.",
757
808
  "meta.refresh": "Refresh",
758
- "master.title": "DeepPilot connection",
809
+ "master.title": "Allow phone connections",
759
810
  "master.loading": "Loading settings…",
760
811
  "master.on": "On: accepting phone connections.",
761
812
  "master.off": "Off: not accepting phone connections.",
@@ -768,11 +819,11 @@ window.__ModuleLoader__.load({
768
819
  "connection.optionsSummary": "Connection options",
769
820
  "pair.description": "Choose a connection type and generate one-time pairing information.",
770
821
  "devices.description": "Manage iPhones that are already paired.",
771
- "troubleshooting.summary": "Tests and troubleshooting",
822
+ "troubleshooting.summary": "Diagnostic tools",
772
823
  "troubleshooting.title": "Diagnostic tools",
773
824
  "troubleshooting.description": "Use these only when diagnosing connection or push issues.",
774
825
  "troubleshooting.debugTitle": "Debug logging",
775
- "troubleshooting.debugDescription": "When on, prints handshake and push diagnostics to the DSH host’s terminal output; never prints pairing tokens, APNs tokens, or message content. Takes effect immediately, no restart needed.",
826
+ "troubleshooting.debugDescription": "New phone connections write diagnostic logs to the DSH terminal. Connected phones must reconnect to pick up the change. Logs exclude pairing codes, push credentials, and message content.",
776
827
  "diagnostics.title": "Connection diagnostics",
777
828
  "diagnostics.description": "Recent connection failures; copy them for troubleshooting.",
778
829
  "diagnostics.recent": "Recent failures",
@@ -816,7 +867,7 @@ window.__ModuleLoader__.load({
816
867
  "local.portApplied": "LAN port saved; the listener is reloading.",
817
868
  "local.portInvalid": "Enter an integer from 1024 to 65535.",
818
869
  "local.portFailed": "Failed to save LAN port: ",
819
- "remote.title": "Remote connection (Tailscale Funnel)",
870
+ "remote.title": "Remote connection",
820
871
  "remote.on": "Configured: the embedded Funnel will start and sync state automatically.",
821
872
  "remote.off": "Off: LAN connections only.",
822
873
  "remote.openAuth": "Open authorization page",
@@ -864,12 +915,12 @@ window.__ModuleLoader__.load({
864
915
  "panel.tokenRotateWarning": "Rotation invalidates the current token immediately and drops every paired phone. Click again within 5s to confirm.",
865
916
  "panel.tokenRotated": "New token active, old token invalidated. Please re-pair every device.",
866
917
  "panel.tokenRotateFailed": "Rotation failed: ",
867
- "pair.qrPanelTitle": "Scan to pair this phone",
918
+ "pair.qrPanelTitle": "Add phone",
868
919
  "pair.kind.public": "Public",
869
920
  "pair.kind.lan": "LAN",
870
921
  "pair.noAddress": "No address available",
871
922
  "pair.noAddressHelp": "No LAN or remote endpoint is available. Make sure LAN is running or enable remote access.",
872
- "pair.disabledHelp": "DeepPilot is turned off. Turn the connection on above before adding a phone.",
923
+ "pair.disabledHelp": "Turn on “Allow phone connections” before adding a phone.",
873
924
  "pair.qrAlt": "DeepPilot pairing QR code",
874
925
  "pair.qrShow": "Show QR code",
875
926
  "pair.qrHide": "Hide QR code",
@@ -880,7 +931,7 @@ window.__ModuleLoader__.load({
880
931
  "pair.infoLabel": "Pairing info (copy once, paste it in the app)",
881
932
  "pair.infoCopyDone": "Pairing info copied",
882
933
  "pair.infoCopyFailed": "Failed to copy pairing info: ",
883
- "pair.qrHint": "The QR code and the pairing info below carry the same {kind} address, single-use code, certificate fingerprint (for LAN) and host identity. Switching between LAN and public issues a matching new string immediately; the code expires after 5 minutes.",
934
+ "pair.qrHint": "The QR code and the pairing info carry the same {kind} address, single-use code, certificate fingerprint (for LAN) and host identity. Switching between LAN and public issues a matching new string immediately; the code expires after 5 minutes.",
884
935
  "advanced.summary": "Advanced info",
885
936
  "advanced.protocolVersion": "Protocol version",
886
937
  "advanced.serverVersion": "Server version",
@@ -906,9 +957,9 @@ window.__ModuleLoader__.load({
906
957
  "push.pushNotEnabled": "Push not enabled",
907
958
  "push.relayStep.health": "Service reachable",
908
959
  "push.relayStep.enroll": "Auto enrollment",
909
- "push.prefix.ok": "✓ ",
910
- "push.prefix.fail": "✗ ",
911
- "devices.title": "Devices",
960
+ "push.prefix.ok": "Passed: ",
961
+ "push.prefix.fail": "Failed: ",
962
+ "devices.title": "Paired devices",
912
963
  "devices.col.name": "Device",
913
964
  "devices.col.appVersion": "App version",
914
965
  "devices.col.push": "Push",
@@ -939,13 +990,13 @@ window.__ModuleLoader__.load({
939
990
  "devices.pushNotRegistered": "Not registered",
940
991
  "devices.pushEnvProduction": "Production",
941
992
  "devices.pushEnvDevelopment": "Development",
942
- "devices.empty": "No v2 devices yet. Scan a single-use pairing QR code in DeepPilot on iPhone.",
993
+ "devices.empty": "No paired devices yet. Click “Add phone”, then scan the QR code in DeepPilot.",
943
994
  "help.remoteTitle": "Remote connection help",
944
995
  "help.recommended": "Recommended setup",
945
- "help.step1": "Turn on \"DeepPilot connection\" first, then \"Remote connection (Tailscale Funnel)\".",
996
+ "help.step1": "Turn on “Allow phone connections”, then “Remote connection”.",
946
997
  "help.step2": "When the status shows \"Awaiting Tailscale authorization\", click \"Open authorization page\".",
947
998
  "help.step3": "Sign in with an account that can manage this Tailnet and follow the on-page prompts to enable Funnel. Owner / Admin / Network admin is usually required.",
948
- "help.step4": "Come back to this page and click \"Refresh\". When a green dot appears next to the remote title, scan the QR code below to add your phone.",
999
+ "help.step4": "Return and click “Refresh”. Once the remote connection is ready, click “Add phone” and scan the QR code.",
949
1000
  "help.funnelHint": "The plugin bundles the Tailscale networking stack, so neither this computer nor the phone needs the Tailscale app — but a Tailnet administrator must approve first-time use.",
950
1001
  "help.httpsTitle": "If authorization does not complete: enable HTTPS",
951
1002
  "help.httpsStep1": "Open the Tailscale admin console → Network → DNS.",
@@ -1066,172 +1117,150 @@ window.__ModuleLoader__.load({
1066
1117
  en: TABLES.en
1067
1118
  });
1068
1119
  }
1069
- //#endregion
1070
- //#region src/client/styles.ts
1071
- const EASE = "cubic-bezier(0.22,1,0.36,1)";
1072
- const CSS = [
1073
- ".pbb-section{container:pbb/inline-size;max-width:760px;color:var(--dsw-alias-label-primary);font-size:14px;line-height:1.5;display:flex;flex-direction:column;gap:16px}",
1074
- ".pbb-section>*{animation:pbb-cardIn 160ms " + EASE + " backwards}",
1075
- ".pbb-section>*:nth-child(2){animation-delay:24ms}",
1076
- ".pbb-section>*:nth-child(3){animation-delay:48ms}",
1077
- ".pbb-section>*:nth-child(4){animation-delay:72ms}",
1078
- ".pbb-section>*:nth-child(5){animation-delay:96ms}",
1079
- ".pbb-section>*:nth-child(6){animation-delay:120ms}",
1080
- ".pbb-section>*:nth-child(7){animation-delay:144ms}",
1081
- ".pbb-section>*:nth-child(n+8){animation-delay:144ms}",
1082
- "@keyframes pbb-cardIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}",
1083
- ".pbb-pageHeader{display:flex;align-items:flex-start;gap:16px;padding:2px 2px 4px}",
1084
- ".pbb-title{margin:0;font-size:18px;font-weight:600;letter-spacing:-.01em}",
1085
- ".pbb-intro{color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1.5;margin:4px 0 0;max-width:600px}",
1086
- ".pbb-refresh{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:none;padding:3px 0;margin-left:auto;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + "}",
1087
- ".pbb-refresh:active{transform:scale(0.97)}",
1088
- ".pbb-refresh:disabled{cursor:default;opacity:.5}",
1089
- ".pbb-statusStrip{display:grid;grid-template-columns:repeat(4,1fr);background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2));border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4));border-radius:var(--dsw-radius-xl,20px);overflow:hidden}",
1090
- ".pbb-statusCell{padding:14px 16px;min-width:0}",
1091
- ".pbb-statusCell+.pbb-statusCell{border-left:1px solid var(--dsw-alias-border-l2)}",
1092
- ".pbb-statusLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
1093
- ".pbb-statusValue{margin-top:2px;font-size:16px;font-weight:600;line-height:22px;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
1094
- ".pbb-statusValue .pbb-dot{margin-right:6px}",
1095
- ".pbb-statusSub{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}",
1096
- ".pbb-card{border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4));background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2));border-radius:var(--dsw-radius-xl,20px);overflow:hidden}",
1097
- ".pbb-cardHeader{display:flex;align-items:flex-start;gap:16px;padding:15px 18px 13px}",
1098
- ".pbb-cardHeader>div:first-child{min-width:0;flex:1}",
1099
- ".pbb-cardTitle{margin:0;font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1100
- ".pbb-cardDescription{margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
1101
- ".pbb-cardHead{display:flex;align-items:center;gap:10px;width:100%;min-height:52px;padding:0 18px;border:0;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background-color 160ms " + EASE + "}",
1102
- ".pbb-cardHead:active{background:var(--dsw-alias-bg-module-platform)}",
1103
- ".pbb-cardSummary{flex:1;min-width:0;text-align:right;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
1104
- ".pbb-chev{flex:none;display:inline-flex;align-items:center;color:var(--dsw-alias-label-tertiary);transition:transform 160ms " + EASE + "}",
1105
- ".pbb-chevIcon{display:block}",
1106
- ".pbb-cardHead[aria-expanded=\"true\"] .pbb-chev,.pbb-expand[aria-expanded=\"true\"] .pbb-chev{transform:rotate(90deg)}",
1107
- ".pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 18px 16px}",
1108
- ".pbb-pairingBody{display:flex;flex-direction:column;gap:10px;padding:0 18px 15px}",
1109
- ".pbb-sectionIntro{padding-bottom:2px}",
1110
- ".pbb-fold{display:grid;grid-template-rows:1fr;transition:grid-template-rows 240ms " + EASE + ",opacity 200ms " + EASE + ";opacity:1}",
1111
- ".pbb-foldClosed{grid-template-rows:0fr;opacity:0}",
1112
- ".pbb-fold>div{overflow:hidden;min-height:0}",
1113
- ".pbb-row{display:flex;align-items:center;gap:8px}",
1114
- ".pbb-label{flex:1;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary);min-width:0}",
1115
- ".pbb-value{font-size:12px;color:var(--dsw-alias-label-secondary);word-break:break-all;text-align:right}",
1116
- ".pbb-badge,.pbb-countBadge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:2px 8px;font-size:12px;font-weight:500;white-space:nowrap}",
1117
- ".pbb-badgeOk{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22a06b) 12%,transparent);color:var(--dsw-alias-state-success-primary,#22a06b)}",
1118
- ".pbb-badgeWarn{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 14%,transparent);color:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1119
- ".pbb-countBadge{min-width:24px;text-align:center}",
1120
- ".pbb-statusBadge{display:inline-flex;align-items:center;gap:6px;flex:none;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;white-space:nowrap}",
1121
- ".pbb-ok{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));font-weight:500}",
1122
- ".pbb-bad{color:var(--dsw-alias-state-error-primary);font-weight:500}",
1123
- ".pbb-diag{font-size:12px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5;overflow-wrap:anywhere}",
1124
- ".pbb-diagBad{color:var(--dsw-alias-state-error-primary)}",
1125
- ".pbb-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.55;margin:0;padding:12px;border:1px dashed var(--dsw-alias-border-l3);border-radius:8px}",
1126
- ".pbb-token{max-width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--dsw-alias-label-secondary);word-break:break-all}",
1127
- ".pbb-action{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:1px solid var(--dsw-alias-border-l3);border-radius:7px;padding:5px 9px;line-height:1.2;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + ",background-color 160ms " + EASE + ",border-color 160ms " + EASE + "}",
1128
- ".pbb-action:active{transform:scale(0.97)}",
1129
- ".pbb-action:disabled{cursor:default;opacity:.5}",
1130
- ".pbb-buttonPrimary{font-size:13px;padding:6px 12px;color:var(--dsw-alias-label-primary-foreground);border-color:transparent;background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary))}",
1131
- ".pbb-actionDangerGhost{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 38%,transparent);background:transparent;line-height:1.35;padding:4px 9px}",
1132
- ".pbb-actionDangerGhost:active{transform:scale(0.97)}",
1133
- ".pbb-actionDangerGhost:disabled{cursor:default;opacity:.62}",
1134
- ".pbb-actionIcon{flex:none;display:inline-block;vertical-align:-2px;margin-right:5px;pointer-events:none}",
1135
- ".pbb-actionBusy{display:inline-flex;align-items:center;gap:6px}",
1136
- ".pbb-actionBusy::before{content:\"\";width:9px;height:9px;border-radius:50%;border:1.5px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,transparent);border-top-color:currentColor;animation:pbb-spin .7s linear infinite}",
1137
- "@keyframes pbb-spin{to{transform:rotate(360deg)}}",
1138
- ".pbb-masterRow{display:flex;align-items:center;gap:14px}",
1139
- ".pbb-switchText{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}",
1140
- ".pbb-switchTitle{font-size:14px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1141
- ".pbb-switchDesc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.45}",
1142
- ".pbb-switch{appearance:none;-webkit-appearance:none;width:40px;height:24px;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent);border:1px solid transparent;position:relative;cursor:pointer;padding:0;flex:none;transition:background-color 160ms " + EASE + ",border-color 160ms " + EASE + ",transform 160ms " + EASE + "}",
1143
- ".pbb-switch::after{content:\"\";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.24);transition:transform 160ms " + EASE + ",background-color 160ms " + EASE + "}",
1144
- ".pbb-switchOn{background:var(--dsw-alias-brand-primary)}",
1145
- ".pbb-switchOn::after{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}",
1146
- ".pbb-switch:active{transform:scale(0.97)}",
1147
- ".pbb-switch:disabled{opacity:.5;cursor:default}",
1148
- ".pbb-switch:focus-visible,.pbb-action:focus-visible,.pbb-targetButton:focus-visible,.pbb-cardHead:focus-visible,.pbb-expand:focus-visible,.pbb-refresh:focus-visible,.pbb-numberInput:focus-visible,.pbb-deviceNameInput:focus-visible,.pbb-disclosure>summary:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}",
1149
- ".pbb-dot{display:inline-block;vertical-align:1px;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
1150
- ".pbb-dotOk{background:var(--dsw-alias-state-success-primary,#22a06b)}",
1151
- ".pbb-dotWarn{background:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1152
- ".pbb-dotBad{background:var(--dsw-alias-state-error-primary)}",
1153
- ".pbb-switchTitle .pbb-dot{margin-right:6px;vertical-align:1px}",
1154
- ".pbb-channel{border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px;display:flex;flex-direction:column;gap:10px}",
1155
- ".pbb-expand{border:0;background:none;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;padding:0;display:flex;align-items:center;gap:6px;align-self:flex-start;transition:color 160ms " + EASE + ",transform 160ms " + EASE + "}",
1156
- ".pbb-expand:active{transform:scale(0.97)}",
1157
- ".pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 0 0 14px}",
1158
- ".pbb-limitRow{display:flex;align-items:center;gap:12px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1159
- ".pbb-limitRow:first-child{border-top:0;padding-top:2px}",
1160
- ".pbb-limitControl{display:flex;align-items:center;gap:8px;flex:none}",
1161
- ".pbb-numberInput{width:72px;box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:6px 8px}",
1162
- ".pbb-numberInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1163
- ".pbb-numberInput:disabled{opacity:.5}",
1164
- ".pbb-targetList{display:inline-flex;flex-wrap:wrap;gap:2px;background:var(--dsw-alias-bg-module-platform);border-radius:999px;padding:2px;align-self:flex-start}",
1165
- ".pbb-targetButton{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:0;border-radius:999px;padding:6px 9px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + ",background-color 160ms " + EASE + ",box-shadow 160ms " + EASE + "}",
1166
- ".pbb-targetButton:active{transform:scale(0.97)}",
1167
- ".pbb-targetButton:disabled{cursor:default;opacity:.5}",
1168
- ".pbb-targetSelected{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);box-shadow:0 1px 3px color-mix(in srgb,var(--dsw-alias-label-primary) 14%,transparent);font-weight:500}",
1169
- ".pbb-qrPanel{display:grid;grid-template-columns:224px 1fr;gap:20px;align-items:start;padding:14px 0 4px;border-top:1px solid var(--dsw-alias-border-l2)}",
1170
- ".pbb-qrImage{width:224px;height:224px;max-width:100%;background:#fff;border-radius:12px;padding:8px;box-sizing:border-box}",
1171
- ".pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}",
1172
- ".pbb-countdownBar{height:4px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);overflow:hidden}",
1173
- ".pbb-countdownBar i{display:block;width:100%;height:100%;transform-origin:left center;background:var(--dsw-alias-state-business-primary,var(--dsw-alias-state-success-primary,#22a06b));border-radius:999px;transition:transform 1s linear}",
1174
- ".pbb-countdownText{font-size:12px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}",
1175
- ".pbb-pairInfo{display:flex;flex-direction:column;gap:10px;min-width:0}",
1176
- ".pbb-pairCodeBlock{width:100%;display:flex;flex-direction:column;gap:6px}",
1177
- ".pbb-pairCodeLabel{font-size:12px;color:var(--dsw-alias-label-tertiary)}",
1178
- ".pbb-pairCodeRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
1179
- ".pbb-pairCode{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;line-height:1.4;letter-spacing:.03em;color:var(--dsw-alias-label-primary);word-break:break-all;user-select:all}",
1180
- ".pbb-qrHint{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
1181
- ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
1182
- ".pbb-resultRow{display:flex;align-items:flex-start;gap:6px}",
1183
- ".pbb-resultIcon{flex:none;display:inline-flex;margin-top:2px;color:var(--dsw-alias-state-success-primary)}",
1184
- ".pbb-resultIconBad{color:var(--dsw-alias-state-error-primary)}",
1185
- ".pbb-deviceList{display:flex;flex-direction:column}",
1186
- ".pbb-deviceRow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:12px;row-gap:8px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1187
- ".pbb-deviceRow:first-child{border-top:0;padding-top:2px}",
1188
- ".pbb-deviceRow:last-child{padding-bottom:0}",
1189
- ".pbb-deviceRow>.pbb-deviceRenameForm,.pbb-deviceRow>.pbb-deviceConfirm{grid-column:1/-1}",
1190
- ".pbb-deviceMain{min-width:0;display:flex;flex-direction:column;gap:2px}",
1191
- ".pbb-deviceNameRow{display:flex;align-items:center;gap:6px;min-width:0}",
1192
- ".pbb-deviceName{display:block;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1193
- ".pbb-deviceMeta{display:flex;flex-wrap:wrap;column-gap:6px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;min-width:0}",
1194
- ".pbb-deviceMeta>*+*::before{content:\"\\00B7\";margin-right:6px}",
1195
- ".pbb-deviceMeta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1196
- ".pbb-deviceActions{grid-row:1;grid-column:2;display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}",
1197
- ".pbb-deviceConfirm{display:flex;flex-direction:column;gap:8px}",
1198
- ".pbb-deviceRenameForm{display:flex;gap:8px;flex-wrap:wrap;min-width:0}",
1199
- ".pbb-deviceNameInput{flex:1;min-width:0;box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:6px 8px}",
1200
- ".pbb-deviceNameInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1201
- ".pbb-deviceNameInput:disabled{opacity:.5}",
1202
- ".pbb-deviceRenameActions{display:flex;align-items:center;gap:5px;flex:none}",
1203
- ".pbb-helpBody{display:flex;flex-direction:column;gap:14px}",
1204
- ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
1205
- ".pbb-helpHeading{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1206
- ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1207
- ".pbb-helpList li+li{margin-top:4px}",
1208
- ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1209
- ".pbb-helpCode{display:block;margin:2px 0 0;padding:10px;overflow:auto;white-space:pre-wrap;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}",
1210
- ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
1211
- ".pbb-testGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}",
1212
- ".pbb-testItem:last-child:nth-child(odd){grid-column:1/-1}",
1213
- ".pbb-testItem{display:flex;align-items:center;gap:12px;padding:11px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-module-platform)}",
1214
- ".pbb-testTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1215
- ".pbb-testDescription{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.5}",
1216
- ".pbb-runtimeList{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;overflow:hidden}",
1217
- ".pbb-runtimeList>.pbb-runtimeRow,.pbb-runtimeRow{display:flex;align-items:center;gap:10px;padding:9px 10px;border-top:1px solid var(--dsw-alias-border-l2)}",
1218
- ".pbb-runtimeList>.pbb-runtimeRow:first-child{border-top:0}",
1219
- ".pbb-runtimeList>.pbb-runtimeRow>.pbb-row{flex:1;min-width:0}",
1220
- ".pbb-updateRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-business-primary,#4176e6) 35%,var(--dsw-alias-border-l3));border-radius:8px;color:var(--dsw-alias-label-primary);font-size:13px;text-decoration:none;cursor:pointer;transition:background-color 160ms " + EASE + "}",
1221
- ".pbb-disclosure{padding:0}",
1222
- ".pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;padding:13px 18px;cursor:pointer;color:var(--dsw-alias-label-primary)}",
1223
- ".pbb-disclosure>summary::-webkit-details-marker{display:none}",
1224
- ".pbb-disclosure[open]>summary .pbb-chev{transform:rotate(90deg)}",
1225
- ".pbb-disclosureTitle{font-size:13px;font-weight:500;flex:1}",
1226
- ".pbb-disclosureMeta{font-size:12px;color:var(--dsw-alias-label-tertiary);max-width:46%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1227
- ".pbb-disclosureBody{display:flex;flex-direction:column;gap:12px;padding:2px 18px 16px}",
1228
- ".pbb-diagnostics>summary{color:var(--dsw-alias-label-secondary)}",
1229
- "@media (hover:hover) and (pointer:fine){.pbb-refresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.pbb-cardHead:hover{background:var(--dsw-alias-bg-module-platform)}.pbb-action:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}.pbb-buttonPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-interactive-bg-hover-solid))}.pbb-actionDangerGhost:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 9%,transparent);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent)}.pbb-expand:hover{color:var(--dsw-alias-label-primary)}.pbb-updateRow:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary,#4176e6) 8%,transparent)}}",
1230
- "@media (prefers-reduced-motion:reduce){.pbb-section *{animation-duration:.01ms!important;animation-delay:0s!important;transition-duration:.01ms!important;transition-delay:0s!important}}",
1231
- "@container pbb (max-width:640px){.pbb-statusStrip{grid-template-columns:repeat(2,1fr)}.pbb-statusCell:nth-child(3){border-left:0}.pbb-statusCell:nth-child(n+3){border-top:1px solid var(--dsw-alias-border-l2)}.pbb-cardSummary{display:none}}",
1232
- "@container pbb (max-width:480px){.pbb-testGrid{grid-template-columns:1fr}.pbb-testItem:last-child:nth-child(odd){grid-column:auto}.pbb-qrPanel{grid-template-columns:1fr}.pbb-limitRow{align-items:flex-start;flex-direction:column}.pbb-limitControl{align-self:flex-end}.pbb-deviceRow{grid-template-columns:minmax(0,1fr)}.pbb-deviceActions{grid-row:auto;grid-column:auto;justify-content:flex-start}}",
1233
- "@media (pointer:coarse){.pbb-action{min-height:32px;padding:7px 10px;font-size:13px}.pbb-buttonPrimary{font-size:14px}.pbb-cardHead{min-height:56px}.pbb-expand{padding:5px 0}.pbb-refresh{padding:5px 0}}"
1234
- ].join("\n");
1120
+ // src/client/styles.ts
1121
+ const CSS = String.raw`
1122
+ .pbb-section{--pbb-ink:var(--dsw-alias-label-primary,#0f1115);--pbb-secondary:var(--dsw-alias-label-secondary,#61666b);--pbb-line:var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l3,#e4e5e7));--pbb-fill:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2,#fff));--pbb-soft:var(--dsw-alias-bg-module-platform,#f5f6f8);container:pbb/inline-size;max-width:760px;color:var(--pbb-ink);font-size:14px;line-height:20px;display:flex;flex-direction:column;gap:20px}
1123
+ .pbb-section *{box-sizing:border-box}
1124
+ .pbb-section :is(.pbb-action,.pbb-refresh,.pbb-iconButton,.pbb-cardHead,.pbb-menuAction,.pbb-targetButton),.pbb-section input{font-family:inherit}
1125
+ .pbb-icon{display:block;flex:none;color:inherit}
1126
+ .pbb-pageHeader{display:flex;align-items:flex-start;gap:12px;min-height:56px}
1127
+ .pbb-pageHeader>div{min-width:0;flex:1;padding-top:2px}
1128
+ .pbb-title{margin:0;font-size:20px;line-height:27px;font-weight:600;letter-spacing:-.3px}
1129
+ .pbb-intro{color:var(--pbb-secondary);font-size:14px;line-height:20px;margin:5px 0 0}
1130
+ .pbb-refresh{display:inline-flex;align-items:center;justify-content:center;gap:5px;height:28px;padding:0 7px;flex:none;border:0;border-radius:6px;background:transparent;color:var(--pbb-secondary);font-size:12px;cursor:pointer}
1131
+ .pbb-card{border:1px solid var(--pbb-line);border-radius:18px;background:var(--pbb-fill)}
1132
+ .pbb-cardTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;color:var(--pbb-ink)}
1133
+ .pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 16px 16px}
1134
+ .pbb-connections>.pbb-cardBody{padding:0;gap:0}
1135
+ .pbb-row{display:flex;align-items:center;gap:8px;min-width:0}
1136
+ .pbb-masterRow{display:flex;align-items:center;gap:12px;min-height:66px;padding:11px 16px}
1137
+ .pbb-controlText{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}
1138
+ .pbb-masterRow>.pbb-controlText{gap:4px}
1139
+ .pbb-controlTitle{font-size:14px;line-height:20px;font-weight:400;color:var(--pbb-ink)}
1140
+ .pbb-masterRow .pbb-controlTitle{font-weight:600}
1141
+ .pbb-controlDescription,.pbb-diag,.pbb-label,.pbb-value{font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1142
+ .pbb-serviceSummary,.pbb-channelState{display:flex;align-items:center;flex-wrap:wrap;gap:6px;font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1143
+ .pbb-serviceSummary{gap:5px}
1144
+ .pbb-serviceState{display:inline-flex}
1145
+ .pbb-metaDivider{height:10px;border-left:1px solid var(--pbb-line);margin:0 2px}
1146
+ .pbb-channel{position:relative}
1147
+ .pbb-channel::before{content:"";position:absolute;top:0;left:16px;right:16px;border-top:1px solid var(--pbb-line)}
1148
+ .pbb-channel+.pbb-channel::before{left:46px}
1149
+ .pbb-channelRow{gap:12px;min-height:60px;padding:8px 16px}
1150
+ .pbb-channelRow>.pbb-icon{color:var(--pbb-secondary)}
1151
+ .pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 16px 14px 46px}
1152
+ .pbb-channel>.pbb-diag,.pbb-channel>.pbb-rowAction{margin:0;padding:0 16px 8px 46px}
1153
+ .pbb-fold[hidden]{display:none}
1154
+ .pbb-action:disabled,.pbb-refresh:disabled,.pbb-menuAction:disabled,.pbb-targetButton:disabled{opacity:.5;cursor:default}
1155
+ .pbb-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;flex:none;height:28px;max-width:100%;padding:0 9px;border:1px solid var(--pbb-line);border-radius:6px;background:transparent;color:var(--pbb-secondary);font:inherit;font-size:14px;line-height:20px;text-decoration:none;cursor:pointer;white-space:nowrap}
1156
+ .pbb-addAction{display:flex;margin-left:auto}
1157
+ .pbb-buttonPrimary{background:var(--dsw-alias-button-primary-fill,var(--pbb-ink));border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}
1158
+ .pbb-actionDangerGhost{color:var(--dsw-alias-state-error-primary,#bc2222);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary,#bc2222) 35%,transparent)}
1159
+ .pbb-iconButton{display:flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--pbb-secondary);cursor:pointer}
1160
+ .pbb-chev{display:inline-flex;flex:none;color:var(--pbb-secondary)}
1161
+ .pbb-chevIcon{width:14px;height:14px;display:block}
1162
+ [aria-expanded="true"]>.pbb-chev,.pbb-helpDisclosure[open]>summary>.pbb-chev,.pbb-disclosure[open]>summary>.pbb-chev{transform:rotate(90deg)}
1163
+ .pbb-diag{margin:0;overflow-wrap:anywhere}
1164
+ .pbb-diagBad,.pbb-bad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1165
+ .pbb-ok{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#287a45) 65%,var(--pbb-ink))}
1166
+ .pbb-label{flex:1;min-width:0;font-weight:500;color:var(--pbb-ink)}
1167
+ .pbb-value{text-align:right;overflow-wrap:anywhere}
1168
+ .pbb-token{max-width:100%;font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--pbb-secondary);overflow-wrap:anywhere}
1169
+ .pbb-empty{margin:0;padding:16px;font-size:14px;line-height:20px;color:var(--pbb-secondary)}
1170
+ .pbb-limitRow{display:flex;align-items:center;gap:12px}
1171
+ .pbb-limitControl{display:flex;align-items:center;gap:8px;flex:none}
1172
+ .pbb-numberInput,.pbb-deviceNameInput{min-width:0;height:28px;border:1px solid var(--pbb-line);border-radius:6px;background:var(--dsw-alias-bg-layer-3,var(--pbb-fill));color:var(--pbb-ink);font:inherit;font-size:14px;line-height:20px;padding:3px 8px;caret-color:var(--pbb-ink)}
1173
+ .pbb-numberInput{width:72px}
1174
+ .pbb-numberInput:disabled,.pbb-deviceNameInput:disabled{opacity:.5}
1175
+ .pbb-endpoint{user-select:all}
1176
+ .pbb-devicesSection{display:flex;flex-direction:column;gap:8px}
1177
+ .pbb-devicesHeader{display:flex;align-items:center;gap:8px;min-height:44px;padding:0 2px}
1178
+ .pbb-deviceCount{font-size:12px;line-height:18px;color:var(--pbb-secondary);white-space:nowrap}
1179
+ .pbb-devicesCard{overflow:visible}
1180
+ .pbb-deviceList{display:flex;flex-direction:column}
1181
+ .pbb-deviceRow{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:12px;row-gap:0;min-height:56px;padding:6px 14px 6px 16px}
1182
+ .pbb-deviceRow+.pbb-deviceRow::before{content:"";position:absolute;top:0;left:46px;right:16px;border-top:1px solid var(--pbb-line)}
1183
+ .pbb-deviceRow:has(details[open]){z-index:2}
1184
+ .pbb-deviceMain{display:flex;align-items:center;gap:12px;min-width:0}
1185
+ .pbb-deviceMain>.pbb-icon{color:var(--pbb-secondary)}
1186
+ .pbb-deviceText{display:flex;flex-direction:column;gap:2px;min-width:0}
1187
+ .pbb-deviceName{font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1188
+ .pbb-deviceMeta{display:flex;flex-wrap:wrap;gap:0 6px;color:var(--pbb-secondary);font-size:12px;line-height:18px}
1189
+ .pbb-deviceMeta>*+*::before{content:"\00B7";margin-right:6px}
1190
+ .pbb-deviceActions{grid-row:1;grid-column:2;display:flex;align-items:center}
1191
+ .pbb-deviceRenameForm,.pbb-deviceConfirm{grid-column:1/-1;display:flex;gap:8px;padding:8px 0;min-width:0}
1192
+ .pbb-deviceRenameForm{flex-wrap:wrap}
1193
+ .pbb-deviceNameInput{flex:1}
1194
+ .pbb-deviceRenameActions{display:flex;align-items:center;gap:5px;flex:none}
1195
+ .pbb-deviceConfirm{flex-direction:column}
1196
+ .pbb-deviceMessage{padding:10px 16px}
1197
+ .pbb-deviceMenu{position:relative}
1198
+ .pbb-deviceMenu>summary{list-style:none}
1199
+ .pbb-deviceMenu>summary::-webkit-details-marker{display:none}
1200
+ .pbb-deviceMenuBody{position:absolute;z-index:3;top:34px;right:0;width:250px;max-width:calc(100cqw - 32px);padding:5px;border:1px solid var(--pbb-line);border-radius:10px;background:var(--dsw-alias-bg-layer-3,var(--pbb-fill));box-shadow:0 8px 24px color-mix(in srgb,var(--pbb-ink) 12%,transparent)}
1201
+ .pbb-deviceMenuInfo{display:flex;flex-direction:column;gap:3px;padding:7px 8px 10px;border-bottom:1px solid var(--pbb-line)}
1202
+ .pbb-menuAction{display:block;width:100%;min-height:32px;padding:5px 8px;border:0;border-radius:5px;background:transparent;color:var(--pbb-ink);text-align:left;font:inherit;font-size:14px;cursor:pointer}
1203
+ .pbb-menuDanger{color:var(--dsw-alias-state-error-primary,#bc2222)}
1204
+ .pbb-actionBusy{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--pbb-secondary)}
1205
+ .pbb-actionBusy::before{content:"";width:10px;height:10px;border:1.5px solid var(--pbb-line);border-top-color:currentColor;border-radius:50%;animation:pbb-spin .7s linear infinite}
1206
+ @keyframes pbb-spin{to{transform:rotate(360deg)}}
1207
+ .pbb-rowAction{display:flex;flex-wrap:wrap;gap:8px}
1208
+ .pbb-pairingPanel{border:1px solid var(--pbb-line);border-radius:16px;padding:16px;background:var(--pbb-fill)}
1209
+ .pbb-pairingHeader{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}
1210
+ .pbb-pairingBody{display:flex;flex-direction:column;gap:10px}
1211
+ .pbb-targetList{display:flex;flex-wrap:wrap;gap:4px;min-width:0}
1212
+ .pbb-targetButton{max-width:100%;padding:5px 9px;border:1px solid transparent;border-radius:7px;background:var(--pbb-soft);color:var(--pbb-secondary);font:inherit;font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}
1213
+ .pbb-targetSelected{color:var(--pbb-ink);border-color:var(--pbb-line);background:var(--pbb-fill)}
1214
+ .pbb-qrPanel{display:grid;grid-template-columns:224px minmax(0,1fr);gap:20px;align-items:start}
1215
+ .pbb-qrImage{display:block;width:224px;height:224px;max-width:100%;padding:8px;border-radius:12px;background:#fff}
1216
+ .pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}
1217
+ .pbb-countdownBar{height:4px;border-radius:99px;background:var(--pbb-soft);overflow:hidden}
1218
+ .pbb-countdownBar i{display:block;width:100%;height:100%;transform-origin:left center;background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:99px}
1219
+ .pbb-countdownText{font-size:12px;line-height:18px;color:var(--pbb-secondary);font-variant-numeric:tabular-nums}
1220
+ .pbb-pairInfo,.pbb-pairCodeBlock{display:flex;flex-direction:column;gap:8px;min-width:0}
1221
+ .pbb-pairCodeLabel,.pbb-qrHint{font-size:12px;line-height:18px;color:var(--pbb-secondary);margin:0}
1222
+ .pbb-pairCodeRow{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
1223
+ .pbb-pairCode{font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere;user-select:all}
1224
+ .pbb-tools{border-radius:16px;overflow:hidden}
1225
+ .pbb-toolSection+.pbb-toolSection{border-top:1px solid var(--pbb-line)}
1226
+ .pbb-cardHead{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:0 16px;border:0;background:transparent;color:var(--pbb-secondary);font:inherit;text-align:left;cursor:pointer}
1227
+ .pbb-cardHead>.pbb-cardTitle{flex:1;font-weight:400}
1228
+ .pbb-toolMeta{max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1229
+ .pbb-toolMeta.pbb-ok{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#287a45) 65%,var(--pbb-ink))}
1230
+ .pbb-toolMeta.pbb-bad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1231
+ .pbb-testGrid{display:flex;flex-direction:column;gap:0}
1232
+ .pbb-testItem{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--pbb-line)}
1233
+ .pbb-testTitle{font-size:14px;line-height:20px;font-weight:500;color:var(--pbb-ink)}
1234
+ .pbb-testDescription{font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1235
+ .pbb-helpBody,.pbb-disclosureBody{display:flex;flex-direction:column;gap:14px}
1236
+ .pbb-helpSection{display:flex;flex-direction:column;gap:6px}
1237
+ .pbb-helpHeading{font-size:14px;line-height:20px;font-weight:500;color:var(--pbb-ink)}
1238
+ .pbb-helpList{margin:0;padding-left:20px;font-size:14px;line-height:22px;color:var(--pbb-secondary)}
1239
+ .pbb-helpList li+li{margin-top:4px}
1240
+ .pbb-helpText{margin:0;font-size:14px;line-height:22px;color:var(--pbb-secondary)}
1241
+ .pbb-helpCode{display:block;margin:0;padding:10px;border:1px solid var(--pbb-line);border-radius:8px;background:var(--pbb-soft);color:var(--pbb-ink);font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre-wrap;overflow-wrap:anywhere}
1242
+ .pbb-helpLink{color:var(--pbb-ink);text-decoration:underline;text-underline-offset:3px}
1243
+ .pbb-helpDisclosure>summary{display:flex;align-items:center;gap:6px;min-height:28px;list-style:none;cursor:pointer;color:var(--pbb-secondary);font-size:12px}
1244
+ .pbb-helpDisclosure>summary::-webkit-details-marker{display:none}
1245
+ .pbb-helpDisclosure[open]>.pbb-helpBody{padding-top:10px}
1246
+ .pbb-runtimeList{display:flex;flex-direction:column}
1247
+ .pbb-runtimeRow{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--pbb-line)}
1248
+ .pbb-runtimeRow>.pbb-row{flex:1;min-width:0}
1249
+ .pbb-updateRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;color:var(--pbb-ink);font-size:14px;text-decoration:none}
1250
+ .pbb-resultRow{display:flex;align-items:flex-start;gap:6px}
1251
+ .pbb-resultIcon{display:inline-flex;flex:none;margin-top:3px;color:var(--dsw-alias-state-success-primary,#287a45)}
1252
+ .pbb-resultIconBad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1253
+ .pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;min-height:44px;padding:0 16px;cursor:pointer;font-size:14px}
1254
+ .pbb-disclosure>summary::-webkit-details-marker{display:none}
1255
+ .pbb-disclosureBody{padding:2px 16px 16px}
1256
+ .pbb-disclosureTitle{flex:1}
1257
+ .pbb-section :is(.pbb-action,.pbb-refresh,.pbb-iconButton,.pbb-cardHead,.pbb-menuAction,.pbb-targetButton,input,summary,a):focus-visible{outline:2px solid var(--dsw-focus-ring-color,#5276b4);outline-offset:2px}
1258
+ @media (hover:hover) and (pointer:fine){.pbb-refresh:hover:not(:disabled),.pbb-iconButton:hover,.pbb-action:hover:not(:disabled),.pbb-cardHead:hover,.pbb-menuAction:hover:not(:disabled){background:var(--pbb-soft)}.pbb-buttonPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#30343b)}.pbb-action:active:not(:disabled),.pbb-refresh:active:not(:disabled),.pbb-iconButton:active{transform:scale(.97)}}
1259
+ @media (prefers-reduced-motion:reduce){.pbb-action,.pbb-refresh,.pbb-iconButton{transition:none;transform:none!important}.pbb-actionBusy::before{animation:none}}
1260
+ @container pbb (max-width:480px){.pbb-qrPanel{grid-template-columns:1fr}.pbb-limitRow{align-items:flex-start;flex-direction:column}.pbb-limitControl{align-self:flex-end}.pbb-deviceMeta{column-gap:4px}.pbb-toolMeta{max-width:42%}.pbb-channelRow{gap:8px}.pbb-testItem{flex-wrap:wrap}.pbb-testItem>.pbb-controlText{flex-basis:65%}}
1261
+ @container pbb (max-width:340px){.pbb-devicesHeader{flex-wrap:wrap}.pbb-deviceRow{column-gap:6px;padding-right:10px}.pbb-channelBody{padding-left:16px}.pbb-toolMeta{max-width:38%}}
1262
+ @media (pointer:coarse){.pbb-action,.pbb-iconButton,.pbb-refresh,.pbb-targetButton,.pbb-menuAction,.pbb-helpDisclosure>summary{min-width:44px;min-height:44px}.pbb-numberInput,.pbb-deviceNameInput{height:44px;font-size:16px}.pbb-deviceMenuBody{top:48px}}
1263
+ `;
1235
1264
  /**
1236
1265
  * Client bundle id, identical to the `window.__ModuleLoader__.load({ id })`
1237
1266
  * identity emitted by tsdown.config.ts. DSH's module system owns style
@@ -1259,15 +1288,109 @@ window.__ModuleLoader__.load({
1259
1288
  tag.textContent = CSS;
1260
1289
  document.head.appendChild(tag);
1261
1290
  }
1262
- //#endregion
1263
- //#region node_modules/qrcode/lib/can-promise.js
1291
+ // src/client/settings-icons.ts
1292
+ /** 设置页的小图标共用线宽与 currentColor,随宿主主题变色,不额外引入图标包。 */
1293
+ function settingsIcon(name, size = 18) {
1294
+ const path = (d) => (0, react.createElement)("path", { d });
1295
+ const circle = (cx, cy, r) => (0, react.createElement)("circle", {
1296
+ cx,
1297
+ cy,
1298
+ r
1299
+ });
1300
+ return (0, react.createElement)("svg", {
1301
+ className: "pbb-icon",
1302
+ viewBox: "0 0 24 24",
1303
+ width: size,
1304
+ height: size,
1305
+ fill: "none",
1306
+ stroke: "currentColor",
1307
+ strokeWidth: 1.6,
1308
+ strokeLinecap: "round",
1309
+ strokeLinejoin: "round",
1310
+ "aria-hidden": true
1311
+ }, ...{
1312
+ phone: () => [(0, react.createElement)("rect", {
1313
+ x: 6.5,
1314
+ y: 2.5,
1315
+ width: 11,
1316
+ height: 19,
1317
+ rx: 2.2
1318
+ }), path("M10 5h4M11 18.5h2")],
1319
+ wifi: () => [path("M2 8.8a16 16 0 0 1 20 0M5 12.1a11 11 0 0 1 14 0M8.3 15.5a6 6 0 0 1 7.4 0"), circle(12, 19, .7)],
1320
+ globe: () => [circle(12, 12, 9), path("M3 12h18M12 3a17 17 0 0 1 0 18 17 17 0 0 1 0-18")],
1321
+ refresh: () => [path("M20 7v5h-5M4 17v-5h5M6.1 7a7 7 0 0 1 11.5-1L20 9M4 15l2.4 3A7 7 0 0 0 18 17")],
1322
+ plus: () => [path("M12 5v14M5 12h14")],
1323
+ more: () => [
1324
+ circle(5, 12, 1.2),
1325
+ circle(12, 12, 1.2),
1326
+ circle(19, 12, 1.2)
1327
+ ],
1328
+ activity: () => [path("M3 12h4l3-7 4 14 3-7h4")],
1329
+ info: () => [circle(12, 12, 9), path("M12 11v6M12 7h.01")],
1330
+ ready: () => [circle(12, 12, 9), path("m8 12 3 3 5-6")],
1331
+ attention: () => [circle(12, 12, 9), path("M12 7v6M12 17h.01")]
1332
+ }[name]());
1333
+ }
1334
+ // src/client/device-actions.ts
1335
+ /**
1336
+ * 原生 disclosure(折叠控件)负责 Enter/Space 与焦点顺序;操作仍是普通按钮,
1337
+ * 不冒充需要方向键导航的 ARIA menu。详情只在展开时进入无障碍读取顺序。
1338
+ */
1339
+ function DeviceActions(props) {
1340
+ const ref = (0, react.useRef)(null);
1341
+ const close = (restoreFocus = false) => {
1342
+ const details = ref.current;
1343
+ if (details === null) return;
1344
+ details.open = false;
1345
+ if (restoreFocus) details.querySelector("summary")?.focus();
1346
+ };
1347
+ (0, react.useEffect)(() => {
1348
+ const outside = (event) => {
1349
+ if (event.target instanceof Node && !ref.current?.contains(event.target)) close();
1350
+ };
1351
+ document.addEventListener("pointerdown", outside);
1352
+ return () => document.removeEventListener("pointerdown", outside);
1353
+ }, []);
1354
+ const act = (action) => {
1355
+ close();
1356
+ action();
1357
+ };
1358
+ return (0, react.createElement)("details", {
1359
+ ref,
1360
+ className: "pbb-deviceMenu",
1361
+ onKeyDown: (event) => {
1362
+ if (event.key !== "Escape" || !ref.current?.open) return;
1363
+ event.preventDefault();
1364
+ event.stopPropagation();
1365
+ close(true);
1366
+ }
1367
+ }, (0, react.createElement)("summary", {
1368
+ className: "pbb-iconButton",
1369
+ "aria-label": props.t("devices.moreAria", { name: props.name })
1370
+ }, settingsIcon("more", 16)), (0, react.createElement)("div", {
1371
+ className: "pbb-deviceMenuBody",
1372
+ "aria-label": props.t("devices.col.actions")
1373
+ }, (0, react.createElement)("div", { className: "pbb-deviceMenuInfo" }, props.customName ? (0, react.createElement)("span", { className: "pbb-diag" }, props.t("devices.customBadge")) : null, (0, react.createElement)("span", { className: "pbb-diag" }, props.t("devices.col.fingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, props.fingerprint || "—")), (0, react.createElement)("button", {
1374
+ type: "button",
1375
+ className: "pbb-menuAction",
1376
+ disabled: !props.canRename,
1377
+ "aria-label": props.t("devices.renameAria", { name: props.name }),
1378
+ onClick: () => act(props.onRename)
1379
+ }, props.t("devices.rename")), (0, react.createElement)("button", {
1380
+ type: "button",
1381
+ className: "pbb-menuAction pbb-menuDanger",
1382
+ disabled: !props.canRevoke,
1383
+ "aria-label": props.t("devices.revokeAria", { name: props.name }),
1384
+ onClick: () => act(props.onRevoke)
1385
+ }, props.t("devices.revoke"))));
1386
+ }
1387
+ // node_modules/qrcode/lib/can-promise.js
1264
1388
  var require_can_promise = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1265
1389
  module.exports = function() {
1266
1390
  return typeof Promise === "function" && Promise.prototype && Promise.prototype.then;
1267
1391
  };
1268
1392
  }));
1269
- //#endregion
1270
- //#region node_modules/qrcode/lib/core/utils.js
1393
+ // node_modules/qrcode/lib/core/utils.js
1271
1394
  var require_utils$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
1272
1395
  let toSJISFunction;
1273
1396
  const CODEWORDS_COUNT = [
@@ -1358,8 +1481,7 @@ window.__ModuleLoader__.load({
1358
1481
  return toSJISFunction(kanji);
1359
1482
  };
1360
1483
  }));
1361
- //#endregion
1362
- //#region node_modules/qrcode/lib/core/error-correction-level.js
1484
+ // node_modules/qrcode/lib/core/error-correction-level.js
1363
1485
  var require_error_correction_level = /* @__PURE__ */ __commonJSMin(((exports) => {
1364
1486
  exports.L = { bit: 1 };
1365
1487
  exports.M = { bit: 0 };
@@ -1391,8 +1513,7 @@ window.__ModuleLoader__.load({
1391
1513
  }
1392
1514
  };
1393
1515
  }));
1394
- //#endregion
1395
- //#region node_modules/qrcode/lib/core/bit-buffer.js
1516
+ // node_modules/qrcode/lib/core/bit-buffer.js
1396
1517
  var require_bit_buffer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1397
1518
  function BitBuffer() {
1398
1519
  this.buffer = [];
@@ -1418,8 +1539,7 @@ window.__ModuleLoader__.load({
1418
1539
  };
1419
1540
  module.exports = BitBuffer;
1420
1541
  }));
1421
- //#endregion
1422
- //#region node_modules/qrcode/lib/core/bit-matrix.js
1542
+ // node_modules/qrcode/lib/core/bit-matrix.js
1423
1543
  var require_bit_matrix = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1424
1544
  /**
1425
1545
  * Helper class to handle QR Code symbol modules
@@ -1479,8 +1599,7 @@ window.__ModuleLoader__.load({
1479
1599
  };
1480
1600
  module.exports = BitMatrix;
1481
1601
  }));
1482
- //#endregion
1483
- //#region node_modules/qrcode/lib/core/alignment-pattern.js
1602
+ // node_modules/qrcode/lib/core/alignment-pattern.js
1484
1603
  var require_alignment_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1485
1604
  /**
1486
1605
  * Alignment pattern are fixed reference pattern in defined positions
@@ -1547,8 +1666,7 @@ window.__ModuleLoader__.load({
1547
1666
  return coords;
1548
1667
  };
1549
1668
  }));
1550
- //#endregion
1551
- //#region node_modules/qrcode/lib/core/finder-pattern.js
1669
+ // node_modules/qrcode/lib/core/finder-pattern.js
1552
1670
  var require_finder_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1553
1671
  const getSymbolSize = require_utils$1().getSymbolSize;
1554
1672
  const FINDER_PATTERN_SIZE = 7;
@@ -1568,8 +1686,7 @@ window.__ModuleLoader__.load({
1568
1686
  ];
1569
1687
  };
1570
1688
  }));
1571
- //#endregion
1572
- //#region node_modules/qrcode/lib/core/mask-pattern.js
1689
+ // node_modules/qrcode/lib/core/mask-pattern.js
1573
1690
  var require_mask_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1574
1691
  /**
1575
1692
  * Data mask pattern reference
@@ -1632,18 +1749,18 @@ window.__ModuleLoader__.load({
1632
1749
  sameCountCol = sameCountRow = 0;
1633
1750
  lastCol = lastRow = null;
1634
1751
  for (let col = 0; col < size; col++) {
1635
- let module$1 = data.get(row, col);
1636
- if (module$1 === lastCol) sameCountCol++;
1752
+ let module = data.get(row, col);
1753
+ if (module === lastCol) sameCountCol++;
1637
1754
  else {
1638
1755
  if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5);
1639
- lastCol = module$1;
1756
+ lastCol = module;
1640
1757
  sameCountCol = 1;
1641
1758
  }
1642
- module$1 = data.get(col, row);
1643
- if (module$1 === lastRow) sameCountRow++;
1759
+ module = data.get(col, row);
1760
+ if (module === lastRow) sameCountRow++;
1644
1761
  else {
1645
1762
  if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5);
1646
- lastRow = module$1;
1763
+ lastRow = module;
1647
1764
  sameCountRow = 1;
1648
1765
  }
1649
1766
  }
@@ -1759,8 +1876,7 @@ window.__ModuleLoader__.load({
1759
1876
  return bestPattern;
1760
1877
  };
1761
1878
  }));
1762
- //#endregion
1763
- //#region node_modules/qrcode/lib/core/error-correction-code.js
1879
+ // node_modules/qrcode/lib/core/error-correction-code.js
1764
1880
  var require_error_correction_code = /* @__PURE__ */ __commonJSMin(((exports) => {
1765
1881
  const ECLevel = require_error_correction_level();
1766
1882
  const EC_BLOCKS_TABLE = [
@@ -2122,8 +2238,7 @@ window.__ModuleLoader__.load({
2122
2238
  }
2123
2239
  };
2124
2240
  }));
2125
- //#endregion
2126
- //#region node_modules/qrcode/lib/core/galois-field.js
2241
+ // node_modules/qrcode/lib/core/galois-field.js
2127
2242
  var require_galois_field = /* @__PURE__ */ __commonJSMin(((exports) => {
2128
2243
  const EXP_TABLE = /* @__PURE__ */ new Uint8Array(512);
2129
2244
  const LOG_TABLE = /* @__PURE__ */ new Uint8Array(256);
@@ -2168,8 +2283,7 @@ window.__ModuleLoader__.load({
2168
2283
  return EXP_TABLE[LOG_TABLE[x] + LOG_TABLE[y]];
2169
2284
  };
2170
2285
  }));
2171
- //#endregion
2172
- //#region node_modules/qrcode/lib/core/polynomial.js
2286
+ // node_modules/qrcode/lib/core/polynomial.js
2173
2287
  var require_polynomial = /* @__PURE__ */ __commonJSMin(((exports) => {
2174
2288
  const GF = require_galois_field();
2175
2289
  /**
@@ -2215,8 +2329,7 @@ window.__ModuleLoader__.load({
2215
2329
  return poly;
2216
2330
  };
2217
2331
  }));
2218
- //#endregion
2219
- //#region node_modules/qrcode/lib/core/reed-solomon-encoder.js
2332
+ // node_modules/qrcode/lib/core/reed-solomon-encoder.js
2220
2333
  var require_reed_solomon_encoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2221
2334
  const Polynomial = require_polynomial();
2222
2335
  function ReedSolomonEncoder(degree) {
@@ -2255,8 +2368,7 @@ window.__ModuleLoader__.load({
2255
2368
  };
2256
2369
  module.exports = ReedSolomonEncoder;
2257
2370
  }));
2258
- //#endregion
2259
- //#region node_modules/qrcode/lib/core/version-check.js
2371
+ // node_modules/qrcode/lib/core/version-check.js
2260
2372
  var require_version_check = /* @__PURE__ */ __commonJSMin(((exports) => {
2261
2373
  /**
2262
2374
  * Check if QR Code version is valid
@@ -2268,8 +2380,7 @@ window.__ModuleLoader__.load({
2268
2380
  return !isNaN(version) && version >= 1 && version <= 40;
2269
2381
  };
2270
2382
  }));
2271
- //#endregion
2272
- //#region node_modules/qrcode/lib/core/regex.js
2383
+ // node_modules/qrcode/lib/core/regex.js
2273
2384
  var require_regex = /* @__PURE__ */ __commonJSMin(((exports) => {
2274
2385
  const numeric = "[0-9]+";
2275
2386
  const alphanumeric = "[A-Z $%*+\\-./:]+";
@@ -2294,8 +2405,7 @@ window.__ModuleLoader__.load({
2294
2405
  return TEST_ALPHANUMERIC.test(str);
2295
2406
  };
2296
2407
  }));
2297
- //#endregion
2298
- //#region node_modules/qrcode/lib/core/mode.js
2408
+ // node_modules/qrcode/lib/core/mode.js
2299
2409
  var require_mode = /* @__PURE__ */ __commonJSMin(((exports) => {
2300
2410
  const VersionCheck = require_version_check();
2301
2411
  const Regex = require_regex();
@@ -2451,8 +2561,7 @@ window.__ModuleLoader__.load({
2451
2561
  }
2452
2562
  };
2453
2563
  }));
2454
- //#endregion
2455
- //#region node_modules/qrcode/lib/core/version.js
2564
+ // node_modules/qrcode/lib/core/version.js
2456
2565
  var require_version = /* @__PURE__ */ __commonJSMin(((exports) => {
2457
2566
  const Utils = require_utils$1();
2458
2567
  const ECCode = require_error_correction_code();
@@ -2548,8 +2657,7 @@ window.__ModuleLoader__.load({
2548
2657
  return version << 12 | d;
2549
2658
  };
2550
2659
  }));
2551
- //#endregion
2552
- //#region node_modules/qrcode/lib/core/format-info.js
2660
+ // node_modules/qrcode/lib/core/format-info.js
2553
2661
  var require_format_info = /* @__PURE__ */ __commonJSMin(((exports) => {
2554
2662
  const Utils = require_utils$1();
2555
2663
  const G15 = 1335;
@@ -2572,8 +2680,7 @@ window.__ModuleLoader__.load({
2572
2680
  return (data << 10 | d) ^ G15_MASK;
2573
2681
  };
2574
2682
  }));
2575
- //#endregion
2576
- //#region node_modules/qrcode/lib/core/numeric-data.js
2683
+ // node_modules/qrcode/lib/core/numeric-data.js
2577
2684
  var require_numeric_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2578
2685
  const Mode = require_mode();
2579
2686
  function NumericData(data) {
@@ -2605,8 +2712,7 @@ window.__ModuleLoader__.load({
2605
2712
  };
2606
2713
  module.exports = NumericData;
2607
2714
  }));
2608
- //#endregion
2609
- //#region node_modules/qrcode/lib/core/alphanumeric-data.js
2715
+ // node_modules/qrcode/lib/core/alphanumeric-data.js
2610
2716
  var require_alphanumeric_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2611
2717
  const Mode = require_mode();
2612
2718
  /**
@@ -2689,8 +2795,7 @@ window.__ModuleLoader__.load({
2689
2795
  };
2690
2796
  module.exports = AlphanumericData;
2691
2797
  }));
2692
- //#endregion
2693
- //#region node_modules/qrcode/lib/core/byte-data.js
2798
+ // node_modules/qrcode/lib/core/byte-data.js
2694
2799
  var require_byte_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2695
2800
  const Mode = require_mode();
2696
2801
  function ByteData(data) {
@@ -2712,8 +2817,7 @@ window.__ModuleLoader__.load({
2712
2817
  };
2713
2818
  module.exports = ByteData;
2714
2819
  }));
2715
- //#endregion
2716
- //#region node_modules/qrcode/lib/core/kanji-data.js
2820
+ // node_modules/qrcode/lib/core/kanji-data.js
2717
2821
  var require_kanji_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2718
2822
  const Mode = require_mode();
2719
2823
  const Utils = require_utils$1();
@@ -2743,8 +2847,7 @@ window.__ModuleLoader__.load({
2743
2847
  };
2744
2848
  module.exports = KanjiData;
2745
2849
  }));
2746
- //#endregion
2747
- //#region node_modules/dijkstrajs/dijkstra.js
2850
+ // node_modules/dijkstrajs/dijkstra.js
2748
2851
  var require_dijkstra = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2749
2852
  /******************************************************************************
2750
2853
  * Created 2008-08-19.
@@ -2859,8 +2962,7 @@ window.__ModuleLoader__.load({
2859
2962
  };
2860
2963
  if (typeof module !== "undefined") module.exports = dijkstra;
2861
2964
  }));
2862
- //#endregion
2863
- //#region node_modules/qrcode/lib/core/segments.js
2965
+ // node_modules/qrcode/lib/core/segments.js
2864
2966
  var require_segments = /* @__PURE__ */ __commonJSMin(((exports) => {
2865
2967
  const Mode = require_mode();
2866
2968
  const NumericData = require_numeric_data();
@@ -3138,8 +3240,7 @@ window.__ModuleLoader__.load({
3138
3240
  return exports.fromArray(getSegmentsFromString(data, Utils.isKanjiModeEnabled()));
3139
3241
  };
3140
3242
  }));
3141
- //#endregion
3142
- //#region node_modules/qrcode/lib/core/qrcode.js
3243
+ // node_modules/qrcode/lib/core/qrcode.js
3143
3244
  var require_qrcode = /* @__PURE__ */ __commonJSMin(((exports) => {
3144
3245
  const Utils = require_utils$1();
3145
3246
  const ECLevel = require_error_correction_level();
@@ -3434,8 +3535,7 @@ window.__ModuleLoader__.load({
3434
3535
  return createSymbol(data, version, errorCorrectionLevel, mask);
3435
3536
  };
3436
3537
  }));
3437
- //#endregion
3438
- //#region node_modules/qrcode/lib/renderer/utils.js
3538
+ // node_modules/qrcode/lib/renderer/utils.js
3439
3539
  var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
3440
3540
  function hex2rgba(hex) {
3441
3541
  if (typeof hex === "number") hex = hex.toString();
@@ -3502,8 +3602,7 @@ window.__ModuleLoader__.load({
3502
3602
  }
3503
3603
  };
3504
3604
  }));
3505
- //#endregion
3506
- //#region node_modules/qrcode/lib/renderer/canvas.js
3605
+ // node_modules/qrcode/lib/renderer/canvas.js
3507
3606
  var require_canvas = /* @__PURE__ */ __commonJSMin(((exports) => {
3508
3607
  const Utils = require_utils();
3509
3608
  function clearCanvas(ctx, canvas, size) {
@@ -3551,8 +3650,7 @@ window.__ModuleLoader__.load({
3551
3650
  return canvasEl.toDataURL(type, rendererOpts.quality);
3552
3651
  };
3553
3652
  }));
3554
- //#endregion
3555
- //#region node_modules/qrcode/lib/renderer/svg-tag.js
3653
+ // node_modules/qrcode/lib/renderer/svg-tag.js
3556
3654
  var require_svg_tag = /* @__PURE__ */ __commonJSMin(((exports) => {
3557
3655
  const Utils = require_utils();
3558
3656
  function getColorAttrib(color, attrib) {
@@ -3602,8 +3700,7 @@ window.__ModuleLoader__.load({
3602
3700
  return svgTag;
3603
3701
  };
3604
3702
  }));
3605
- //#endregion
3606
- //#region src/pairing-qr.ts
3703
+ // src/pairing-qr.ts
3607
3704
  var import_browser = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
3608
3705
  const canPromise = require_can_promise();
3609
3706
  const QRCode = require_qrcode();
@@ -3729,8 +3826,7 @@ window.__ModuleLoader__.load({
3729
3826
  if (!grant.audience.startsWith("deeppilot:")) throw new TypeError("pairing audience is invalid");
3730
3827
  if (tlsFingerprint !== void 0 && !isLanTlsFingerprint(tlsFingerprint)) throw new TypeError("pairing TLS fingerprint is invalid");
3731
3828
  }
3732
- //#endregion
3733
- //#region src/client/pairing-panel.ts
3829
+ // src/client/pairing-panel.ts
3734
3830
  const initialPairingPanelState = {
3735
3831
  target: null,
3736
3832
  busy: false,
@@ -3804,16 +3900,14 @@ window.__ModuleLoader__.load({
3804
3900
  function normalizeFunnelConnectionLimit(value) {
3805
3901
  return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 16 ? value : 8;
3806
3902
  }
3807
- //#endregion
3808
- //#region src/local-policy.ts
3903
+ // src/local-policy.ts
3809
3904
  const DEFAULT_LOCAL_PORT = 3098;
3810
3905
  const MIN_LOCAL_PORT = 1024;
3811
3906
  const MAX_LOCAL_PORT = 65535;
3812
3907
  function normalizeLocalPort(value) {
3813
3908
  return typeof value === "number" && Number.isInteger(value) && value >= 1024 && value <= 65535 ? value : DEFAULT_LOCAL_PORT;
3814
3909
  }
3815
- //#endregion
3816
- //#region src/client/settings-props.ts
3910
+ // src/client/settings-props.ts
3817
3911
  const MISSING_REPORT_HOOK = () => ({
3818
3912
  status: "loading",
3819
3913
  report: null,
@@ -3948,17 +4042,7 @@ window.__ModuleLoader__.load({
3948
4042
  reportHookMissing: !isFn(source.useDeepPilotReport)
3949
4043
  };
3950
4044
  }
3951
- //#endregion
3952
- //#region src/client/settings-view-models.ts
3953
- /**
3954
- * 设置页的 view model:把原先住在渲染函数里的三块逻辑变成可单测的纯模块。
3955
- *
3956
- * 迁移前 `settings-page.ts` 是一个 1059 行的组件,funnel phase → {dot, label}、
3957
- 端口/连接数草稿的编辑与合法性、serviceStatus 派生全在渲染函数里,没有任何
3958
- 直接测试。这里按 pairing-panel.ts 的形状把它们拆成 state / action / reduce /
3959
- view 四件套——shape 与该先例一致,但**不继承它唯一的泄漏**:view model 只返回
3960
- 语义值,i18n key 的映射留在页面(措辞归页面,测试不跟着措辞漂)。
3961
- */
4045
+ // src/client/settings-view-models.ts
3962
4046
  const REMOTE_PHASE_META = {
3963
4047
  disabled: {
3964
4048
  dot: "",
@@ -4093,26 +4177,10 @@ window.__ModuleLoader__.load({
4093
4177
  function deviceLabel(device) {
4094
4178
  return (device.customName ?? device.deviceName).trim();
4095
4179
  }
4096
- //#endregion
4097
- //#region src/client/settings-page.ts
4098
- /** phase 语义标签 → i18n key。措辞归页面,view model 只认语义。 */
4099
- const REMOTE_LABEL_KEY = {
4100
- disabled: "phase.disabled",
4101
- starting: "phase.starting",
4102
- login_required: "phase.login_required",
4103
- online: "phase.online",
4104
- error: "phase.error",
4105
- unavailable: "phase.unavailable",
4106
- stopped: "phase.stopped"
4107
- };
4108
- const LOCAL_LABEL_KEY = {
4109
- disabled: "local.phaseDisabled",
4110
- starting: "local.phaseStarting",
4111
- online: "local.phaseOnline",
4112
- error: "local.phaseError",
4113
- stopped: "local.phaseStopped"
4114
- };
4115
- /** 状态条用的短状态:列标签已经写明是哪条通道,值只说状态,免得在窄格里被截断。 */
4180
+ // src/client/settings-page.ts
4181
+ /** 中文结论由调用处提供,原始上游信息统一放入括号,保留排查依据。 */
4182
+ const errorDetails = (error) => "(" + (error instanceof Error ? error.message : String(error)) + ")";
4183
+ /** 通道标题旁的短状态;完整端点和参数放入对应的连接选项。 */
4116
4184
  const CHANNEL_STATE_KEY = {
4117
4185
  disabled: "channel.state.disabled",
4118
4186
  starting: "channel.state.starting",
@@ -4129,21 +4197,6 @@ window.__ModuleLoader__.load({
4129
4197
  attention: "status.attention",
4130
4198
  ready: "status.ready"
4131
4199
  };
4132
- /** Inline trash icon for the compact destructive row action. Kept as a tiny
4133
- * element (no icon dependency) so the client bundle stays dependency-free
4134
- * apart from React. */
4135
- const TrashIcon = () => (0, react.createElement)("svg", {
4136
- className: "pbb-actionIcon",
4137
- viewBox: "0 0 16 16",
4138
- width: 13,
4139
- height: 13,
4140
- fill: "none",
4141
- stroke: "currentColor",
4142
- strokeWidth: 1.5,
4143
- strokeLinecap: "round",
4144
- strokeLinejoin: "round",
4145
- "aria-hidden": true
4146
- }, (0, react.createElement)("path", { d: "M2.5 4.5h11" }), (0, react.createElement)("path", { d: "M6.5 2.5h3" }), (0, react.createElement)("path", { d: "M4 4.5l.6 8.2a1 1 0 0 0 1 .9h4.8a1 1 0 0 0 1-.9l.6-8.2" }), (0, react.createElement)("path", { d: "M6.6 7v4.2" }), (0, react.createElement)("path", { d: "M9.4 7v4.2" }));
4147
4200
  /** Chevron icon (linear, currentColor). CSS rotates it 90° while its owner is
4148
4201
  * expanded, so collapsed always means "points right, more content behind". */
4149
4202
  const ChevronIcon = () => (0, react.createElement)("svg", {
@@ -4158,8 +4211,7 @@ window.__ModuleLoader__.load({
4158
4211
  strokeLinejoin: "round",
4159
4212
  "aria-hidden": true
4160
4213
  }, (0, react.createElement)("path", { d: "M6 3.5 10.5 8 6 12.5" }));
4161
- /** Check/cross marks for the relay and push result lines. They used to be the
4162
- * dingbats '✓'/'✗', which the repo's visual rules reject as character icons. */
4214
+ /** 诊断结果使用线性 SVG,与页面其余图标保持一致。 */
4163
4215
  const CheckIcon = () => (0, react.createElement)("svg", {
4164
4216
  viewBox: "0 0 16 16",
4165
4217
  width: 12,
@@ -4226,6 +4278,13 @@ window.__ModuleLoader__.load({
4226
4278
  const [remoteLimitDraft, dispatchRemoteLimitDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, REMOTE_LIMIT_RANGE), initialNumericDraft(REMOTE_LIMIT_RANGE));
4227
4279
  const [localPortDraft, dispatchLocalPortDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, LOCAL_PORT_RANGE), initialNumericDraft(LOCAL_PORT_RANGE));
4228
4280
  const [selectedPairingHost, setSelectedPairingHost] = (0, react.useState)(null);
4281
+ const [pairingOpen, setPairingOpen] = (0, react.useState)(false);
4282
+ const pageId = (0, react.useId)();
4283
+ const localOptionsId = pageId + "-local";
4284
+ const remoteOptionsId = pageId + "-remote";
4285
+ const troubleshootingId = pageId + "-troubleshooting";
4286
+ const systemId = pageId + "-system";
4287
+ const pairingId = pageId + "-pairing";
4229
4288
  const [pairingPanel, dispatchPairingPanel] = (0, react.useReducer)(pairingPanelReduce, initialPairingPanelState);
4230
4289
  const qrRequestId = (0, react.useRef)(0);
4231
4290
  const [troubleshootingOpen, setTroubleshootingOpen] = (0, react.useState)(false);
@@ -4236,7 +4295,6 @@ window.__ModuleLoader__.load({
4236
4295
  const [pushTestResult, setPushTestResult] = (0, react.useState)(null);
4237
4296
  const [pushTestError, setPushTestError] = (0, react.useState)("");
4238
4297
  const [deviceBusy, setDeviceBusy] = (0, react.useState)(null);
4239
- const [deviceBusyAction, setDeviceBusyAction] = (0, react.useState)(null);
4240
4298
  const [deviceMessage, setDeviceMessage] = (0, react.useState)("");
4241
4299
  const [editingDeviceId, setEditingDeviceId] = (0, react.useState)(null);
4242
4300
  const [deviceNameDraft, setDeviceNameDraft] = (0, react.useState)("");
@@ -4270,7 +4328,7 @@ window.__ModuleLoader__.load({
4270
4328
  setPushTestResult(result);
4271
4329
  setPushTestBusy(false);
4272
4330
  }, (error) => {
4273
- setPushTestError(error instanceof Error ? error.message : String(error));
4331
+ setPushTestError(translateWith(props.t, "push.pushFailed") + errorDetails(error));
4274
4332
  setPushTestBusy(false);
4275
4333
  });
4276
4334
  };
@@ -4287,7 +4345,7 @@ window.__ModuleLoader__.load({
4287
4345
  setRelayTestResult(result);
4288
4346
  setRelayTestBusy(false);
4289
4347
  }, (error) => {
4290
- setRelayTestError(error instanceof Error ? error.message : String(error));
4348
+ setRelayTestError(translateWith(props.t, "push.relayBad") + errorDetails(error));
4291
4349
  setRelayTestBusy(false);
4292
4350
  });
4293
4351
  };
@@ -4323,7 +4381,7 @@ window.__ModuleLoader__.load({
4323
4381
  const reportState = props.useDeepPilotReport((s) => s);
4324
4382
  hooks.report = reportState.report;
4325
4383
  if (reportState.status === "error" && reportState.message) {
4326
- diag.push(reportState.message);
4384
+ diag.push(translateWith(props.t, "diag.reportFailed") + errorDetails(reportState.message));
4327
4385
  failed = true;
4328
4386
  }
4329
4387
  const enabledState = props.useDeepPilotEnabled((s) => s);
@@ -4364,7 +4422,7 @@ window.__ModuleLoader__.load({
4364
4422
  try {
4365
4423
  readHooks();
4366
4424
  } catch (error) {
4367
- diag.push(translateWith(props.t, "diag.renderError") + (error instanceof Error ? error.message : String(error)));
4425
+ diag.push(translateWith(props.t, "diag.renderError") + errorDetails(error));
4368
4426
  failed = true;
4369
4427
  }
4370
4428
  const report = hooks.report;
@@ -4411,7 +4469,7 @@ window.__ModuleLoader__.load({
4411
4469
  }, (error) => {
4412
4470
  dispatchLocalPortDraft({
4413
4471
  type: "failed",
4414
- message: translateWith(props.t, "local.portFailed") + (error instanceof Error ? error.message : String(error))
4472
+ message: translateWith(props.t, "local.portFailed") + errorDetails(error)
4415
4473
  });
4416
4474
  });
4417
4475
  };
@@ -4432,7 +4490,7 @@ window.__ModuleLoader__.load({
4432
4490
  }, (error) => {
4433
4491
  dispatchRemoteLimitDraft({
4434
4492
  type: "failed",
4435
- message: translateWith(props.t, "remote.limitFailed") + (error instanceof Error ? error.message : String(error))
4493
+ message: translateWith(props.t, "remote.limitFailed") + errorDetails(error)
4436
4494
  });
4437
4495
  });
4438
4496
  };
@@ -4482,7 +4540,7 @@ window.__ModuleLoader__.load({
4482
4540
  dispatchPairingPanel({
4483
4541
  type: "failed",
4484
4542
  requestId,
4485
- message: translateWith(props.t, "pair.qrFailed") + (error instanceof Error ? error.message : String(error))
4543
+ message: translateWith(props.t, "pair.qrFailed") + errorDetails(error)
4486
4544
  });
4487
4545
  });
4488
4546
  };
@@ -4505,7 +4563,7 @@ window.__ModuleLoader__.load({
4505
4563
  }, (error) => {
4506
4564
  dispatchPairingPanel({
4507
4565
  type: "notice",
4508
- message: translateWith(props.t, "pair.infoCopyFailed") + (error instanceof Error ? error.message : String(error))
4566
+ message: translateWith(props.t, "pair.infoCopyFailed") + errorDetails(error)
4509
4567
  });
4510
4568
  });
4511
4569
  };
@@ -4526,15 +4584,13 @@ window.__ModuleLoader__.load({
4526
4584
  const revokeDevice = (deviceId, _name) => {
4527
4585
  if (!can("revokeDevice")) return;
4528
4586
  setDeviceBusy(deviceId);
4529
- setDeviceBusyAction("revoke");
4530
4587
  setDeviceMessage("");
4531
4588
  props.revokeDevice(deviceId).then(() => {
4532
4589
  setDeviceMessage(translateWith(props.t, "devices.revoked"));
4533
4590
  }, (error) => {
4534
- setDeviceMessage(translateWith(props.t, "devices.revokeFailed") + (error instanceof Error ? error.message : String(error)));
4591
+ setDeviceMessage(translateWith(props.t, "devices.revokeFailed") + errorDetails(error));
4535
4592
  }).finally(() => {
4536
4593
  setDeviceBusy(null);
4537
- setDeviceBusyAction(null);
4538
4594
  });
4539
4595
  };
4540
4596
  const deviceLabel$1 = (device) => deviceLabel(device) || translateWith(props.t, "devices.unnamed");
@@ -4557,17 +4613,15 @@ window.__ModuleLoader__.load({
4557
4613
  return;
4558
4614
  }
4559
4615
  setDeviceBusy(deviceId);
4560
- setDeviceBusyAction("rename");
4561
4616
  setDeviceMessage("");
4562
4617
  props.setDeviceName(deviceId, normalized === "" ? null : normalized).then(() => {
4563
4618
  setDeviceMessage(normalized === "" ? translateWith(props.t, "devices.nameCleared") : translateWith(props.t, "devices.renamed"));
4564
4619
  setEditingDeviceId(null);
4565
4620
  setDeviceNameDraft("");
4566
4621
  }, (error) => {
4567
- setDeviceMessage(translateWith(props.t, "devices.renameFailed") + (error instanceof Error ? error.message : String(error)));
4622
+ setDeviceMessage(translateWith(props.t, "devices.renameFailed") + errorDetails(error));
4568
4623
  }).finally(() => {
4569
4624
  setDeviceBusy(null);
4570
- setDeviceBusyAction(null);
4571
4625
  });
4572
4626
  };
4573
4627
  const visibleDevices$1 = visibleDevices(report);
@@ -4583,10 +4637,10 @@ window.__ModuleLoader__.load({
4583
4637
  "data-armed": armed || void 0,
4584
4638
  "aria-busy": busy,
4585
4639
  "aria-label": translateWith(props.t, "devices.col.name") + ": " + label
4586
- }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", {
4640
+ }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, settingsIcon("phone"), (0, react.createElement)("div", { className: "pbb-deviceText" }, (0, react.createElement)("span", {
4587
4641
  className: "pbb-deviceName",
4588
4642
  title: label
4589
- }, label), d.customName ? (0, react.createElement)("span", { className: "pbb-badge" }, translateWith(props.t, "devices.customBadge")) : null), (0, react.createElement)("div", { className: "pbb-deviceMeta" }, (0, react.createElement)("span", null, d.appVersion), (0, react.createElement)("code", { title: fingerprint }, fingerprint.slice(0, 12)), (0, react.createElement)("span", null, translateWith(props.t, "devices.col.lastSeen") + ": " + formatLastSeen(d.lastSeenTs)))), editing ? (0, react.createElement)("form", {
4643
+ }, label), (0, react.createElement)("div", { className: "pbb-deviceMeta" }, (0, react.createElement)("span", null, d.appVersion), (0, react.createElement)("span", null, translateWith(props.t, "devices.col.lastSeen") + " " + formatLastSeen(d.lastSeenTs))))), editing ? (0, react.createElement)("form", {
4590
4644
  className: "pbb-deviceRenameForm",
4591
4645
  onSubmit: (event) => {
4592
4646
  event.preventDefault();
@@ -4624,25 +4678,22 @@ window.__ModuleLoader__.load({
4624
4678
  title: translateWith(props.t, "devices.nameCancel"),
4625
4679
  "aria-label": translateWith(props.t, "devices.nameCancel"),
4626
4680
  onClick: cancelDeviceRename
4627
- }, translateWith(props.t, "devices.nameCancel"))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : null, armed ? null : (0, react.createElement)("div", {
4628
- className: "pbb-deviceActions",
4629
- "aria-label": translateWith(props.t, "devices.col.actions")
4630
- }, !editing ? (0, react.createElement)("button", {
4631
- type: "button",
4632
- className: "pbb-action",
4633
- disabled: busy || editingDeviceId !== null || !can("setDeviceName"),
4634
- "aria-label": translateWith(props.t, "devices.renameAria", { name: label }),
4635
- onClick: () => beginDeviceRename(d)
4636
- }, translateWith(props.t, "devices.rename")) : null, (0, react.createElement)("button", {
4637
- type: "button",
4638
- className: "pbb-action pbb-actionDangerGhost",
4639
- disabled: busy,
4640
- "aria-label": busy && deviceBusyAction === "revoke" ? translateWith(props.t, "devices.deleting", { name: label }) : translateWith(props.t, "devices.revokeAria", { name: label }),
4641
- onClick: () => {
4681
+ }, translateWith(props.t, "devices.nameCancel"))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : null, armed || editing ? null : (0, react.createElement)("div", { className: "pbb-deviceActions" }, busy ? (0, react.createElement)("span", {
4682
+ className: "pbb-actionBusy",
4683
+ role: "status"
4684
+ }, translateWith(props.t, "devices.deleting")) : (0, react.createElement)(DeviceActions, {
4685
+ name: label,
4686
+ fingerprint,
4687
+ customName: d.customName,
4688
+ canRename: editingDeviceId === null && can("setDeviceName"),
4689
+ canRevoke: can("revokeDevice"),
4690
+ t: props.t,
4691
+ onRename: () => beginDeviceRename(d),
4692
+ onRevoke: () => {
4642
4693
  cancelDeviceRename();
4643
4694
  setArmedRevokeDeviceId(d.deviceId);
4644
4695
  }
4645
- }, busy && deviceBusyAction === "revoke" ? null : TrashIcon(), busy && deviceBusyAction === "revoke" ? (0, react.createElement)("span", { className: "pbb-actionBusy" }, translateWith(props.t, "devices.deleting")) : translateWith(props.t, "devices.revoke"))), armed && !busy ? (0, react.createElement)("div", { className: "pbb-deviceConfirm" }, (0, react.createElement)("p", {
4696
+ })), armed && !busy ? (0, react.createElement)("div", { className: "pbb-deviceConfirm" }, (0, react.createElement)("p", {
4646
4697
  className: "pbb-diag pbb-diagBad",
4647
4698
  role: "alert"
4648
4699
  }, translateWith(props.t, "devices.revokeConfirm", { name: label })), (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
@@ -4660,27 +4711,32 @@ window.__ModuleLoader__.load({
4660
4711
  }, translateWith(props.t, "devices.nameCancel")))) : null);
4661
4712
  })) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
4662
4713
  const switchTitle = translateWith(props.t, "master.title");
4663
- const switchDesc = switchReady ? enabled ? translateWith(props.t, "master.on") : translateWith(props.t, "master.off") : translateWith(props.t, "master.loading");
4664
4714
  const localMeta = report === null ? null : LOCAL_PHASE_META[report.local.phase];
4665
4715
  const remoteMeta = report === null ? null : REMOTE_PHASE_META[report.remote.phase];
4666
4716
  const service = serviceStatus({
4667
4717
  enabled,
4668
- switchReady,
4718
+ switchReady: switchReady && report !== null,
4669
4719
  localDot: localMeta?.dot,
4670
4720
  remoteDot: remoteMeta?.dot
4671
4721
  });
4672
4722
  const serviceStatusText = translateWith(props.t, SERVICE_STATUS_KEY[service.kind]);
4673
- const serviceDot = service.dot;
4674
- const localPhaseText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, LOCAL_LABEL_KEY[localMeta.label]);
4675
- const remotePhaseText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, REMOTE_LABEL_KEY[remoteMeta.label]);
4676
4723
  const localStateText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[localMeta.label]);
4677
4724
  const remoteStateText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[remoteMeta.label]);
4678
4725
  /** 一条失败事件的单行文本:时间 · 类型 · 分因,附带可选排查字段。 */
4679
4726
  const diagnosticLine = (failure) => {
4680
4727
  const base = translateWith(props.t, "diagnostics.item", {
4681
4728
  time: new Date(failure.ts).toLocaleString(),
4682
- kind: failure.kind,
4683
- reason: failure.reason
4729
+ kind: translateWith(props.t, {
4730
+ "upgrade-rejected": "diagnostics.kind.upgrade",
4731
+ "pair-rejected": "diagnostics.kind.pair",
4732
+ "auth-failed": "diagnostics.kind.auth",
4733
+ "connection-opened": "diagnostics.kind.opened",
4734
+ "connection-closed": "diagnostics.kind.closed",
4735
+ "connection-error": "diagnostics.kind.error",
4736
+ identity: "diagnostics.kind.identity",
4737
+ storage: "diagnostics.kind.storage"
4738
+ }[failure.kind] ?? "diagnostics.kind.unknown"),
4739
+ reason: "(" + failure.kind + " / " + failure.reason + ")"
4684
4740
  });
4685
4741
  const extras = [];
4686
4742
  if (failure.attemptId !== void 0) extras.push(translateWith(props.t, "diagnostics.attempt", { id: failure.attemptId }));
@@ -4717,12 +4773,12 @@ window.__ModuleLoader__.load({
4717
4773
  });
4718
4774
  }, (error) => {
4719
4775
  setDiagnosticsCopy({
4720
- message: translateWith(props.t, "diagnostics.copyFailed") + (error instanceof Error ? error.message : String(error)),
4776
+ message: translateWith(props.t, "diagnostics.copyFailed") + errorDetails(error),
4721
4777
  failed: true
4722
4778
  });
4723
4779
  });
4724
4780
  };
4725
- const diagnosticsSection = report === null ? null : (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "diagnostics.title")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "diagnostics.description"))), (0, react.createElement)("button", {
4781
+ const diagnosticsSection = report === null ? null : (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "diagnostics.title")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "diagnostics.description"))), (0, react.createElement)("button", {
4726
4782
  type: "button",
4727
4783
  className: "pbb-action",
4728
4784
  onClick: copyDiagnostics,
@@ -4734,14 +4790,10 @@ window.__ModuleLoader__.load({
4734
4790
  className: "pbb-diag",
4735
4791
  key: String(index)
4736
4792
  }, diagnosticLine(failure)))));
4737
- /** 状态总览的一格:标签 / 点位 + 值 / 次行;整格 title 兜住被裁掉的全文。
4738
- * dot 为 null 表示这一格是计数而非状态,不画点位(灰点会被读成「未启用」)。 */
4739
- const statusCell = (label, dot, value, sub) => (0, react.createElement)("div", {
4740
- className: "pbb-statusCell",
4741
- title: label + " · " + value + (sub === "" ? "" : " · " + sub)
4742
- }, (0, react.createElement)("div", { className: "pbb-statusLabel" }, label), (0, react.createElement)("div", { className: "pbb-statusValue" }, dot === null ? null : (0, react.createElement)("span", { className: "pbb-dot" + dot }), value), (0, react.createElement)("div", { className: "pbb-statusSub" }, sub));
4743
- /** 折叠区:grid-rows 0fr→1fr 平滑开合(mockup 的做法),不用 <details> 的瞬时跳变。 */
4744
- const fold = (open, body) => (0, react.createElement)("div", { className: "pbb-fold" + (open ? "" : " pbb-foldClosed") }, (0, react.createElement)("div", null, body));
4793
+ const fold = (open, body) => (0, react.createElement)("div", {
4794
+ className: "pbb-fold",
4795
+ hidden: !open
4796
+ }, body);
4745
4797
  /** 配对倒计时:仅在 QR 面板打开且 grant 在场时每秒刷新一次。 */
4746
4798
  (0, react.useEffect)(() => {
4747
4799
  if (!panel.open || pairingPanel.grant === null) return;
@@ -4751,44 +4803,19 @@ window.__ModuleLoader__.load({
4751
4803
  const grantRemainingMs = pairingPanel.grant === null ? 0 : Math.max(0, pairingPanel.grant.expiresAt - nowMs);
4752
4804
  const grantRemainingText = String(Math.floor(grantRemainingMs / 6e4)) + ":" + String(Math.floor(grantRemainingMs / 1e3) % 60).padStart(2, "0");
4753
4805
  const grantRemainingPct = Math.min(100, grantRemainingMs / 3e5 * 100);
4754
- const localDesc = localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading");
4755
- const remoteDesc = remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading");
4756
- /** 端点 → 状态条副行只暴露 host[:port],去掉协议前缀让一行能放下。 */
4757
- const endpointHost = (value) => {
4758
- if (value === void 0 || value === "") return void 0;
4759
- try {
4760
- return new URL(value).host;
4761
- } catch {
4762
- return value;
4763
- }
4764
- };
4765
- const localStatusSub = !localSwitchReady ? translateWith(props.t, "master.loading") : report !== null && report.local.phase === "online" ? endpointHost(report.local.endpoints[0]) ?? localDesc : report !== null && report.local.phase === "error" ? report.local.message !== void 0 && report.local.message !== "" ? report.local.message : localDesc : report !== null && (report.local.phase === "disabled" || report.local.phase === "stopped") ? translateWith(props.t, "local.off") : localDesc;
4766
- const remoteStatusSub = !remoteSwitchReady ? translateWith(props.t, "master.loading") : report !== null && report.remote.phase === "online" ? endpointHost(report.remote.publicURL) ?? remoteDesc : report !== null && report.remote.phase === "error" ? report.remote.message !== void 0 && report.remote.message !== "" ? report.remote.message : remoteDesc : report !== null && report.remote.phase === "disabled" ? translateWith(props.t, "remote.off") : remoteDesc;
4767
- const isOffPhase = (phase) => phase === "disabled" || phase === "stopped";
4768
- const localSettled = report === null || (localEnabled ? report.local.phase === "online" : isOffPhase(report.local.phase));
4769
- const remoteSettled = report === null || (remoteEnabled ? report.remote.phase === "online" : isOffPhase(report.remote.phase));
4770
- const localChannelDesc = localSettled ? localDesc : localPhaseText + " · " + localDesc;
4771
- const remoteChannelDesc = remoteSettled ? remoteDesc : remotePhaseText + " · " + remoteDesc;
4772
4806
  const recentFailures = report?.diagnostics?.recentFailures ?? [];
4773
4807
  const troubleGood = relayTestResult !== null ? relayTestResult.overall === "ok" : pushTestResult !== null ? pushTestResult.overall === "sent" : false;
4774
4808
  const troubleBad = relayTestResult !== null ? relayTestResult.overall !== "ok" : pushTestResult !== null ? pushTestResult.overall !== "sent" : recentFailures.length > 0;
4775
- const troubleConclusion = relayTestResult !== null ? relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad") : pushTestResult !== null ? pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : translateWith(props.t, "push.pushFailed") : recentFailures.length > 0 ? translateWith(props.t, "diagnostics.recent") + " " + String(recentFailures.length) : translateWith(props.t, "troubleshooting.title");
4776
- return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
4777
- type: "button",
4778
- className: "pbb-refresh",
4779
- onClick: () => {
4780
- if (can("refresh")) props.refresh();
4781
- }
4782
- }, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("section", { className: "pbb-statusStrip" }, statusCell(translateWith(props.t, "master.title"), serviceDot, serviceStatusText, switchDesc), statusCell(translateWith(props.t, "local.title"), localMeta?.dot ?? "", localStateText, localStatusSub), statusCell(translateWith(props.t, "remote.title"), remoteMeta?.dot ?? "", remoteStateText, remoteStatusSub), statusCell(translateWith(props.t, "panel.activeConnections"), null, report === null ? translateWith(props.t, "status.loading") : String(report.activeConnections), translateWith(props.t, "panel.identity") + " · " + (report !== null && report.pairingReady ? translateWith(props.t, "panel.identityReady") : translateWith(props.t, "panel.identityNotReady")))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "pair.qrPanelTitle")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "pair.description"))), pairingTarget === null ? (0, react.createElement)("span", { className: "pbb-statusBadge" + (enabled ? " pbb-bad" : "") }, enabled ? translateWith(props.t, "pair.noAddress") : translateWith(props.t, "status.off")) : (0, react.createElement)("button", {
4809
+ const troubleConclusion = relayTestResult !== null ? relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad") : pushTestResult !== null ? pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : translateWith(props.t, "push.pushFailed") : recentFailures.length > 0 ? translateWith(props.t, "diagnostics.historyCount", { count: recentFailures.length }) : "";
4810
+ const pairingSection = (0, react.createElement)("div", {
4811
+ id: pairingId,
4812
+ hidden: !pairingOpen
4813
+ }, !pairingOpen || report === null ? null : (0, react.createElement)("div", { className: "pbb-pairingPanel" }, (0, react.createElement)("div", { className: "pbb-pairingHeader" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "pair.qrPanelTitle")), !panel.open && pairingTarget !== null ? (0, react.createElement)("button", {
4783
4814
  type: "button",
4784
- className: "pbb-action pbb-buttonPrimary",
4785
- disabled: panel.busy || report !== null && !report.pairingReady,
4786
- onClick: () => {
4787
- if (pairingTarget === null) return;
4788
- if (panel.open) hidePairingQR();
4789
- else showPairingQR(pairingTarget);
4790
- }
4791
- }, translateWith(props.t, panel.action))), (0, react.createElement)("div", { className: "pbb-pairingBody" }, pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag" }, enabled ? translateWith(props.t, "pair.noAddressHelp") : translateWith(props.t, "pair.disabledHelp")) : null, pairingTargets.length < 2 ? null : (0, react.createElement)("div", { className: "pbb-targetList" }, pairingTargets.map((target) => (0, react.createElement)("button", {
4815
+ className: "pbb-action",
4816
+ disabled: panel.busy || !report.pairingReady,
4817
+ onClick: () => showPairingQR(pairingTarget)
4818
+ }, translateWith(props.t, "pair.qrShow")) : null), (0, react.createElement)("div", { className: "pbb-pairingBody" }, pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag" }, enabled ? translateWith(props.t, "pair.noAddressHelp") : translateWith(props.t, "pair.disabledHelp")) : null, pairingTargets.length < 2 ? null : (0, react.createElement)("div", { className: "pbb-targetList" }, pairingTargets.map((target) => (0, react.createElement)("button", {
4792
4819
  type: "button",
4793
4820
  key: target.host,
4794
4821
  className: "pbb-targetButton" + (target.host === pairingTarget?.host ? " pbb-targetSelected" : ""),
@@ -4818,35 +4845,49 @@ window.__ModuleLoader__.load({
4818
4845
  className: "pbb-action pbb-buttonPrimary",
4819
4846
  onClick: copyPairingInfo,
4820
4847
  "data-testid": "pairingInfoCopy"
4821
- }, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") })))) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "connection.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "connection.description")))), (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-masterRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, switchTitle), (0, react.createElement)("span", { className: "pbb-switchDesc" }, switchDesc)), (0, react.createElement)("button", {
4848
+ }, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") })))) : null)));
4849
+ return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
4822
4850
  type: "button",
4823
- role: "switch",
4824
- "aria-checked": enabled,
4825
- "aria-label": switchTitle,
4851
+ className: "pbb-refresh",
4852
+ disabled: !can("refresh"),
4853
+ onClick: () => props.refresh?.()
4854
+ }, settingsIcon("refresh", 14), translateWith(props.t, "meta.refresh"))), (0, react.createElement)("section", {
4855
+ className: "pbb-card pbb-connections",
4856
+ "aria-label": translateWith(props.t, "connection.title")
4857
+ }, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-masterRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, switchTitle), (0, react.createElement)("div", { className: "pbb-serviceSummary" }, (0, react.createElement)("span", { className: "pbb-serviceState" + (service.kind === "ready" ? " pbb-ok" : service.kind === "attention" ? " pbb-bad" : "") }, settingsIcon(service.kind === "ready" ? "ready" : service.kind === "attention" ? "attention" : "info", 13)), (0, react.createElement)("span", null, serviceStatusText), (0, react.createElement)("span", {
4858
+ className: "pbb-metaDivider",
4859
+ "aria-hidden": true
4860
+ }), (0, react.createElement)("span", null, translateWith(props.t, "panel.activeConnections") + " " + (report === null ? "—" : String(report.activeConnections))))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4861
+ checked: enabled,
4862
+ label: switchTitle,
4826
4863
  disabled: !switchReady,
4827
- className: "pbb-switch" + (enabled ? " pbb-switchOn" : ""),
4828
- onClick: () => {
4829
- if (can("setEnabled")) props.setDeepPilotEnabled(!enabled);
4864
+ onChange: (next) => {
4865
+ if (can("setEnabled")) props.setDeepPilotEnabled(next);
4830
4866
  }
4831
- })), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, (0, react.createElement)("span", { className: "pbb-dot" + (localMeta?.dot ?? "") }), " ", translateWith(props.t, "local.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, localChannelDesc)), (0, react.createElement)("button", {
4832
- type: "button",
4833
- role: "switch",
4834
- "aria-checked": localEnabled,
4835
- "aria-label": translateWith(props.t, "local.title"),
4867
+ })), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row pbb-channelRow" }, settingsIcon("wifi"), (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, translateWith(props.t, "local.title")), (0, react.createElement)("div", { className: "pbb-channelState" }, (0, react.createElement)("span", { className: localMeta?.label === "online" ? "pbb-ok" : localMeta?.label === "error" ? "pbb-bad" : void 0 }, localStateText))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4868
+ checked: localEnabled,
4869
+ label: translateWith(props.t, "local.title"),
4836
4870
  disabled: !localSwitchReady,
4837
- className: "pbb-switch" + (localEnabled ? " pbb-switchOn" : ""),
4838
- onClick: () => {
4839
- if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
4871
+ onChange: (next) => {
4872
+ if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(next);
4840
4873
  }
4841
- })), report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.local.message) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null, (0, react.createElement)("button", {
4874
+ }), (0, react.createElement)("button", {
4842
4875
  type: "button",
4843
- className: "pbb-expand",
4876
+ className: "pbb-iconButton pbb-expand",
4877
+ "aria-label": translateWith(props.t, "local.options"),
4844
4878
  "aria-expanded": localOptionsOpen,
4879
+ "aria-controls": localOptionsId,
4845
4880
  onClick: () => setLocalOptionsOpen(!localOptionsOpen)
4846
- }, translateWith(props.t, "connection.optionsSummary"), " ", (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(localOptionsOpen, (0, react.createElement)("div", { className: "pbb-channelBody" }, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4847
- className: "pbb-switchTitle",
4881
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()))), report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", {
4882
+ className: "pbb-diag pbb-diagBad",
4883
+ role: "alert"
4884
+ }, translateWith(props.t, "local.errorDetails", { message: report.local.message })) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null, fold(localOptionsOpen, (0, react.createElement)("div", {
4885
+ className: "pbb-channelBody",
4886
+ id: localOptionsId
4887
+ }, report !== null && report.local.phase === "online" ? (0, react.createElement)("p", { className: "pbb-diag pbb-endpoint" }, report.local.endpoints.join(" · ")) : null, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("label", {
4888
+ className: "pbb-controlTitle",
4848
4889
  htmlFor: "deeppilot-local-port"
4849
- }, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "local.portDescription")), !localPortView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.portInvalid")) : localPortDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (localPortDraft.failed ? " pbb-diagBad" : "") }, localPortDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
4890
+ }, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-controlDescription" }, translateWith(props.t, "local.portDescription")), !localPortView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.portInvalid")) : localPortDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (localPortDraft.failed ? " pbb-diagBad" : "") }, localPortDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
4850
4891
  id: "deeppilot-local-port",
4851
4892
  className: "pbb-numberInput",
4852
4893
  type: "number",
@@ -4872,30 +4913,38 @@ window.__ModuleLoader__.load({
4872
4913
  className: "pbb-action",
4873
4914
  disabled: !localPortReady || !localPortView.valid || localPortView.unchanged,
4874
4915
  onClick: applyLocalPort
4875
- }, translateWith(props.t, "remote.limitApply")))), report !== null && report.local.phase === "online" && report.local.tlsFingerprint ? (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, translateWith(props.t, "local.tlsFingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, report.local.tlsFingerprint))) : null))), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, (0, react.createElement)("span", { className: "pbb-dot" + (remoteMeta?.dot ?? "") }), " ", translateWith(props.t, "remote.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, remoteChannelDesc)), (0, react.createElement)("button", {
4876
- type: "button",
4877
- role: "switch",
4878
- "aria-checked": remoteEnabled,
4879
- "aria-label": translateWith(props.t, "remote.title"),
4916
+ }, translateWith(props.t, "remote.limitApply")))), report !== null && report.local.phase === "online" && report.local.tlsFingerprint ? (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, translateWith(props.t, "local.tlsFingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, report.local.tlsFingerprint))) : null))), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row pbb-channelRow" }, settingsIcon("globe"), (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, translateWith(props.t, "remote.title")), (0, react.createElement)("div", { className: "pbb-channelState" }, (0, react.createElement)("span", { className: remoteMeta?.label === "online" ? "pbb-ok" : remoteMeta?.label === "error" || remoteMeta?.label === "unavailable" ? "pbb-bad" : void 0 }, remoteStateText), (0, react.createElement)("span", {
4917
+ className: "pbb-metaDivider",
4918
+ "aria-hidden": true
4919
+ }), (0, react.createElement)("span", null, "Tailscale Funnel"))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4920
+ checked: remoteEnabled,
4921
+ label: translateWith(props.t, "remote.title"),
4880
4922
  disabled: !remoteSwitchReady,
4881
- className: "pbb-switch" + (remoteEnabled ? " pbb-switchOn" : ""),
4882
- onClick: () => {
4883
- if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
4923
+ onChange: (next) => {
4924
+ if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(next);
4884
4925
  }
4885
- })), report !== null && report.remote.phase === "login_required" && typeof report.remote.authURL === "string" && report.remote.authURL.startsWith("https://") ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("a", {
4926
+ }), (0, react.createElement)("button", {
4927
+ type: "button",
4928
+ className: "pbb-iconButton pbb-expand",
4929
+ "aria-label": translateWith(props.t, "remote.options"),
4930
+ "aria-expanded": remoteOptionsOpen,
4931
+ "aria-controls": remoteOptionsId,
4932
+ onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4933
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()))), report !== null && report.remote.phase === "login_required" && typeof report.remote.authURL === "string" && report.remote.authURL.startsWith("https://") ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("a", {
4886
4934
  className: "pbb-action pbb-buttonPrimary",
4887
4935
  href: report.remote.authURL,
4888
4936
  target: "_blank",
4889
4937
  rel: "noreferrer"
4890
- }, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.remote.message) : null, (0, react.createElement)("button", {
4891
- type: "button",
4892
- className: "pbb-expand",
4893
- "aria-expanded": remoteOptionsOpen,
4894
- onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4895
- }, translateWith(props.t, "connection.optionsSummary"), " ", (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(remoteOptionsOpen, (0, react.createElement)("div", { className: "pbb-channelBody" }, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4896
- className: "pbb-switchTitle",
4938
+ }, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", {
4939
+ className: "pbb-diag pbb-diagBad",
4940
+ role: "alert"
4941
+ }, translateWith(props.t, "remote.errorDetails", { message: report.remote.message })) : null, fold(remoteOptionsOpen, (0, react.createElement)("div", {
4942
+ className: "pbb-channelBody",
4943
+ id: remoteOptionsId
4944
+ }, report !== null && report.remote.phase === "online" ? (0, react.createElement)("p", { className: "pbb-diag pbb-endpoint" }, report.remote.publicURL) : null, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("label", {
4945
+ className: "pbb-controlTitle",
4897
4946
  htmlFor: "deeppilot-funnel-source-limit"
4898
- }, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "remote.limitDescription")), !remoteLimitView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "remote.limitInvalid")) : remoteLimitDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (remoteLimitDraft.failed ? " pbb-diagBad" : "") }, remoteLimitDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
4947
+ }, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-controlDescription" }, translateWith(props.t, "remote.limitDescription")), !remoteLimitView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "remote.limitInvalid")) : remoteLimitDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (remoteLimitDraft.failed ? " pbb-diagBad" : "") }, remoteLimitDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
4899
4948
  id: "deeppilot-funnel-source-limit",
4900
4949
  className: "pbb-numberInput",
4901
4950
  type: "number",
@@ -4921,62 +4970,125 @@ window.__ModuleLoader__.load({
4921
4970
  className: "pbb-action",
4922
4971
  disabled: !remoteConnectionLimitReady || !remoteLimitView.valid || remoteLimitView.unchanged,
4923
4972
  onClick: applyRemoteLimit
4924
- }, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.remoteTitle")), (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.recommended")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.step1")), (0, react.createElement)("li", null, translateWith(props.t, "help.step2")), (0, react.createElement)("li", null, translateWith(props.t, "help.step3")), (0, react.createElement)("li", null, translateWith(props.t, "help.step4"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.funnelHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.httpsTitle")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep1")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep2")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep3"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.httpsHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.allowTitle")), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowBody")), (0, react.createElement)("code", { className: "pbb-helpCode" }, "\"nodeAttrs\": [\n {\n \"target\": [\"autogroup:member\"],\n \"attr\": [\"funnel\"],\n },\n],"), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.faqTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.faq1")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq2")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq3")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq4")))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.securityTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.security1")), (0, react.createElement)("li", null, translateWith(props.t, "help.security2")), (0, react.createElement)("li", null, translateWith(props.t, "help.security3")), (0, react.createElement)("li", null, translateWith(props.t, "help.security4")), (0, react.createElement)("li", null, translateWith(props.t, "help.security5"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.docsPrefix"), (0, react.createElement)("a", {
4973
+ }, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("details", { className: "pbb-helpDisclosure" }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), translateWith(props.t, "help.remoteTitle")), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.recommended")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.step1")), (0, react.createElement)("li", null, translateWith(props.t, "help.step2")), (0, react.createElement)("li", null, translateWith(props.t, "help.step3")), (0, react.createElement)("li", null, translateWith(props.t, "help.step4"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.funnelHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.httpsTitle")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep1")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep2")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep3"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.httpsHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.allowTitle")), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowBody")), (0, react.createElement)("code", { className: "pbb-helpCode" }, "\"nodeAttrs\": [\n {\n \"target\": [\"autogroup:member\"],\n \"attr\": [\"funnel\"],\n },\n],"), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.faqTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.faq1")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq2")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq3")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq4")))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.securityTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.security1")), (0, react.createElement)("li", null, translateWith(props.t, "help.security2")), (0, react.createElement)("li", null, translateWith(props.t, "help.security3")), (0, react.createElement)("li", null, translateWith(props.t, "help.security4")), (0, react.createElement)("li", null, translateWith(props.t, "help.security5"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.docsPrefix"), (0, react.createElement)("a", {
4925
4974
  className: "pbb-helpLink",
4926
4975
  href: "https://tailscale.com/docs/features/tailscale-funnel",
4927
4976
  target: "_blank",
4928
4977
  rel: "noreferrer"
4929
- }, translateWith(props.t, "help.funnelDocs")))))))))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "devices.description"))), (0, react.createElement)("span", { className: "pbb-countBadge" }, String(visibleDevices$1.length))), (0, react.createElement)("div", { className: "pbb-cardBody" }, deviceMessage ? (0, react.createElement)("p", {
4930
- className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4978
+ }, translateWith(props.t, "help.funnelDocs"))))))))))), (0, react.createElement)("section", {
4979
+ className: "pbb-devicesSection",
4980
+ "aria-label": translateWith(props.t, "devices.title")
4981
+ }, (0, react.createElement)("div", { className: "pbb-devicesHeader" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("span", { className: "pbb-deviceCount" }, translateWith(props.t, "devices.count", { count: visibleDevices$1.length })), (0, react.createElement)("div", { className: "pbb-addAction" }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4982
+ variant: "outline",
4983
+ size: "sm",
4984
+ icon: settingsIcon("plus", 16),
4985
+ disabled: panel.busy || report === null || !report.pairingReady || !can("beginPairing"),
4986
+ "aria-expanded": pairingOpen,
4987
+ "aria-controls": pairingId,
4988
+ onClick: () => {
4989
+ if (pairingOpen) {
4990
+ hidePairingQR();
4991
+ setPairingOpen(false);
4992
+ return;
4993
+ }
4994
+ setPairingOpen(true);
4995
+ if (pairingTarget !== null && report?.pairingReady) showPairingQR(pairingTarget);
4996
+ }
4997
+ }, translateWith(props.t, pairingOpen ? "pair.close" : "pair.add")))), pairingSection, (0, react.createElement)("div", { className: "pbb-card pbb-devicesCard" }, deviceMessage ? (0, react.createElement)("p", {
4998
+ className: "pbb-diag pbb-deviceMessage" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4931
4999
  role: "status"
4932
- }, deviceMessage) : null, deviceRows)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
5000
+ }, deviceMessage) : null, deviceRows)), (0, react.createElement)("section", {
5001
+ className: "pbb-card pbb-tools",
5002
+ "aria-label": translateWith(props.t, "tools.title")
5003
+ }, (0, react.createElement)("div", { className: "pbb-toolSection" }, (0, react.createElement)("button", {
4933
5004
  type: "button",
4934
5005
  className: "pbb-cardHead",
4935
5006
  "aria-expanded": troubleshootingOpen,
5007
+ "aria-controls": troubleshootingId,
4936
5008
  onClick: () => setTroubleshootingOpen(!troubleshootingOpen)
4937
- }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "troubleshooting.summary")), (0, react.createElement)("span", { className: "pbb-cardSummary" }, translateWith(props.t, "troubleshooting.description")), (0, react.createElement)("span", { className: "pbb-badge" + (troubleGood ? " pbb-badgeOk" : troubleBad ? " pbb-badgeWarn" : "") }, troubleConclusion)), fold(troubleshootingOpen, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-testGrid" }, (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.relayTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.relayDefault"))), (0, react.createElement)("button", {
5009
+ }, settingsIcon("activity", 17), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "troubleshooting.summary")), (0, react.createElement)("span", { className: "pbb-toolMeta" + (troubleGood ? " pbb-ok" : troubleBad && relayTestResult !== null ? " pbb-bad" : "") }, troubleConclusion), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(troubleshootingOpen, (0, react.createElement)("div", {
5010
+ className: "pbb-cardBody",
5011
+ id: troubleshootingId
5012
+ }, (0, react.createElement)("div", { className: "pbb-testGrid" }, (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.relayTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.relayDefault"))), (0, react.createElement)("button", {
4938
5013
  type: "button",
4939
5014
  className: "pbb-action",
4940
5015
  disabled: relayTestBusy,
4941
5016
  onClick: runRelayTest
4942
- }, relayTestBusy ? translateWith(props.t, "push.relayTesting") : translateWith(props.t, "push.testRelay"))), (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.testPush")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.pushDefault"))), (0, react.createElement)("button", {
5017
+ }, relayTestBusy ? translateWith(props.t, "push.relayTesting") : translateWith(props.t, "push.testRelay"))), (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.testPush")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.pushDefault"))), (0, react.createElement)("button", {
4943
5018
  type: "button",
4944
5019
  className: "pbb-action",
4945
5020
  disabled: pushTestBusy,
4946
5021
  onClick: sendPushTest
4947
- }, pushTestBusy ? translateWith(props.t, "push.pushSending") : translateWith(props.t, "push.testPush"))), (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "troubleshooting.debugTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "troubleshooting.debugDescription"))), (0, react.createElement)("button", {
4948
- type: "button",
4949
- role: "switch",
4950
- "aria-checked": debugEnabled,
4951
- "aria-label": translateWith(props.t, "troubleshooting.debugTitle"),
5022
+ }, pushTestBusy ? translateWith(props.t, "push.pushSending") : translateWith(props.t, "push.testPush"))), (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "troubleshooting.debugTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "troubleshooting.debugDescription"))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
5023
+ checked: debugEnabled,
5024
+ label: translateWith(props.t, "troubleshooting.debugTitle"),
4952
5025
  disabled: !debugSwitchReady,
4953
- className: "pbb-switch" + (debugEnabled ? " pbb-switchOn" : ""),
4954
- onClick: () => {
4955
- if (can("setDebug")) props.setDeepPilotDebug(!debugEnabled);
5026
+ onChange: (next) => {
5027
+ if (can("setDebug")) props.setDeepPilotDebug(next);
4956
5028
  }
4957
5029
  }))), relayTestError ? (0, react.createElement)("p", {
4958
5030
  className: "pbb-diag pbb-diagBad",
4959
5031
  role: "alert"
4960
- }, relayTestError) : null, relayTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || translateWith(props.t, "push.relayUrlEmpty")), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad"))), relayTestResult.steps.map((step, index) => resultRow(step.ok, (step.id === "health" ? translateWith(props.t, "push.relayStep.health") : translateWith(props.t, "push.relayStep.enroll")) + (step.latencyMs !== void 0 ? ` (${String(step.latencyMs)}ms)` : "") + " — " + step.message, String(index)))), pushTestError ? (0, react.createElement)("p", {
5032
+ }, relayTestError) : null, relayTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || translateWith(props.t, "push.relayUrlEmpty")), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad"))), relayTestResult.steps.map((step, index) => resultRow(step.ok, (step.id === "health" ? translateWith(props.t, "push.relayStep.health") : translateWith(props.t, "push.relayStep.enroll")) + (step.latencyMs !== void 0 ? ` (${String(step.latencyMs)}ms)` : "") + "(" + step.message + ")", String(index)))), pushTestError ? (0, react.createElement)("p", {
4961
5033
  className: "pbb-diag pbb-diagBad",
4962
5034
  role: "alert"
4963
- }, pushTestError) : null, pushTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token " + (pushTestResult.overall === "sent" ? "pbb-ok" : pushTestResult.overall === "failed" ? "pbb-bad" : "") }, pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : pushTestResult.overall === "failed" ? translateWith(props.t, "push.pushFailed") : pushTestResult.overall === "no-targets" ? translateWith(props.t, "push.pushNoTargets") : translateWith(props.t, "push.pushNotEnabled"))), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, pushTestResult.message) : null, pushTestResult.results.map((r, index) => resultRow(r.outcome === "sent", r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : ""), String(index)))), diagnosticsSection))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
5035
+ }, pushTestError) : null, pushTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token " + (pushTestResult.overall === "sent" ? "pbb-ok" : pushTestResult.overall === "failed" ? "pbb-bad" : "") }, pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : pushTestResult.overall === "failed" ? translateWith(props.t, "push.pushFailed") : pushTestResult.overall === "no-targets" ? translateWith(props.t, "push.pushNoTargets") : translateWith(props.t, "push.pushNotEnabled"))), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "push.resultDetail", { message: pushTestResult.message })) : null, pushTestResult.results.map((r, index) => resultRow(r.outcome === "sent", r.name + "(" + translateWith(props.t, r.environment === "production" ? "push.environment.production" : r.environment === "development" ? "push.environment.development" : "push.environment.other", { environment: r.environment }) + ") · " + translateWith(props.t, r.outcome === "sent" ? "push.outcome.sent" : r.outcome === "invalid-token" ? "push.outcome.invalid" : r.outcome === "failed" ? "push.outcome.failed" : "push.outcome.other", { outcome: r.outcome }) + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : ""), String(index)))), diagnosticsSection))), report === null ? null : (0, react.createElement)("div", { className: "pbb-toolSection" }, (0, react.createElement)("button", {
4964
5036
  type: "button",
4965
5037
  className: "pbb-cardHead",
4966
5038
  "aria-expanded": systemOpen,
5039
+ "aria-controls": systemId,
4967
5040
  onClick: () => setSystemOpen(!systemOpen)
4968
- }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-cardSummary" }, translateWith(props.t, "system.description")), (0, react.createElement)("span", { className: "pbb-badge" + (report.updateAvailable === true ? " pbb-badgeOk" : "") }, "v" + report.pluginVersion + (report.updateAvailable === true ? " · " + translateWith(props.t, "update.badge") : ""))), fold(systemOpen, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.tailscaleVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.remote.tailscaleVersion ?? translateWith(props.t, "advanced.tailscaleVersionUnknown"))), advancedRows, report.updateAvailable === true ? (0, react.createElement)("a", {
5041
+ }, settingsIcon("info", 17), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-toolMeta" + (report.updateAvailable === true ? " pbb-ok" : "") }, "v" + report.pluginVersion + (report.updateAvailable === true ? " · " + translateWith(props.t, "update.badge") : "")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(systemOpen, (0, react.createElement)("div", {
5042
+ className: "pbb-cardBody",
5043
+ id: systemId
5044
+ }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.tailscaleVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.remote.tailscaleVersion ?? translateWith(props.t, "advanced.tailscaleVersionUnknown"))), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "panel.identity")), (0, react.createElement)("span", { className: "pbb-value" }, translateWith(props.t, report.pairingReady ? "panel.identityReady" : "panel.identityNotReady"))), advancedRows, report.updateAvailable === true ? (0, react.createElement)("a", {
4969
5045
  className: "pbb-updateRow",
4970
5046
  href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
4971
5047
  target: "_blank",
4972
5048
  rel: "noreferrer"
4973
- }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())) : null)))), diag.length > 0 ? (0, react.createElement)("details", {
5049
+ }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())) : null))))), diag.length > 0 ? (0, react.createElement)("details", {
4974
5050
  className: "pbb-card pbb-disclosure pbb-diagnostics",
4975
5051
  open: failed
4976
5052
  }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-disclosureTitle" }, translateWith(props.t, "diag.summary"))), (0, react.createElement)("div", { className: "pbb-disclosureBody" }, (0, react.createElement)("p", { className: "pbb-diag" + (failed ? " pbb-diagBad" : "") }, translateWith(props.t, "diag.prefix") + diag.join(" | ")))) : null);
4977
5053
  }
4978
- //#endregion
4979
- //#region src/client/config-form.ts
5054
+ // src/client/nav-icon.ts
5055
+ /**
5056
+ * 设置页左侧导航里 DeepPilot 一行的图标。
5057
+ *
5058
+ * DSH 0.2.2 的设置壳为每个 `settings.section` 渲染
5059
+ * `renderSlot('settings.section.icon', { size, active }, { only: id, fallback })`:
5060
+ * 有同 id 的图标贡献就用它,否则画默认齿轮。壳负责按钮、文字和装饰性外框
5061
+ * (外框已 `aria-hidden`),图标只需按 `size` 画一个随文字颜色变化的线性 SVG。
5062
+ *
5063
+ * 不用 `icon.svg`:那是插件管理页的彩色品牌图(渐变填充、白底),放进导航行
5064
+ * 会和内置单色线性图标格格不入,也违反「图标统一用线性单色 SVG」的要求。
5065
+ * 这里画一部手机(DeepPilot 的职责是把手机接到这台电脑的 DSH),描边沿用
5066
+ * 设置页其他图标的 16×16 视图框和 1.5 线宽。
5067
+ */
5068
+ /** 导航行默认边长;壳当前固定传 16。 */
5069
+ const DEFAULT_NAV_ICON_SIZE = 16;
5070
+ const DeepPilotNavIcon = ({ size }) => {
5071
+ const edge = typeof size === "number" && size > 0 ? size : DEFAULT_NAV_ICON_SIZE;
5072
+ return (0, react.createElement)("svg", {
5073
+ viewBox: "0 0 16 16",
5074
+ width: edge,
5075
+ height: edge,
5076
+ fill: "none",
5077
+ stroke: "currentColor",
5078
+ strokeWidth: 1.5,
5079
+ strokeLinecap: "round",
5080
+ strokeLinejoin: "round",
5081
+ "aria-hidden": true,
5082
+ focusable: "false"
5083
+ }, (0, react.createElement)("rect", {
5084
+ x: 4.25,
5085
+ y: 1.75,
5086
+ width: 7.5,
5087
+ height: 12.5,
5088
+ rx: 1.75
5089
+ }), (0, react.createElement)("path", { d: "M7 11.75h2" }));
5090
+ };
5091
+ // src/client/config-form.ts
4980
5092
  /**
4981
5093
  * Deep-unwrap live-update references in a snapshot section.
4982
5094
  *
@@ -5029,8 +5141,7 @@ window.__ModuleLoader__.load({
5029
5141
  function bindConfigForm(ctx) {
5030
5142
  return adaptConfigForm(ctx.get("configForms"), "deeppilot");
5031
5143
  }
5032
- //#endregion
5033
- //#region src/client/index.ts
5144
+ // src/client/index.ts
5034
5145
  /** Polls the report remote and owns the page state transitions. */
5035
5146
  var ReportController = class {
5036
5147
  fetchReport;
@@ -5482,8 +5593,11 @@ window.__ModuleLoader__.load({
5482
5593
  t: (key, vars) => t(ctx, key, vars)
5483
5594
  })
5484
5595
  }, DeepPilotSettingsPage));
5596
+ anyCtx.slots?.inject("settings.section.icon", () => anyCtx.slots.register({
5597
+ name: "settings.section.icon",
5598
+ id: "deeppilot"
5599
+ }, DeepPilotNavIcon));
5485
5600
  }
5486
- //#endregion
5487
5601
  exports.DeepPilotSettingsPage = DeepPilotSettingsPage;
5488
5602
  exports.apply = apply;
5489
5603
  exports.inject = inject;