dsh-deeppilot 0.9.14 → 0.9.16

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
@@ -27,6 +27,7 @@ window.__ModuleLoader__.load({
27
27
  enumerable: true
28
28
  }) : target, mod));
29
29
  let react = require("react");
30
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
30
31
  // src/client/snapshot-store.ts
31
32
  function createSnapshotStore(initial) {
32
33
  let snapshot = initial;
@@ -492,11 +493,39 @@ window.__ModuleLoader__.load({
492
493
  */
493
494
  const TABLES = {
494
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})",
495
524
  "nav": "DeepPilot",
496
525
  "meta.title": "DeepPilot",
497
- "meta.intro": "把 iPhone 与这台电脑上的 DeepSeek Harness(DSH)连接起来(协议 v2)。",
526
+ "meta.intro": "在手机上使用这台电脑的 DSH。",
498
527
  "meta.refresh": "刷新",
499
- "master.title": "DeepPilot 连接",
528
+ "master.title": "允许手机连接",
500
529
  "master.loading": "正在读取配置…",
501
530
  "master.on": "已开启:接受手机连接。",
502
531
  "master.off": "已关闭:不接受手机连接。",
@@ -509,11 +538,11 @@ window.__ModuleLoader__.load({
509
538
  "connection.optionsSummary": "连接选项",
510
539
  "pair.description": "选择连接方式并生成一次性配对信息。",
511
540
  "devices.description": "管理已经配对的 iPhone。",
512
- "troubleshooting.summary": "测试与故障排查",
541
+ "troubleshooting.summary": "诊断工具",
513
542
  "troubleshooting.title": "诊断工具",
514
543
  "troubleshooting.description": "仅在排查连接或推送问题时使用。",
515
544
  "troubleshooting.debugTitle": "调试日志",
516
- "troubleshooting.debugDescription": "打开后在 DSH host 的终端输出里打印握手、推送等诊断信息;不会打印配对令牌、APNs token 或消息内容。改动立即生效,无需重启。",
545
+ "troubleshooting.debugDescription": "开启后,新的手机连接会在 DSH 终端输出诊断日志;已连接手机需重连后生效。日志不包含配对码、推送凭据或消息内容。",
517
546
  "diagnostics.title": "连接诊断",
518
547
  "diagnostics.description": "最近的连接失败事件;可一键复制后发给维护者排查。",
519
548
  "diagnostics.recent": "最近失败",
@@ -543,6 +572,7 @@ window.__ModuleLoader__.load({
543
572
  "system.title": "运行信息",
544
573
  "system.description": "插件、协议与连接端点的技术信息。",
545
574
  "local.title": "局域网连接",
575
+ "local.autoRoute": "局域网连接始终开启:手机和电脑在同一 Wi-Fi 下会自动走局域网,在外面自动改走公网。",
546
576
  "local.on": "已开启:手机可通过独立端口 {port} 以 TLS 加密连接。",
547
577
  "local.off": "已关闭:不接受局域网连接。",
548
578
  "local.tlsFingerprint": "证书指纹",
@@ -557,7 +587,7 @@ window.__ModuleLoader__.load({
557
587
  "local.portApplied": "局域网端口已保存,监听器正在重新加载。",
558
588
  "local.portInvalid": "请输入 1024–65535 之间的整数。",
559
589
  "local.portFailed": "局域网端口保存失败:",
560
- "remote.title": "远程连接(Tailscale Funnel)",
590
+ "remote.title": "远程连接",
561
591
  "remote.on": "已配置:内嵌 Funnel 会自动启动并同步状态。",
562
592
  "remote.off": "关闭:仅保留局域网连接。",
563
593
  "remote.openAuth": "打开授权页面",
@@ -605,12 +635,12 @@ window.__ModuleLoader__.load({
605
635
  "panel.tokenRotateWarning": "更换会使当前 Token 立即失效、断开所有手机连接;5 秒内再次点击确认。",
606
636
  "panel.tokenRotated": "新 Token 已生效,旧 Token 已失效;请重新配对所有设备。",
607
637
  "panel.tokenRotateFailed": "更换失败:",
608
- "pair.qrPanelTitle": "扫码添加当前手机",
638
+ "pair.qrPanelTitle": "添加手机",
609
639
  "pair.kind.public": "公网",
610
640
  "pair.kind.lan": "内网",
611
641
  "pair.noAddress": "没有可用地址",
612
642
  "pair.noAddressHelp": "没有可用的局域网或远程入口。请确认局域网连接已启动,或启用远程连接。",
613
- "pair.disabledHelp": "DeepPilot 连接已关闭。先在上方开启连接,再添加手机。",
643
+ "pair.disabledHelp": "请先开启“允许手机连接”,再添加手机。",
614
644
  "pair.qrAlt": "DeepPilot 配对二维码",
615
645
  "pair.qrShow": "显示二维码",
616
646
  "pair.qrHide": "隐藏二维码",
@@ -621,7 +651,7 @@ window.__ModuleLoader__.load({
621
651
  "pair.infoLabel": "配对信息(复制一次,粘贴到 App 即可)",
622
652
  "pair.infoCopyDone": "配对信息已复制",
623
653
  "pair.infoCopyFailed": "配对信息复制失败:",
624
- "pair.qrHint": "二维码与下面的「配对信息」内容相同:包含{kind}地址、一次性配对码、证书指纹(内网时)与主机标识。在局域网与公网之间切换会立即生成对应的新配对信息,配对码 5 分钟后失效。",
654
+ "pair.qrHint": "二维码与「配对信息」内容相同:包含{kind}地址、一次性配对码、证书指纹(内网时)与主机标识。在局域网与公网之间切换会立即生成对应的新配对信息,配对码 5 分钟后失效。",
625
655
  "advanced.summary": "高级信息",
626
656
  "advanced.protocolVersion": "协议版本",
627
657
  "advanced.serverVersion": "服务器版本",
@@ -647,9 +677,9 @@ window.__ModuleLoader__.load({
647
677
  "push.pushNotEnabled": "推送未启用",
648
678
  "push.relayStep.health": "服务可达",
649
679
  "push.relayStep.enroll": "自动注册",
650
- "push.prefix.ok": "✓ ",
651
- "push.prefix.fail": "✗ ",
652
- "devices.title": "设备",
680
+ "push.prefix.ok": "通过:",
681
+ "push.prefix.fail": "未通过:",
682
+ "devices.title": "已配对设备",
653
683
  "devices.col.name": "设备",
654
684
  "devices.col.appVersion": "App 版本",
655
685
  "devices.col.push": "离线推送",
@@ -680,13 +710,13 @@ window.__ModuleLoader__.load({
680
710
  "devices.pushNotRegistered": "未注册",
681
711
  "devices.pushEnvProduction": "生产",
682
712
  "devices.pushEnvDevelopment": "开发",
683
- "devices.empty": "还没有 v2 设备。请在 iPhone 上扫描一次性配对二维码。",
713
+ "devices.empty": "还没有配对设备。点击“添加手机”,然后在 DeepPilot 中扫码连接。",
684
714
  "help.remoteTitle": "远程连接帮助",
685
715
  "help.recommended": "推荐设置流程",
686
- "help.step1": "先打开\"DeepPilot 连接\",再打开\"远程连接(Tailscale Funnel)\"。",
716
+ "help.step1": "先打开“允许手机连接”,再打开“远程连接”。",
687
717
  "help.step2": "状态变为\"等待 Tailscale 授权\"后,点击\"打开授权页面\"。",
688
718
  "help.step3": "使用有权管理此 Tailnet 的账号登录,并按授权页提示启用 Funnel。通常需要 Owner、Admin 或 Network admin 权限。",
689
- "help.step4": "返回此页面并点击\"刷新\"。远程连接标题旁出现绿点后,即可扫描下方二维码添加手机。",
719
+ "help.step4": "返回此页面并点击“刷新”。远程连接显示“已就绪”后,点击“添加手机”并扫码。",
690
720
  "help.funnelHint": "插件已内嵌 Tailscale 网络组件,这台电脑和手机都不需要另外安装 Tailscale App;但首次启用仍需由 Tailnet 管理员授权。",
691
721
  "help.httpsTitle": "授权页未自动完成时:开启 HTTPS",
692
722
  "help.httpsStep1": "打开 Tailscale 管理后台的 Network → DNS。",
@@ -718,7 +748,6 @@ window.__ModuleLoader__.load({
718
748
  "diag.settingsUnavailable": "设置表单不可用(configForms 未提供)",
719
749
  "diag.missingReportHook": "useDeepPilotReport hook 缺失",
720
750
  "diag.missingEnabledHook": "useDeepPilotEnabled hook 缺失",
721
- "diag.missingLocalEnabledHook": "useDeepPilotLocalEnabled hook 缺失",
722
751
  "diag.missingLocalPortHook": "useDeepPilotLocalPort hook 缺失",
723
752
  "diag.missingRemoteEnabledHook": "useDeepPilotRemoteEnabled hook 缺失",
724
753
  "diag.missingRemoteLimitHook": "useDeepPilotRemoteConnectionLimit hook 缺失",
@@ -730,7 +759,6 @@ window.__ModuleLoader__.load({
730
759
  "diag.missingTestRelay": "testRelay 回调缺失",
731
760
  "diag.missingTestPush": "testPush 回调缺失",
732
761
  "diag.missingSetEnabled": "setDeepPilotEnabled 回调缺失",
733
- "diag.missingSetLocal": "setDeepPilotLocalEnabled 回调缺失",
734
762
  "diag.missingSetLocalPort": "setDeepPilotLocalPort 回调缺失",
735
763
  "diag.missingSetRemote": "setDeepPilotRemoteEnabled 回调缺失",
736
764
  "diag.missingSetRemoteLimit": "setDeepPilotRemoteConnectionLimit 回调缺失",
@@ -745,11 +773,39 @@ window.__ModuleLoader__.load({
745
773
  "update.badge": "有新版本"
746
774
  },
747
775
  en: {
776
+ "local.options": "LAN connection options",
777
+ "remote.options": "Remote connection options",
778
+ "local.errorDetails": "LAN connection failed. Check the connection options ({message}).",
779
+ "remote.errorDetails": "Remote connection failed. Check authorization and connection options ({message}).",
780
+ "pair.add": "Add phone",
781
+ "pair.close": "Collapse",
782
+ "devices.count": "{count} devices",
783
+ "devices.moreAria": "More actions for “{name}”",
784
+ "tools.title": "Tools and information",
785
+ "diag.reportFailed": "Failed to read connection status",
786
+ "diagnostics.historyCount": "{count} past failures",
787
+ "diagnostics.kind.upgrade": "Connection request rejected",
788
+ "diagnostics.kind.pair": "Pairing request rejected",
789
+ "diagnostics.kind.auth": "Device authentication failed",
790
+ "diagnostics.kind.opened": "Connection opened",
791
+ "diagnostics.kind.closed": "Connection closed unexpectedly",
792
+ "diagnostics.kind.error": "Connection error",
793
+ "diagnostics.kind.identity": "Device identity error",
794
+ "diagnostics.kind.storage": "Saving device information failed",
795
+ "diagnostics.kind.unknown": "Connection problem",
796
+ "push.outcome.sent": "Sent",
797
+ "push.outcome.failed": "Failed to send",
798
+ "push.outcome.invalid": "Invalid push credentials",
799
+ "push.environment.development": "Development",
800
+ "push.environment.production": "Production",
801
+ "push.environment.other": "Other environment ({environment})",
802
+ "push.outcome.other": "Push status ({outcome})",
803
+ "push.resultDetail": "Push result ({message})",
748
804
  "nav": "DeepPilot",
749
805
  "meta.title": "DeepPilot",
750
- "meta.intro": "Connect your iPhone to DeepSeek Harness (DSH) on this computer (protocol v2).",
806
+ "meta.intro": "Use this computer’s DSH from your phone.",
751
807
  "meta.refresh": "Refresh",
752
- "master.title": "DeepPilot connection",
808
+ "master.title": "Allow phone connections",
753
809
  "master.loading": "Loading settings…",
754
810
  "master.on": "On: accepting phone connections.",
755
811
  "master.off": "Off: not accepting phone connections.",
@@ -762,11 +818,11 @@ window.__ModuleLoader__.load({
762
818
  "connection.optionsSummary": "Connection options",
763
819
  "pair.description": "Choose a connection type and generate one-time pairing information.",
764
820
  "devices.description": "Manage iPhones that are already paired.",
765
- "troubleshooting.summary": "Tests and troubleshooting",
821
+ "troubleshooting.summary": "Diagnostic tools",
766
822
  "troubleshooting.title": "Diagnostic tools",
767
823
  "troubleshooting.description": "Use these only when diagnosing connection or push issues.",
768
824
  "troubleshooting.debugTitle": "Debug logging",
769
- "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.",
825
+ "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.",
770
826
  "diagnostics.title": "Connection diagnostics",
771
827
  "diagnostics.description": "Recent connection failures; copy them for troubleshooting.",
772
828
  "diagnostics.recent": "Recent failures",
@@ -796,6 +852,7 @@ window.__ModuleLoader__.load({
796
852
  "system.title": "Runtime information",
797
853
  "system.description": "Technical details about the plugin, protocol, and endpoints.",
798
854
  "local.title": "LAN connection",
855
+ "local.autoRoute": "LAN connections are always on: on the same Wi-Fi the phone connects over the LAN automatically, and switches to the public route when away.",
799
856
  "local.on": "On: phones connect over TLS through the independent port {port}.",
800
857
  "local.off": "Off: LAN connections are not accepted.",
801
858
  "local.tlsFingerprint": "Certificate fingerprint",
@@ -810,7 +867,7 @@ window.__ModuleLoader__.load({
810
867
  "local.portApplied": "LAN port saved; the listener is reloading.",
811
868
  "local.portInvalid": "Enter an integer from 1024 to 65535.",
812
869
  "local.portFailed": "Failed to save LAN port: ",
813
- "remote.title": "Remote connection (Tailscale Funnel)",
870
+ "remote.title": "Remote connection",
814
871
  "remote.on": "Configured: the embedded Funnel will start and sync state automatically.",
815
872
  "remote.off": "Off: LAN connections only.",
816
873
  "remote.openAuth": "Open authorization page",
@@ -858,12 +915,12 @@ window.__ModuleLoader__.load({
858
915
  "panel.tokenRotateWarning": "Rotation invalidates the current token immediately and drops every paired phone. Click again within 5s to confirm.",
859
916
  "panel.tokenRotated": "New token active, old token invalidated. Please re-pair every device.",
860
917
  "panel.tokenRotateFailed": "Rotation failed: ",
861
- "pair.qrPanelTitle": "Scan to pair this phone",
918
+ "pair.qrPanelTitle": "Add phone",
862
919
  "pair.kind.public": "Public",
863
920
  "pair.kind.lan": "LAN",
864
921
  "pair.noAddress": "No address available",
865
922
  "pair.noAddressHelp": "No LAN or remote endpoint is available. Make sure LAN is running or enable remote access.",
866
- "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.",
867
924
  "pair.qrAlt": "DeepPilot pairing QR code",
868
925
  "pair.qrShow": "Show QR code",
869
926
  "pair.qrHide": "Hide QR code",
@@ -874,7 +931,7 @@ window.__ModuleLoader__.load({
874
931
  "pair.infoLabel": "Pairing info (copy once, paste it in the app)",
875
932
  "pair.infoCopyDone": "Pairing info copied",
876
933
  "pair.infoCopyFailed": "Failed to copy pairing info: ",
877
- "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.",
878
935
  "advanced.summary": "Advanced info",
879
936
  "advanced.protocolVersion": "Protocol version",
880
937
  "advanced.serverVersion": "Server version",
@@ -900,9 +957,9 @@ window.__ModuleLoader__.load({
900
957
  "push.pushNotEnabled": "Push not enabled",
901
958
  "push.relayStep.health": "Service reachable",
902
959
  "push.relayStep.enroll": "Auto enrollment",
903
- "push.prefix.ok": "✓ ",
904
- "push.prefix.fail": "✗ ",
905
- "devices.title": "Devices",
960
+ "push.prefix.ok": "Passed: ",
961
+ "push.prefix.fail": "Failed: ",
962
+ "devices.title": "Paired devices",
906
963
  "devices.col.name": "Device",
907
964
  "devices.col.appVersion": "App version",
908
965
  "devices.col.push": "Push",
@@ -933,13 +990,13 @@ window.__ModuleLoader__.load({
933
990
  "devices.pushNotRegistered": "Not registered",
934
991
  "devices.pushEnvProduction": "Production",
935
992
  "devices.pushEnvDevelopment": "Development",
936
- "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.",
937
994
  "help.remoteTitle": "Remote connection help",
938
995
  "help.recommended": "Recommended setup",
939
- "help.step1": "Turn on \"DeepPilot connection\" first, then \"Remote connection (Tailscale Funnel)\".",
996
+ "help.step1": "Turn on “Allow phone connections”, then “Remote connection”.",
940
997
  "help.step2": "When the status shows \"Awaiting Tailscale authorization\", click \"Open authorization page\".",
941
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.",
942
- "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.",
943
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.",
944
1001
  "help.httpsTitle": "If authorization does not complete: enable HTTPS",
945
1002
  "help.httpsStep1": "Open the Tailscale admin console → Network → DNS.",
@@ -971,7 +1028,6 @@ window.__ModuleLoader__.load({
971
1028
  "diag.settingsUnavailable": "Settings form unavailable (configForms not provided)",
972
1029
  "diag.missingReportHook": "useDeepPilotReport hook missing",
973
1030
  "diag.missingEnabledHook": "useDeepPilotEnabled hook missing",
974
- "diag.missingLocalEnabledHook": "useDeepPilotLocalEnabled hook missing",
975
1031
  "diag.missingLocalPortHook": "useDeepPilotLocalPort hook missing",
976
1032
  "diag.missingRemoteEnabledHook": "useDeepPilotRemoteEnabled hook missing",
977
1033
  "diag.missingRemoteLimitHook": "useDeepPilotRemoteConnectionLimit hook missing",
@@ -983,7 +1039,6 @@ window.__ModuleLoader__.load({
983
1039
  "diag.missingTestRelay": "testRelay callback missing",
984
1040
  "diag.missingTestPush": "testPush callback missing",
985
1041
  "diag.missingSetEnabled": "setDeepPilotEnabled callback missing",
986
- "diag.missingSetLocal": "setDeepPilotLocalEnabled callback missing",
987
1042
  "diag.missingSetLocalPort": "setDeepPilotLocalPort callback missing",
988
1043
  "diag.missingSetRemote": "setDeepPilotRemoteEnabled callback missing",
989
1044
  "diag.missingSetRemoteLimit": "setDeepPilotRemoteConnectionLimit callback missing",
@@ -1061,170 +1116,149 @@ window.__ModuleLoader__.load({
1061
1116
  });
1062
1117
  }
1063
1118
  // src/client/styles.ts
1064
- const EASE = "cubic-bezier(0.22,1,0.36,1)";
1065
- const CSS = [
1066
- ".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}",
1067
- ".pbb-section>*{animation:pbb-cardIn 160ms " + EASE + " backwards}",
1068
- ".pbb-section>*:nth-child(2){animation-delay:24ms}",
1069
- ".pbb-section>*:nth-child(3){animation-delay:48ms}",
1070
- ".pbb-section>*:nth-child(4){animation-delay:72ms}",
1071
- ".pbb-section>*:nth-child(5){animation-delay:96ms}",
1072
- ".pbb-section>*:nth-child(6){animation-delay:120ms}",
1073
- ".pbb-section>*:nth-child(7){animation-delay:144ms}",
1074
- ".pbb-section>*:nth-child(n+8){animation-delay:144ms}",
1075
- "@keyframes pbb-cardIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}",
1076
- ".pbb-pageHeader{display:flex;align-items:flex-start;gap:16px;padding:2px 2px 4px}",
1077
- ".pbb-title{margin:0;font-size:18px;font-weight:600;letter-spacing:-.01em}",
1078
- ".pbb-intro{color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1.5;margin:4px 0 0;max-width:600px}",
1079
- ".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 + "}",
1080
- ".pbb-refresh:active{transform:scale(0.97)}",
1081
- ".pbb-refresh:disabled{cursor:default;opacity:.5}",
1082
- ".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}",
1083
- ".pbb-statusCell{padding:14px 16px;min-width:0}",
1084
- ".pbb-statusCell+.pbb-statusCell{border-left:1px solid var(--dsw-alias-border-l2)}",
1085
- ".pbb-statusLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
1086
- ".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}",
1087
- ".pbb-statusValue .pbb-dot{margin-right:6px}",
1088
- ".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}",
1089
- ".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}",
1090
- ".pbb-cardHeader{display:flex;align-items:flex-start;gap:16px;padding:15px 18px 13px}",
1091
- ".pbb-cardHeader>div:first-child{min-width:0;flex:1}",
1092
- ".pbb-cardTitle{margin:0;font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1093
- ".pbb-cardDescription{margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
1094
- ".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 + "}",
1095
- ".pbb-cardHead:active{background:var(--dsw-alias-bg-module-platform)}",
1096
- ".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}",
1097
- ".pbb-chev{flex:none;display:inline-flex;align-items:center;color:var(--dsw-alias-label-tertiary);transition:transform 160ms " + EASE + "}",
1098
- ".pbb-chevIcon{display:block}",
1099
- ".pbb-cardHead[aria-expanded=\"true\"] .pbb-chev,.pbb-expand[aria-expanded=\"true\"] .pbb-chev{transform:rotate(90deg)}",
1100
- ".pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 18px 16px}",
1101
- ".pbb-pairingBody{display:flex;flex-direction:column;gap:10px;padding:0 18px 15px}",
1102
- ".pbb-sectionIntro{padding-bottom:2px}",
1103
- ".pbb-fold{display:grid;grid-template-rows:1fr;transition:grid-template-rows 240ms " + EASE + ",opacity 200ms " + EASE + ";opacity:1}",
1104
- ".pbb-foldClosed{grid-template-rows:0fr;opacity:0}",
1105
- ".pbb-fold>div{overflow:hidden;min-height:0}",
1106
- ".pbb-row{display:flex;align-items:center;gap:8px}",
1107
- ".pbb-label{flex:1;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary);min-width:0}",
1108
- ".pbb-value{font-size:12px;color:var(--dsw-alias-label-secondary);word-break:break-all;text-align:right}",
1109
- ".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}",
1110
- ".pbb-badgeOk{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22a06b) 12%,transparent);color:var(--dsw-alias-state-success-primary,#22a06b)}",
1111
- ".pbb-badgeWarn{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 14%,transparent);color:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1112
- ".pbb-countBadge{min-width:24px;text-align:center}",
1113
- ".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}",
1114
- ".pbb-ok{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));font-weight:500}",
1115
- ".pbb-bad{color:var(--dsw-alias-state-error-primary);font-weight:500}",
1116
- ".pbb-diag{font-size:12px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5;overflow-wrap:anywhere}",
1117
- ".pbb-diagBad{color:var(--dsw-alias-state-error-primary)}",
1118
- ".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}",
1119
- ".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}",
1120
- ".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 + "}",
1121
- ".pbb-action:active{transform:scale(0.97)}",
1122
- ".pbb-action:disabled{cursor:default;opacity:.5}",
1123
- ".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))}",
1124
- ".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}",
1125
- ".pbb-actionDangerGhost:active{transform:scale(0.97)}",
1126
- ".pbb-actionDangerGhost:disabled{cursor:default;opacity:.62}",
1127
- ".pbb-actionIcon{flex:none;display:inline-block;vertical-align:-2px;margin-right:5px;pointer-events:none}",
1128
- ".pbb-actionBusy{display:inline-flex;align-items:center;gap:6px}",
1129
- ".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}",
1130
- "@keyframes pbb-spin{to{transform:rotate(360deg)}}",
1131
- ".pbb-masterRow{display:flex;align-items:center;gap:14px}",
1132
- ".pbb-switchText{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}",
1133
- ".pbb-switchTitle{font-size:14px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1134
- ".pbb-switchDesc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.45}",
1135
- ".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 + "}",
1136
- ".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 + "}",
1137
- ".pbb-switchOn{background:var(--dsw-alias-brand-primary)}",
1138
- ".pbb-switchOn::after{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}",
1139
- ".pbb-switch:active{transform:scale(0.97)}",
1140
- ".pbb-switch:disabled{opacity:.5;cursor:default}",
1141
- ".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}",
1142
- ".pbb-dot{display:inline-block;vertical-align:1px;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
1143
- ".pbb-dotOk{background:var(--dsw-alias-state-success-primary,#22a06b)}",
1144
- ".pbb-dotWarn{background:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1145
- ".pbb-dotBad{background:var(--dsw-alias-state-error-primary)}",
1146
- ".pbb-switchTitle .pbb-dot{margin-right:6px;vertical-align:1px}",
1147
- ".pbb-channel{border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px;display:flex;flex-direction:column;gap:10px}",
1148
- ".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 + "}",
1149
- ".pbb-expand:active{transform:scale(0.97)}",
1150
- ".pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 0 0 14px}",
1151
- ".pbb-limitRow{display:flex;align-items:center;gap:12px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1152
- ".pbb-limitRow:first-child{border-top:0;padding-top:2px}",
1153
- ".pbb-limitControl{display:flex;align-items:center;gap:8px;flex:none}",
1154
- ".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}",
1155
- ".pbb-numberInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1156
- ".pbb-numberInput:disabled{opacity:.5}",
1157
- ".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}",
1158
- ".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 + "}",
1159
- ".pbb-targetButton:active{transform:scale(0.97)}",
1160
- ".pbb-targetButton:disabled{cursor:default;opacity:.5}",
1161
- ".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}",
1162
- ".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)}",
1163
- ".pbb-qrImage{width:224px;height:224px;max-width:100%;background:#fff;border-radius:12px;padding:8px;box-sizing:border-box}",
1164
- ".pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}",
1165
- ".pbb-countdownBar{height:4px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);overflow:hidden}",
1166
- ".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}",
1167
- ".pbb-countdownText{font-size:12px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}",
1168
- ".pbb-pairInfo{display:flex;flex-direction:column;gap:10px;min-width:0}",
1169
- ".pbb-pairCodeBlock{width:100%;display:flex;flex-direction:column;gap:6px}",
1170
- ".pbb-pairCodeLabel{font-size:12px;color:var(--dsw-alias-label-tertiary)}",
1171
- ".pbb-pairCodeRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
1172
- ".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}",
1173
- ".pbb-qrHint{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
1174
- ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
1175
- ".pbb-resultRow{display:flex;align-items:flex-start;gap:6px}",
1176
- ".pbb-resultIcon{flex:none;display:inline-flex;margin-top:2px;color:var(--dsw-alias-state-success-primary)}",
1177
- ".pbb-resultIconBad{color:var(--dsw-alias-state-error-primary)}",
1178
- ".pbb-deviceList{display:flex;flex-direction:column}",
1179
- ".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)}",
1180
- ".pbb-deviceRow:first-child{border-top:0;padding-top:2px}",
1181
- ".pbb-deviceRow:last-child{padding-bottom:0}",
1182
- ".pbb-deviceRow>.pbb-deviceRenameForm,.pbb-deviceRow>.pbb-deviceConfirm{grid-column:1/-1}",
1183
- ".pbb-deviceMain{min-width:0;display:flex;flex-direction:column;gap:2px}",
1184
- ".pbb-deviceNameRow{display:flex;align-items:center;gap:6px;min-width:0}",
1185
- ".pbb-deviceName{display:block;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1186
- ".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}",
1187
- ".pbb-deviceMeta>*+*::before{content:\"\\00B7\";margin-right:6px}",
1188
- ".pbb-deviceMeta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1189
- ".pbb-deviceActions{grid-row:1;grid-column:2;display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}",
1190
- ".pbb-deviceConfirm{display:flex;flex-direction:column;gap:8px}",
1191
- ".pbb-deviceRenameForm{display:flex;gap:8px;flex-wrap:wrap;min-width:0}",
1192
- ".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}",
1193
- ".pbb-deviceNameInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1194
- ".pbb-deviceNameInput:disabled{opacity:.5}",
1195
- ".pbb-deviceRenameActions{display:flex;align-items:center;gap:5px;flex:none}",
1196
- ".pbb-helpBody{display:flex;flex-direction:column;gap:14px}",
1197
- ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
1198
- ".pbb-helpHeading{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1199
- ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1200
- ".pbb-helpList li+li{margin-top:4px}",
1201
- ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1202
- ".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}",
1203
- ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
1204
- ".pbb-testGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}",
1205
- ".pbb-testItem:last-child:nth-child(odd){grid-column:1/-1}",
1206
- ".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)}",
1207
- ".pbb-testTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1208
- ".pbb-testDescription{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.5}",
1209
- ".pbb-runtimeList{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;overflow:hidden}",
1210
- ".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)}",
1211
- ".pbb-runtimeList>.pbb-runtimeRow:first-child{border-top:0}",
1212
- ".pbb-runtimeList>.pbb-runtimeRow>.pbb-row{flex:1;min-width:0}",
1213
- ".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 + "}",
1214
- ".pbb-disclosure{padding:0}",
1215
- ".pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;padding:13px 18px;cursor:pointer;color:var(--dsw-alias-label-primary)}",
1216
- ".pbb-disclosure>summary::-webkit-details-marker{display:none}",
1217
- ".pbb-disclosure[open]>summary .pbb-chev{transform:rotate(90deg)}",
1218
- ".pbb-disclosureTitle{font-size:13px;font-weight:500;flex:1}",
1219
- ".pbb-disclosureMeta{font-size:12px;color:var(--dsw-alias-label-tertiary);max-width:46%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1220
- ".pbb-disclosureBody{display:flex;flex-direction:column;gap:12px;padding:2px 18px 16px}",
1221
- ".pbb-diagnostics>summary{color:var(--dsw-alias-label-secondary)}",
1222
- "@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)}}",
1223
- "@media (prefers-reduced-motion:reduce){.pbb-section *{animation-duration:.01ms!important;animation-delay:0s!important;transition-duration:.01ms!important;transition-delay:0s!important}}",
1224
- "@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}}",
1225
- "@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}}",
1226
- "@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}}"
1227
- ].join("\n");
1119
+ const CSS = String.raw`
1120
+ .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}
1121
+ .pbb-section *{box-sizing:border-box}
1122
+ .pbb-section :is(.pbb-action,.pbb-refresh,.pbb-iconButton,.pbb-cardHead,.pbb-menuAction,.pbb-targetButton),.pbb-section input{font-family:inherit}
1123
+ .pbb-icon{display:block;flex:none;color:inherit}
1124
+ .pbb-pageHeader{display:flex;align-items:flex-start;gap:12px;min-height:56px}
1125
+ .pbb-pageHeader>div{min-width:0;flex:1;padding-top:2px}
1126
+ .pbb-title{margin:0;font-size:20px;line-height:27px;font-weight:600;letter-spacing:-.3px}
1127
+ .pbb-intro{color:var(--pbb-secondary);font-size:14px;line-height:20px;margin:5px 0 0}
1128
+ .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}
1129
+ .pbb-card{border:1px solid var(--pbb-line);border-radius:18px;background:var(--pbb-fill)}
1130
+ .pbb-cardTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;color:var(--pbb-ink)}
1131
+ .pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 16px 16px}
1132
+ .pbb-connections>.pbb-cardBody{padding:0;gap:0}
1133
+ .pbb-row{display:flex;align-items:center;gap:8px;min-width:0}
1134
+ .pbb-masterRow{display:flex;align-items:center;gap:12px;min-height:66px;padding:11px 16px}
1135
+ .pbb-controlText{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}
1136
+ .pbb-masterRow>.pbb-controlText{gap:4px}
1137
+ .pbb-controlTitle{font-size:14px;line-height:20px;font-weight:400;color:var(--pbb-ink)}
1138
+ .pbb-masterRow .pbb-controlTitle{font-weight:600}
1139
+ .pbb-controlDescription,.pbb-diag,.pbb-label,.pbb-value{font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1140
+ .pbb-serviceSummary,.pbb-channelState{display:flex;align-items:center;flex-wrap:wrap;gap:6px;font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1141
+ .pbb-serviceSummary{gap:5px}
1142
+ .pbb-serviceState{display:inline-flex}
1143
+ .pbb-metaDivider{height:10px;border-left:1px solid var(--pbb-line);margin:0 2px}
1144
+ .pbb-channel{position:relative}
1145
+ .pbb-channel::before{content:"";position:absolute;top:0;left:16px;right:16px;border-top:1px solid var(--pbb-line)}
1146
+ .pbb-channel+.pbb-channel::before{left:46px}
1147
+ .pbb-channelRow{gap:12px;min-height:60px;padding:8px 16px}
1148
+ .pbb-channelRow>.pbb-icon{color:var(--pbb-secondary)}
1149
+ .pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 16px 14px 46px}
1150
+ .pbb-channel>.pbb-diag,.pbb-channel>.pbb-rowAction{margin:0;padding:0 16px 8px 46px}
1151
+ .pbb-fold[hidden]{display:none}
1152
+ .pbb-action:disabled,.pbb-refresh:disabled,.pbb-menuAction:disabled,.pbb-targetButton:disabled{opacity:.5;cursor:default}
1153
+ .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}
1154
+ .pbb-addAction{display:flex;margin-left:auto}
1155
+ .pbb-buttonPrimary{background:var(--dsw-alias-button-primary-fill,var(--pbb-ink));border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}
1156
+ .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)}
1157
+ .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}
1158
+ .pbb-chev{display:inline-flex;flex:none;color:var(--pbb-secondary)}
1159
+ .pbb-chevIcon{width:14px;height:14px;display:block}
1160
+ [aria-expanded="true"]>.pbb-chev,.pbb-helpDisclosure[open]>summary>.pbb-chev,.pbb-disclosure[open]>summary>.pbb-chev{transform:rotate(90deg)}
1161
+ .pbb-diag{margin:0;overflow-wrap:anywhere}
1162
+ .pbb-diagBad,.pbb-bad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1163
+ .pbb-ok{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#287a45) 65%,var(--pbb-ink))}
1164
+ .pbb-label{flex:1;min-width:0;font-weight:500;color:var(--pbb-ink)}
1165
+ .pbb-value{text-align:right;overflow-wrap:anywhere}
1166
+ .pbb-token{max-width:100%;font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--pbb-secondary);overflow-wrap:anywhere}
1167
+ .pbb-empty{margin:0;padding:16px;font-size:14px;line-height:20px;color:var(--pbb-secondary)}
1168
+ .pbb-limitRow{display:flex;align-items:center;gap:12px}
1169
+ .pbb-limitControl{display:flex;align-items:center;gap:8px;flex:none}
1170
+ .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)}
1171
+ .pbb-numberInput{width:72px}
1172
+ .pbb-numberInput:disabled,.pbb-deviceNameInput:disabled{opacity:.5}
1173
+ .pbb-endpoint{user-select:all}
1174
+ .pbb-devicesSection{display:flex;flex-direction:column;gap:8px}
1175
+ .pbb-devicesHeader{display:flex;align-items:center;gap:8px;min-height:44px;padding:0 2px}
1176
+ .pbb-deviceCount{font-size:12px;line-height:18px;color:var(--pbb-secondary);white-space:nowrap}
1177
+ .pbb-devicesCard{overflow:visible}
1178
+ .pbb-deviceList{display:flex;flex-direction:column}
1179
+ .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}
1180
+ .pbb-deviceRow+.pbb-deviceRow::before{content:"";position:absolute;top:0;left:46px;right:16px;border-top:1px solid var(--pbb-line)}
1181
+ .pbb-deviceRow:has(details[open]){z-index:2}
1182
+ .pbb-deviceMain{display:flex;align-items:center;gap:12px;min-width:0}
1183
+ .pbb-deviceMain>.pbb-icon{color:var(--pbb-secondary)}
1184
+ .pbb-deviceText{display:flex;flex-direction:column;gap:2px;min-width:0}
1185
+ .pbb-deviceName{font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1186
+ .pbb-deviceMeta{display:flex;flex-wrap:wrap;gap:0 6px;color:var(--pbb-secondary);font-size:12px;line-height:18px}
1187
+ .pbb-deviceMeta>*+*::before{content:"\00B7";margin-right:6px}
1188
+ .pbb-deviceActions{grid-row:1;grid-column:2;display:flex;align-items:center}
1189
+ .pbb-deviceRenameForm,.pbb-deviceConfirm{grid-column:1/-1;display:flex;gap:8px;padding:8px 0;min-width:0}
1190
+ .pbb-deviceRenameForm{flex-wrap:wrap}
1191
+ .pbb-deviceNameInput{flex:1}
1192
+ .pbb-deviceRenameActions{display:flex;align-items:center;gap:5px;flex:none}
1193
+ .pbb-deviceConfirm{flex-direction:column}
1194
+ .pbb-deviceMessage{padding:10px 16px}
1195
+ .pbb-deviceMenu{position:relative}
1196
+ .pbb-deviceMenu>summary{list-style:none}
1197
+ .pbb-deviceMenu>summary::-webkit-details-marker{display:none}
1198
+ .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)}
1199
+ .pbb-deviceMenuInfo{display:flex;flex-direction:column;gap:3px;padding:7px 8px 10px;border-bottom:1px solid var(--pbb-line)}
1200
+ .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}
1201
+ .pbb-menuDanger{color:var(--dsw-alias-state-error-primary,#bc2222)}
1202
+ .pbb-actionBusy{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--pbb-secondary)}
1203
+ .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}
1204
+ @keyframes pbb-spin{to{transform:rotate(360deg)}}
1205
+ .pbb-rowAction{display:flex;flex-wrap:wrap;gap:8px}
1206
+ .pbb-pairingPanel{border:1px solid var(--pbb-line);border-radius:16px;padding:16px;background:var(--pbb-fill)}
1207
+ .pbb-pairingHeader{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}
1208
+ .pbb-pairingBody{display:flex;flex-direction:column;gap:10px}
1209
+ .pbb-targetList{display:flex;flex-wrap:wrap;gap:4px;min-width:0}
1210
+ .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}
1211
+ .pbb-targetSelected{color:var(--pbb-ink);border-color:var(--pbb-line);background:var(--pbb-fill)}
1212
+ .pbb-qrPanel{display:grid;grid-template-columns:224px minmax(0,1fr);gap:20px;align-items:start}
1213
+ .pbb-qrImage{display:block;width:224px;height:224px;max-width:100%;padding:8px;border-radius:12px;background:#fff}
1214
+ .pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}
1215
+ .pbb-countdownBar{height:4px;border-radius:99px;background:var(--pbb-soft);overflow:hidden}
1216
+ .pbb-countdownBar i{display:block;width:100%;height:100%;transform-origin:left center;background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:99px}
1217
+ .pbb-countdownText{font-size:12px;line-height:18px;color:var(--pbb-secondary);font-variant-numeric:tabular-nums}
1218
+ .pbb-pairInfo,.pbb-pairCodeBlock{display:flex;flex-direction:column;gap:8px;min-width:0}
1219
+ .pbb-pairCodeLabel,.pbb-qrHint{font-size:12px;line-height:18px;color:var(--pbb-secondary);margin:0}
1220
+ .pbb-pairCodeRow{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
1221
+ .pbb-pairCode{font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere;user-select:all}
1222
+ .pbb-tools{border-radius:16px;overflow:hidden}
1223
+ .pbb-toolSection+.pbb-toolSection{border-top:1px solid var(--pbb-line)}
1224
+ .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}
1225
+ .pbb-cardHead>.pbb-cardTitle{flex:1;font-weight:400}
1226
+ .pbb-toolMeta{max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1227
+ .pbb-toolMeta.pbb-ok{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#287a45) 65%,var(--pbb-ink))}
1228
+ .pbb-toolMeta.pbb-bad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1229
+ .pbb-testGrid{display:flex;flex-direction:column;gap:0}
1230
+ .pbb-testItem{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--pbb-line)}
1231
+ .pbb-testTitle{font-size:14px;line-height:20px;font-weight:500;color:var(--pbb-ink)}
1232
+ .pbb-testDescription{font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1233
+ .pbb-helpBody,.pbb-disclosureBody{display:flex;flex-direction:column;gap:14px}
1234
+ .pbb-helpSection{display:flex;flex-direction:column;gap:6px}
1235
+ .pbb-helpHeading{font-size:14px;line-height:20px;font-weight:500;color:var(--pbb-ink)}
1236
+ .pbb-helpList{margin:0;padding-left:20px;font-size:14px;line-height:22px;color:var(--pbb-secondary)}
1237
+ .pbb-helpList li+li{margin-top:4px}
1238
+ .pbb-helpText{margin:0;font-size:14px;line-height:22px;color:var(--pbb-secondary)}
1239
+ .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}
1240
+ .pbb-helpLink{color:var(--pbb-ink);text-decoration:underline;text-underline-offset:3px}
1241
+ .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}
1242
+ .pbb-helpDisclosure>summary::-webkit-details-marker{display:none}
1243
+ .pbb-helpDisclosure[open]>.pbb-helpBody{padding-top:10px}
1244
+ .pbb-runtimeList{display:flex;flex-direction:column}
1245
+ .pbb-runtimeRow{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--pbb-line)}
1246
+ .pbb-runtimeRow>.pbb-row{flex:1;min-width:0}
1247
+ .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}
1248
+ .pbb-resultRow{display:flex;align-items:flex-start;gap:6px}
1249
+ .pbb-resultIcon{display:inline-flex;flex:none;margin-top:3px;color:var(--dsw-alias-state-success-primary,#287a45)}
1250
+ .pbb-resultIconBad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1251
+ .pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;min-height:44px;padding:0 16px;cursor:pointer;font-size:14px}
1252
+ .pbb-disclosure>summary::-webkit-details-marker{display:none}
1253
+ .pbb-disclosureBody{padding:2px 16px 16px}
1254
+ .pbb-disclosureTitle{flex:1}
1255
+ .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}
1256
+ @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)}}
1257
+ @media (prefers-reduced-motion:reduce){.pbb-action,.pbb-refresh,.pbb-iconButton{transition:none;transform:none!important}.pbb-actionBusy::before{animation:none}}
1258
+ @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%}}
1259
+ @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%}}
1260
+ @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}}
1261
+ `;
1228
1262
  /**
1229
1263
  * Client bundle id, identical to the `window.__ModuleLoader__.load({ id })`
1230
1264
  * identity emitted by tsdown.config.ts. DSH's module system owns style
@@ -1252,6 +1286,102 @@ window.__ModuleLoader__.load({
1252
1286
  tag.textContent = CSS;
1253
1287
  document.head.appendChild(tag);
1254
1288
  }
1289
+ // src/client/settings-icons.ts
1290
+ /** 设置页的小图标共用线宽与 currentColor,随宿主主题变色,不额外引入图标包。 */
1291
+ function settingsIcon(name, size = 18) {
1292
+ const path = (d) => (0, react.createElement)("path", { d });
1293
+ const circle = (cx, cy, r) => (0, react.createElement)("circle", {
1294
+ cx,
1295
+ cy,
1296
+ r
1297
+ });
1298
+ return (0, react.createElement)("svg", {
1299
+ className: "pbb-icon",
1300
+ viewBox: "0 0 24 24",
1301
+ width: size,
1302
+ height: size,
1303
+ fill: "none",
1304
+ stroke: "currentColor",
1305
+ strokeWidth: 1.6,
1306
+ strokeLinecap: "round",
1307
+ strokeLinejoin: "round",
1308
+ "aria-hidden": true
1309
+ }, ...{
1310
+ phone: () => [(0, react.createElement)("rect", {
1311
+ x: 6.5,
1312
+ y: 2.5,
1313
+ width: 11,
1314
+ height: 19,
1315
+ rx: 2.2
1316
+ }), path("M10 5h4M11 18.5h2")],
1317
+ 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)],
1318
+ globe: () => [circle(12, 12, 9), path("M3 12h18M12 3a17 17 0 0 1 0 18 17 17 0 0 1 0-18")],
1319
+ 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")],
1320
+ plus: () => [path("M12 5v14M5 12h14")],
1321
+ more: () => [
1322
+ circle(5, 12, 1.2),
1323
+ circle(12, 12, 1.2),
1324
+ circle(19, 12, 1.2)
1325
+ ],
1326
+ activity: () => [path("M3 12h4l3-7 4 14 3-7h4")],
1327
+ info: () => [circle(12, 12, 9), path("M12 11v6M12 7h.01")],
1328
+ ready: () => [circle(12, 12, 9), path("m8 12 3 3 5-6")],
1329
+ attention: () => [circle(12, 12, 9), path("M12 7v6M12 17h.01")]
1330
+ }[name]());
1331
+ }
1332
+ // src/client/device-actions.ts
1333
+ /**
1334
+ * 原生 disclosure(折叠控件)负责 Enter/Space 与焦点顺序;操作仍是普通按钮,
1335
+ * 不冒充需要方向键导航的 ARIA menu。详情只在展开时进入无障碍读取顺序。
1336
+ */
1337
+ function DeviceActions(props) {
1338
+ const ref = (0, react.useRef)(null);
1339
+ const close = (restoreFocus = false) => {
1340
+ const details = ref.current;
1341
+ if (details === null) return;
1342
+ details.open = false;
1343
+ if (restoreFocus) details.querySelector("summary")?.focus();
1344
+ };
1345
+ (0, react.useEffect)(() => {
1346
+ const outside = (event) => {
1347
+ if (event.target instanceof Node && !ref.current?.contains(event.target)) close();
1348
+ };
1349
+ document.addEventListener("pointerdown", outside);
1350
+ return () => document.removeEventListener("pointerdown", outside);
1351
+ }, []);
1352
+ const act = (action) => {
1353
+ close();
1354
+ action();
1355
+ };
1356
+ return (0, react.createElement)("details", {
1357
+ ref,
1358
+ className: "pbb-deviceMenu",
1359
+ onKeyDown: (event) => {
1360
+ if (event.key !== "Escape" || !ref.current?.open) return;
1361
+ event.preventDefault();
1362
+ event.stopPropagation();
1363
+ close(true);
1364
+ }
1365
+ }, (0, react.createElement)("summary", {
1366
+ className: "pbb-iconButton",
1367
+ "aria-label": props.t("devices.moreAria", { name: props.name })
1368
+ }, settingsIcon("more", 16)), (0, react.createElement)("div", {
1369
+ className: "pbb-deviceMenuBody",
1370
+ "aria-label": props.t("devices.col.actions")
1371
+ }, (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", {
1372
+ type: "button",
1373
+ className: "pbb-menuAction",
1374
+ disabled: !props.canRename,
1375
+ "aria-label": props.t("devices.renameAria", { name: props.name }),
1376
+ onClick: () => act(props.onRename)
1377
+ }, props.t("devices.rename")), (0, react.createElement)("button", {
1378
+ type: "button",
1379
+ className: "pbb-menuAction pbb-menuDanger",
1380
+ disabled: !props.canRevoke,
1381
+ "aria-label": props.t("devices.revokeAria", { name: props.name }),
1382
+ onClick: () => act(props.onRevoke)
1383
+ }, props.t("devices.revoke"))));
1384
+ }
1255
1385
  // node_modules/qrcode/lib/can-promise.js
1256
1386
  var require_can_promise = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1257
1387
  module.exports = function() {
@@ -3785,10 +3915,6 @@ window.__ModuleLoader__.load({
3785
3915
  status: "unavailable",
3786
3916
  enabled: true
3787
3917
  });
3788
- const MISSING_LOCAL_ENABLED_HOOK = () => ({
3789
- status: "unavailable",
3790
- enabled: true
3791
- });
3792
3918
  const MISSING_LOCAL_PORT_HOOK = () => ({
3793
3919
  status: "unavailable",
3794
3920
  value: DEFAULT_LOCAL_PORT
@@ -3809,7 +3935,6 @@ window.__ModuleLoader__.load({
3809
3935
  const DIAG_KEY = {
3810
3936
  reportHook: "diag.missingReportHook",
3811
3937
  enabledHook: "diag.missingEnabledHook",
3812
- localEnabledHook: "diag.missingLocalEnabledHook",
3813
3938
  localPortHook: "diag.missingLocalPortHook",
3814
3939
  remoteEnabledHook: "diag.missingRemoteEnabledHook",
3815
3940
  remoteLimitHook: "diag.missingRemoteLimitHook",
@@ -3821,7 +3946,6 @@ window.__ModuleLoader__.load({
3821
3946
  testRelay: "diag.missingTestRelay",
3822
3947
  testPush: "diag.missingTestPush",
3823
3948
  setEnabled: "diag.missingSetEnabled",
3824
- setLocal: "diag.missingSetLocal",
3825
3949
  setLocalPort: "diag.missingSetLocalPort",
3826
3950
  setRemote: "diag.missingSetRemote",
3827
3951
  setRemoteLimit: "diag.missingSetRemoteLimit",
@@ -3836,7 +3960,6 @@ window.__ModuleLoader__.load({
3836
3960
  ["testRelay", "testRelay"],
3837
3961
  ["testPush", "testPush"],
3838
3962
  ["setEnabled", "setDeepPilotEnabled"],
3839
- ["setLocalEnabled", "setDeepPilotLocalEnabled"],
3840
3963
  ["setLocalPort", "setDeepPilotLocalPort"],
3841
3964
  ["setRemoteEnabled", "setDeepPilotRemoteEnabled"],
3842
3965
  ["setRemoteLimit", "setDeepPilotRemoteConnectionLimit"],
@@ -3857,7 +3980,6 @@ window.__ModuleLoader__.load({
3857
3980
  const can = /* @__PURE__ */ new Set();
3858
3981
  const useReport = isFn(source.useDeepPilotReport) ? source.useDeepPilotReport : MISSING_REPORT_HOOK;
3859
3982
  const useEnabled = isFn(source.useDeepPilotEnabled) ? source.useDeepPilotEnabled : MISSING_ENABLED_HOOK;
3860
- const useLocalEnabled = isFn(source.useDeepPilotLocalEnabled) ? source.useDeepPilotLocalEnabled : MISSING_LOCAL_ENABLED_HOOK;
3861
3983
  const useLocalPort = isFn(source.useDeepPilotLocalPort) ? source.useDeepPilotLocalPort : MISSING_LOCAL_PORT_HOOK;
3862
3984
  const useRemoteEnabled = isFn(source.useDeepPilotRemoteEnabled) ? source.useDeepPilotRemoteEnabled : MISSING_REMOTE_ENABLED_HOOK;
3863
3985
  const useRemoteLimit = isFn(source.useDeepPilotRemoteConnectionLimit) ? source.useDeepPilotRemoteConnectionLimit : MISSING_REMOTE_LIMIT_HOOK;
@@ -3871,9 +3993,7 @@ window.__ModuleLoader__.load({
3871
3993
  if (!isFn(source.testRelay)) diagnostics.push(t(DIAG_KEY.testRelay));
3872
3994
  if (!isFn(source.testPush)) diagnostics.push(t(DIAG_KEY.testPush));
3873
3995
  if (!isFn(source.setDeepPilotEnabled)) diagnostics.push(t(DIAG_KEY.setEnabled));
3874
- if (!isFn(source.useDeepPilotLocalEnabled)) diagnostics.push(t(DIAG_KEY.localEnabledHook));
3875
3996
  if (!isFn(source.useDeepPilotLocalPort)) diagnostics.push(t(DIAG_KEY.localPortHook));
3876
- if (!isFn(source.setDeepPilotLocalEnabled)) diagnostics.push(t(DIAG_KEY.setLocal));
3877
3997
  if (!isFn(source.setDeepPilotLocalPort)) diagnostics.push(t(DIAG_KEY.setLocalPort));
3878
3998
  if (!isFn(source.useDeepPilotRemoteEnabled)) diagnostics.push(t(DIAG_KEY.remoteEnabledHook));
3879
3999
  if (!isFn(source.setDeepPilotRemoteEnabled)) diagnostics.push(t(DIAG_KEY.setRemote));
@@ -3886,7 +4006,6 @@ window.__ModuleLoader__.load({
3886
4006
  props: {
3887
4007
  useDeepPilotReport: useReport,
3888
4008
  useDeepPilotEnabled: useEnabled,
3889
- useDeepPilotLocalEnabled: useLocalEnabled,
3890
4009
  useDeepPilotLocalPort: useLocalPort,
3891
4010
  useDeepPilotRemoteEnabled: useRemoteEnabled,
3892
4011
  useDeepPilotRemoteConnectionLimit: useRemoteLimit,
@@ -3899,7 +4018,6 @@ window.__ModuleLoader__.load({
3899
4018
  testRelay: isFn(source.testRelay) ? source.testRelay : void 0,
3900
4019
  testPush: isFn(source.testPush) ? source.testPush : void 0,
3901
4020
  setDeepPilotEnabled: isFn(source.setDeepPilotEnabled) ? source.setDeepPilotEnabled : void 0,
3902
- setDeepPilotLocalEnabled: isFn(source.setDeepPilotLocalEnabled) ? source.setDeepPilotLocalEnabled : void 0,
3903
4021
  setDeepPilotLocalPort: isFn(source.setDeepPilotLocalPort) ? source.setDeepPilotLocalPort : void 0,
3904
4022
  setDeepPilotRemoteEnabled: isFn(source.setDeepPilotRemoteEnabled) ? source.setDeepPilotRemoteEnabled : void 0,
3905
4023
  setDeepPilotRemoteConnectionLimit: isFn(source.setDeepPilotRemoteConnectionLimit) ? source.setDeepPilotRemoteConnectionLimit : void 0,
@@ -3911,15 +4029,6 @@ window.__ModuleLoader__.load({
3911
4029
  };
3912
4030
  }
3913
4031
  // src/client/settings-view-models.ts
3914
- /**
3915
- * 设置页的 view model:把原先住在渲染函数里的三块逻辑变成可单测的纯模块。
3916
- *
3917
- * 迁移前 `settings-page.ts` 是一个 1059 行的组件,funnel phase → {dot, label}、
3918
- 端口/连接数草稿的编辑与合法性、serviceStatus 派生全在渲染函数里,没有任何
3919
- 直接测试。这里按 pairing-panel.ts 的形状把它们拆成 state / action / reduce /
3920
- view 四件套——shape 与该先例一致,但**不继承它唯一的泄漏**:view model 只返回
3921
- 语义值,i18n key 的映射留在页面(措辞归页面,测试不跟着措辞漂)。
3922
- */
3923
4032
  const REMOTE_PHASE_META = {
3924
4033
  disabled: {
3925
4034
  dot: "",
@@ -4055,24 +4164,9 @@ window.__ModuleLoader__.load({
4055
4164
  return (device.customName ?? device.deviceName).trim();
4056
4165
  }
4057
4166
  // src/client/settings-page.ts
4058
- /** phase 语义标签 → i18n key。措辞归页面,view model 只认语义。 */
4059
- const REMOTE_LABEL_KEY = {
4060
- disabled: "phase.disabled",
4061
- starting: "phase.starting",
4062
- login_required: "phase.login_required",
4063
- online: "phase.online",
4064
- error: "phase.error",
4065
- unavailable: "phase.unavailable",
4066
- stopped: "phase.stopped"
4067
- };
4068
- const LOCAL_LABEL_KEY = {
4069
- disabled: "local.phaseDisabled",
4070
- starting: "local.phaseStarting",
4071
- online: "local.phaseOnline",
4072
- error: "local.phaseError",
4073
- stopped: "local.phaseStopped"
4074
- };
4075
- /** 状态条用的短状态:列标签已经写明是哪条通道,值只说状态,免得在窄格里被截断。 */
4167
+ /** 中文结论由调用处提供,原始上游信息统一放入括号,保留排查依据。 */
4168
+ const errorDetails = (error) => "(" + (error instanceof Error ? error.message : String(error)) + ")";
4169
+ /** 通道标题旁的短状态;完整端点和参数放入对应的连接选项。 */
4076
4170
  const CHANNEL_STATE_KEY = {
4077
4171
  disabled: "channel.state.disabled",
4078
4172
  starting: "channel.state.starting",
@@ -4089,21 +4183,6 @@ window.__ModuleLoader__.load({
4089
4183
  attention: "status.attention",
4090
4184
  ready: "status.ready"
4091
4185
  };
4092
- /** Inline trash icon for the compact destructive row action. Kept as a tiny
4093
- * element (no icon dependency) so the client bundle stays dependency-free
4094
- * apart from React. */
4095
- const TrashIcon = () => (0, react.createElement)("svg", {
4096
- className: "pbb-actionIcon",
4097
- viewBox: "0 0 16 16",
4098
- width: 13,
4099
- height: 13,
4100
- fill: "none",
4101
- stroke: "currentColor",
4102
- strokeWidth: 1.5,
4103
- strokeLinecap: "round",
4104
- strokeLinejoin: "round",
4105
- "aria-hidden": true
4106
- }, (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" }));
4107
4186
  /** Chevron icon (linear, currentColor). CSS rotates it 90° while its owner is
4108
4187
  * expanded, so collapsed always means "points right, more content behind". */
4109
4188
  const ChevronIcon = () => (0, react.createElement)("svg", {
@@ -4118,8 +4197,7 @@ window.__ModuleLoader__.load({
4118
4197
  strokeLinejoin: "round",
4119
4198
  "aria-hidden": true
4120
4199
  }, (0, react.createElement)("path", { d: "M6 3.5 10.5 8 6 12.5" }));
4121
- /** Check/cross marks for the relay and push result lines. They used to be the
4122
- * dingbats '✓'/'✗', which the repo's visual rules reject as character icons. */
4200
+ /** 诊断结果使用线性 SVG,与页面其余图标保持一致。 */
4123
4201
  const CheckIcon = () => (0, react.createElement)("svg", {
4124
4202
  viewBox: "0 0 16 16",
4125
4203
  width: 12,
@@ -4186,6 +4264,13 @@ window.__ModuleLoader__.load({
4186
4264
  const [remoteLimitDraft, dispatchRemoteLimitDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, REMOTE_LIMIT_RANGE), initialNumericDraft(REMOTE_LIMIT_RANGE));
4187
4265
  const [localPortDraft, dispatchLocalPortDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, LOCAL_PORT_RANGE), initialNumericDraft(LOCAL_PORT_RANGE));
4188
4266
  const [selectedPairingHost, setSelectedPairingHost] = (0, react.useState)(null);
4267
+ const [pairingOpen, setPairingOpen] = (0, react.useState)(false);
4268
+ const pageId = (0, react.useId)();
4269
+ const localOptionsId = pageId + "-local";
4270
+ const remoteOptionsId = pageId + "-remote";
4271
+ const troubleshootingId = pageId + "-troubleshooting";
4272
+ const systemId = pageId + "-system";
4273
+ const pairingId = pageId + "-pairing";
4189
4274
  const [pairingPanel, dispatchPairingPanel] = (0, react.useReducer)(pairingPanelReduce, initialPairingPanelState);
4190
4275
  const qrRequestId = (0, react.useRef)(0);
4191
4276
  const [troubleshootingOpen, setTroubleshootingOpen] = (0, react.useState)(false);
@@ -4196,7 +4281,6 @@ window.__ModuleLoader__.load({
4196
4281
  const [pushTestResult, setPushTestResult] = (0, react.useState)(null);
4197
4282
  const [pushTestError, setPushTestError] = (0, react.useState)("");
4198
4283
  const [deviceBusy, setDeviceBusy] = (0, react.useState)(null);
4199
- const [deviceBusyAction, setDeviceBusyAction] = (0, react.useState)(null);
4200
4284
  const [deviceMessage, setDeviceMessage] = (0, react.useState)("");
4201
4285
  const [editingDeviceId, setEditingDeviceId] = (0, react.useState)(null);
4202
4286
  const [deviceNameDraft, setDeviceNameDraft] = (0, react.useState)("");
@@ -4230,7 +4314,7 @@ window.__ModuleLoader__.load({
4230
4314
  setPushTestResult(result);
4231
4315
  setPushTestBusy(false);
4232
4316
  }, (error) => {
4233
- setPushTestError(error instanceof Error ? error.message : String(error));
4317
+ setPushTestError(translateWith(props.t, "push.pushFailed") + errorDetails(error));
4234
4318
  setPushTestBusy(false);
4235
4319
  });
4236
4320
  };
@@ -4247,7 +4331,7 @@ window.__ModuleLoader__.load({
4247
4331
  setRelayTestResult(result);
4248
4332
  setRelayTestBusy(false);
4249
4333
  }, (error) => {
4250
- setRelayTestError(error instanceof Error ? error.message : String(error));
4334
+ setRelayTestError(translateWith(props.t, "push.relayBad") + errorDetails(error));
4251
4335
  setRelayTestBusy(false);
4252
4336
  });
4253
4337
  };
@@ -4283,16 +4367,13 @@ window.__ModuleLoader__.load({
4283
4367
  const reportState = props.useDeepPilotReport((s) => s);
4284
4368
  hooks.report = reportState.report;
4285
4369
  if (reportState.status === "error" && reportState.message) {
4286
- diag.push(reportState.message);
4370
+ diag.push(translateWith(props.t, "diag.reportFailed") + errorDetails(reportState.message));
4287
4371
  failed = true;
4288
4372
  }
4289
4373
  const enabledState = props.useDeepPilotEnabled((s) => s);
4290
4374
  hooks.enabled = enabledState.enabled;
4291
4375
  hooks.switchReady = enabledState.status === "ready";
4292
4376
  if (enabledState.status === "unavailable") diag.push(translateWith(props.t, "diag.settingsUnavailable"));
4293
- const localEnabledState = props.useDeepPilotLocalEnabled((s) => s);
4294
- hooks.localEnabled = localEnabledState.enabled;
4295
- hooks.localSwitchReady = localEnabledState.status === "ready";
4296
4377
  const localPortState = props.useDeepPilotLocalPort((s) => s);
4297
4378
  hooks.localPort = localPortState.value;
4298
4379
  hooks.localPortReady = localPortState.status === "ready";
@@ -4310,8 +4391,6 @@ window.__ModuleLoader__.load({
4310
4391
  report: null,
4311
4392
  enabled: true,
4312
4393
  switchReady: false,
4313
- localEnabled: true,
4314
- localSwitchReady: false,
4315
4394
  localPort: DEFAULT_LOCAL_PORT,
4316
4395
  localPortReady: false,
4317
4396
  remoteEnabled: false,
@@ -4324,14 +4403,12 @@ window.__ModuleLoader__.load({
4324
4403
  try {
4325
4404
  readHooks();
4326
4405
  } catch (error) {
4327
- diag.push(translateWith(props.t, "diag.renderError") + (error instanceof Error ? error.message : String(error)));
4406
+ diag.push(translateWith(props.t, "diag.renderError") + errorDetails(error));
4328
4407
  failed = true;
4329
4408
  }
4330
4409
  const report = hooks.report;
4331
4410
  const enabled = hooks.enabled;
4332
4411
  const switchReady = hooks.switchReady;
4333
- const localEnabled = hooks.localEnabled;
4334
- const localSwitchReady = hooks.localSwitchReady;
4335
4412
  const localPort = hooks.localPort;
4336
4413
  const localPortReady = hooks.localPortReady;
4337
4414
  const remoteEnabled = hooks.remoteEnabled;
@@ -4371,7 +4448,7 @@ window.__ModuleLoader__.load({
4371
4448
  }, (error) => {
4372
4449
  dispatchLocalPortDraft({
4373
4450
  type: "failed",
4374
- message: translateWith(props.t, "local.portFailed") + (error instanceof Error ? error.message : String(error))
4451
+ message: translateWith(props.t, "local.portFailed") + errorDetails(error)
4375
4452
  });
4376
4453
  });
4377
4454
  };
@@ -4392,7 +4469,7 @@ window.__ModuleLoader__.load({
4392
4469
  }, (error) => {
4393
4470
  dispatchRemoteLimitDraft({
4394
4471
  type: "failed",
4395
- message: translateWith(props.t, "remote.limitFailed") + (error instanceof Error ? error.message : String(error))
4472
+ message: translateWith(props.t, "remote.limitFailed") + errorDetails(error)
4396
4473
  });
4397
4474
  });
4398
4475
  };
@@ -4442,7 +4519,7 @@ window.__ModuleLoader__.load({
4442
4519
  dispatchPairingPanel({
4443
4520
  type: "failed",
4444
4521
  requestId,
4445
- message: translateWith(props.t, "pair.qrFailed") + (error instanceof Error ? error.message : String(error))
4522
+ message: translateWith(props.t, "pair.qrFailed") + errorDetails(error)
4446
4523
  });
4447
4524
  });
4448
4525
  };
@@ -4465,7 +4542,7 @@ window.__ModuleLoader__.load({
4465
4542
  }, (error) => {
4466
4543
  dispatchPairingPanel({
4467
4544
  type: "notice",
4468
- message: translateWith(props.t, "pair.infoCopyFailed") + (error instanceof Error ? error.message : String(error))
4545
+ message: translateWith(props.t, "pair.infoCopyFailed") + errorDetails(error)
4469
4546
  });
4470
4547
  });
4471
4548
  };
@@ -4486,15 +4563,13 @@ window.__ModuleLoader__.load({
4486
4563
  const revokeDevice = (deviceId, _name) => {
4487
4564
  if (!can("revokeDevice")) return;
4488
4565
  setDeviceBusy(deviceId);
4489
- setDeviceBusyAction("revoke");
4490
4566
  setDeviceMessage("");
4491
4567
  props.revokeDevice(deviceId).then(() => {
4492
4568
  setDeviceMessage(translateWith(props.t, "devices.revoked"));
4493
4569
  }, (error) => {
4494
- setDeviceMessage(translateWith(props.t, "devices.revokeFailed") + (error instanceof Error ? error.message : String(error)));
4570
+ setDeviceMessage(translateWith(props.t, "devices.revokeFailed") + errorDetails(error));
4495
4571
  }).finally(() => {
4496
4572
  setDeviceBusy(null);
4497
- setDeviceBusyAction(null);
4498
4573
  });
4499
4574
  };
4500
4575
  const deviceLabel$1 = (device) => deviceLabel(device) || translateWith(props.t, "devices.unnamed");
@@ -4517,17 +4592,15 @@ window.__ModuleLoader__.load({
4517
4592
  return;
4518
4593
  }
4519
4594
  setDeviceBusy(deviceId);
4520
- setDeviceBusyAction("rename");
4521
4595
  setDeviceMessage("");
4522
4596
  props.setDeviceName(deviceId, normalized === "" ? null : normalized).then(() => {
4523
4597
  setDeviceMessage(normalized === "" ? translateWith(props.t, "devices.nameCleared") : translateWith(props.t, "devices.renamed"));
4524
4598
  setEditingDeviceId(null);
4525
4599
  setDeviceNameDraft("");
4526
4600
  }, (error) => {
4527
- setDeviceMessage(translateWith(props.t, "devices.renameFailed") + (error instanceof Error ? error.message : String(error)));
4601
+ setDeviceMessage(translateWith(props.t, "devices.renameFailed") + errorDetails(error));
4528
4602
  }).finally(() => {
4529
4603
  setDeviceBusy(null);
4530
- setDeviceBusyAction(null);
4531
4604
  });
4532
4605
  };
4533
4606
  const visibleDevices$1 = visibleDevices(report);
@@ -4543,10 +4616,10 @@ window.__ModuleLoader__.load({
4543
4616
  "data-armed": armed || void 0,
4544
4617
  "aria-busy": busy,
4545
4618
  "aria-label": translateWith(props.t, "devices.col.name") + ": " + label
4546
- }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", {
4619
+ }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, settingsIcon("phone"), (0, react.createElement)("div", { className: "pbb-deviceText" }, (0, react.createElement)("span", {
4547
4620
  className: "pbb-deviceName",
4548
4621
  title: label
4549
- }, 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", {
4622
+ }, 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", {
4550
4623
  className: "pbb-deviceRenameForm",
4551
4624
  onSubmit: (event) => {
4552
4625
  event.preventDefault();
@@ -4584,25 +4657,22 @@ window.__ModuleLoader__.load({
4584
4657
  title: translateWith(props.t, "devices.nameCancel"),
4585
4658
  "aria-label": translateWith(props.t, "devices.nameCancel"),
4586
4659
  onClick: cancelDeviceRename
4587
- }, 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", {
4588
- className: "pbb-deviceActions",
4589
- "aria-label": translateWith(props.t, "devices.col.actions")
4590
- }, !editing ? (0, react.createElement)("button", {
4591
- type: "button",
4592
- className: "pbb-action",
4593
- disabled: busy || editingDeviceId !== null || !can("setDeviceName"),
4594
- "aria-label": translateWith(props.t, "devices.renameAria", { name: label }),
4595
- onClick: () => beginDeviceRename(d)
4596
- }, translateWith(props.t, "devices.rename")) : null, (0, react.createElement)("button", {
4597
- type: "button",
4598
- className: "pbb-action pbb-actionDangerGhost",
4599
- disabled: busy,
4600
- "aria-label": busy && deviceBusyAction === "revoke" ? translateWith(props.t, "devices.deleting", { name: label }) : translateWith(props.t, "devices.revokeAria", { name: label }),
4601
- onClick: () => {
4660
+ }, 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", {
4661
+ className: "pbb-actionBusy",
4662
+ role: "status"
4663
+ }, translateWith(props.t, "devices.deleting")) : (0, react.createElement)(DeviceActions, {
4664
+ name: label,
4665
+ fingerprint,
4666
+ customName: d.customName,
4667
+ canRename: editingDeviceId === null && can("setDeviceName"),
4668
+ canRevoke: can("revokeDevice"),
4669
+ t: props.t,
4670
+ onRename: () => beginDeviceRename(d),
4671
+ onRevoke: () => {
4602
4672
  cancelDeviceRename();
4603
4673
  setArmedRevokeDeviceId(d.deviceId);
4604
4674
  }
4605
- }, 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", {
4675
+ })), armed && !busy ? (0, react.createElement)("div", { className: "pbb-deviceConfirm" }, (0, react.createElement)("p", {
4606
4676
  className: "pbb-diag pbb-diagBad",
4607
4677
  role: "alert"
4608
4678
  }, translateWith(props.t, "devices.revokeConfirm", { name: label })), (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
@@ -4620,27 +4690,32 @@ window.__ModuleLoader__.load({
4620
4690
  }, translateWith(props.t, "devices.nameCancel")))) : null);
4621
4691
  })) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
4622
4692
  const switchTitle = translateWith(props.t, "master.title");
4623
- const switchDesc = switchReady ? enabled ? translateWith(props.t, "master.on") : translateWith(props.t, "master.off") : translateWith(props.t, "master.loading");
4624
4693
  const localMeta = report === null ? null : LOCAL_PHASE_META[report.local.phase];
4625
4694
  const remoteMeta = report === null ? null : REMOTE_PHASE_META[report.remote.phase];
4626
4695
  const service = serviceStatus({
4627
4696
  enabled,
4628
- switchReady,
4697
+ switchReady: switchReady && report !== null,
4629
4698
  localDot: localMeta?.dot,
4630
4699
  remoteDot: remoteMeta?.dot
4631
4700
  });
4632
4701
  const serviceStatusText = translateWith(props.t, SERVICE_STATUS_KEY[service.kind]);
4633
- const serviceDot = service.dot;
4634
- const localPhaseText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, LOCAL_LABEL_KEY[localMeta.label]);
4635
- const remotePhaseText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, REMOTE_LABEL_KEY[remoteMeta.label]);
4636
4702
  const localStateText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[localMeta.label]);
4637
4703
  const remoteStateText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[remoteMeta.label]);
4638
4704
  /** 一条失败事件的单行文本:时间 · 类型 · 分因,附带可选排查字段。 */
4639
4705
  const diagnosticLine = (failure) => {
4640
4706
  const base = translateWith(props.t, "diagnostics.item", {
4641
4707
  time: new Date(failure.ts).toLocaleString(),
4642
- kind: failure.kind,
4643
- reason: failure.reason
4708
+ kind: translateWith(props.t, {
4709
+ "upgrade-rejected": "diagnostics.kind.upgrade",
4710
+ "pair-rejected": "diagnostics.kind.pair",
4711
+ "auth-failed": "diagnostics.kind.auth",
4712
+ "connection-opened": "diagnostics.kind.opened",
4713
+ "connection-closed": "diagnostics.kind.closed",
4714
+ "connection-error": "diagnostics.kind.error",
4715
+ identity: "diagnostics.kind.identity",
4716
+ storage: "diagnostics.kind.storage"
4717
+ }[failure.kind] ?? "diagnostics.kind.unknown"),
4718
+ reason: "(" + failure.kind + " / " + failure.reason + ")"
4644
4719
  });
4645
4720
  const extras = [];
4646
4721
  if (failure.attemptId !== void 0) extras.push(translateWith(props.t, "diagnostics.attempt", { id: failure.attemptId }));
@@ -4677,12 +4752,12 @@ window.__ModuleLoader__.load({
4677
4752
  });
4678
4753
  }, (error) => {
4679
4754
  setDiagnosticsCopy({
4680
- message: translateWith(props.t, "diagnostics.copyFailed") + (error instanceof Error ? error.message : String(error)),
4755
+ message: translateWith(props.t, "diagnostics.copyFailed") + errorDetails(error),
4681
4756
  failed: true
4682
4757
  });
4683
4758
  });
4684
4759
  };
4685
- 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", {
4760
+ 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", {
4686
4761
  type: "button",
4687
4762
  className: "pbb-action",
4688
4763
  onClick: copyDiagnostics,
@@ -4694,14 +4769,10 @@ window.__ModuleLoader__.load({
4694
4769
  className: "pbb-diag",
4695
4770
  key: String(index)
4696
4771
  }, diagnosticLine(failure)))));
4697
- /** 状态总览的一格:标签 / 点位 + 值 / 次行;整格 title 兜住被裁掉的全文。
4698
- * dot 为 null 表示这一格是计数而非状态,不画点位(灰点会被读成「未启用」)。 */
4699
- const statusCell = (label, dot, value, sub) => (0, react.createElement)("div", {
4700
- className: "pbb-statusCell",
4701
- title: label + " · " + value + (sub === "" ? "" : " · " + sub)
4702
- }, (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));
4703
- /** 折叠区:grid-rows 0fr→1fr 平滑开合(mockup 的做法),不用 <details> 的瞬时跳变。 */
4704
- const fold = (open, body) => (0, react.createElement)("div", { className: "pbb-fold" + (open ? "" : " pbb-foldClosed") }, (0, react.createElement)("div", null, body));
4772
+ const fold = (open, body) => (0, react.createElement)("div", {
4773
+ className: "pbb-fold",
4774
+ hidden: !open
4775
+ }, body);
4705
4776
  /** 配对倒计时:仅在 QR 面板打开且 grant 在场时每秒刷新一次。 */
4706
4777
  (0, react.useEffect)(() => {
4707
4778
  if (!panel.open || pairingPanel.grant === null) return;
@@ -4711,44 +4782,19 @@ window.__ModuleLoader__.load({
4711
4782
  const grantRemainingMs = pairingPanel.grant === null ? 0 : Math.max(0, pairingPanel.grant.expiresAt - nowMs);
4712
4783
  const grantRemainingText = String(Math.floor(grantRemainingMs / 6e4)) + ":" + String(Math.floor(grantRemainingMs / 1e3) % 60).padStart(2, "0");
4713
4784
  const grantRemainingPct = Math.min(100, grantRemainingMs / 3e5 * 100);
4714
- const localDesc = localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading");
4715
- const remoteDesc = remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading");
4716
- /** 端点 → 状态条副行只暴露 host[:port],去掉协议前缀让一行能放下。 */
4717
- const endpointHost = (value) => {
4718
- if (value === void 0 || value === "") return void 0;
4719
- try {
4720
- return new URL(value).host;
4721
- } catch {
4722
- return value;
4723
- }
4724
- };
4725
- 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;
4726
- 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;
4727
- const isOffPhase = (phase) => phase === "disabled" || phase === "stopped";
4728
- const localSettled = report === null || (localEnabled ? report.local.phase === "online" : isOffPhase(report.local.phase));
4729
- const remoteSettled = report === null || (remoteEnabled ? report.remote.phase === "online" : isOffPhase(report.remote.phase));
4730
- const localChannelDesc = localSettled ? localDesc : localPhaseText + " · " + localDesc;
4731
- const remoteChannelDesc = remoteSettled ? remoteDesc : remotePhaseText + " · " + remoteDesc;
4732
4785
  const recentFailures = report?.diagnostics?.recentFailures ?? [];
4733
4786
  const troubleGood = relayTestResult !== null ? relayTestResult.overall === "ok" : pushTestResult !== null ? pushTestResult.overall === "sent" : false;
4734
4787
  const troubleBad = relayTestResult !== null ? relayTestResult.overall !== "ok" : pushTestResult !== null ? pushTestResult.overall !== "sent" : recentFailures.length > 0;
4735
- 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");
4736
- 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", {
4788
+ 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 }) : "";
4789
+ const pairingSection = (0, react.createElement)("div", {
4790
+ id: pairingId,
4791
+ hidden: !pairingOpen
4792
+ }, !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", {
4737
4793
  type: "button",
4738
- className: "pbb-refresh",
4739
- onClick: () => {
4740
- if (can("refresh")) props.refresh();
4741
- }
4742
- }, 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", {
4743
- type: "button",
4744
- className: "pbb-action pbb-buttonPrimary",
4745
- disabled: panel.busy || report !== null && !report.pairingReady,
4746
- onClick: () => {
4747
- if (pairingTarget === null) return;
4748
- if (panel.open) hidePairingQR();
4749
- else showPairingQR(pairingTarget);
4750
- }
4751
- }, 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", {
4794
+ className: "pbb-action",
4795
+ disabled: panel.busy || !report.pairingReady,
4796
+ onClick: () => showPairingQR(pairingTarget)
4797
+ }, 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", {
4752
4798
  type: "button",
4753
4799
  key: target.host,
4754
4800
  className: "pbb-targetButton" + (target.host === pairingTarget?.host ? " pbb-targetSelected" : ""),
@@ -4778,35 +4824,42 @@ window.__ModuleLoader__.load({
4778
4824
  className: "pbb-action pbb-buttonPrimary",
4779
4825
  onClick: copyPairingInfo,
4780
4826
  "data-testid": "pairingInfoCopy"
4781
- }, 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", {
4827
+ }, 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)));
4828
+ 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", {
4782
4829
  type: "button",
4783
- role: "switch",
4784
- "aria-checked": enabled,
4785
- "aria-label": switchTitle,
4830
+ className: "pbb-refresh",
4831
+ disabled: !can("refresh"),
4832
+ onClick: () => props.refresh?.()
4833
+ }, settingsIcon("refresh", 14), translateWith(props.t, "meta.refresh"))), (0, react.createElement)("section", {
4834
+ className: "pbb-card pbb-connections",
4835
+ "aria-label": translateWith(props.t, "connection.title")
4836
+ }, (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", {
4837
+ className: "pbb-metaDivider",
4838
+ "aria-hidden": true
4839
+ }), (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, {
4840
+ checked: enabled,
4841
+ label: switchTitle,
4786
4842
  disabled: !switchReady,
4787
- className: "pbb-switch" + (enabled ? " pbb-switchOn" : ""),
4788
- onClick: () => {
4789
- if (can("setEnabled")) props.setDeepPilotEnabled(!enabled);
4790
- }
4791
- })), (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", {
4792
- type: "button",
4793
- role: "switch",
4794
- "aria-checked": localEnabled,
4795
- "aria-label": translateWith(props.t, "local.title"),
4796
- disabled: !localSwitchReady,
4797
- className: "pbb-switch" + (localEnabled ? " pbb-switchOn" : ""),
4798
- onClick: () => {
4799
- if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
4843
+ onChange: (next) => {
4844
+ if (can("setEnabled")) props.setDeepPilotEnabled(next);
4800
4845
  }
4801
- })), 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", {
4846
+ })), (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)("button", {
4802
4847
  type: "button",
4803
- className: "pbb-expand",
4848
+ className: "pbb-iconButton pbb-expand",
4849
+ "aria-label": translateWith(props.t, "local.options"),
4804
4850
  "aria-expanded": localOptionsOpen,
4851
+ "aria-controls": localOptionsId,
4805
4852
  onClick: () => setLocalOptionsOpen(!localOptionsOpen)
4806
- }, 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", {
4807
- className: "pbb-switchTitle",
4853
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()))), report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", {
4854
+ className: "pbb-diag pbb-diagBad",
4855
+ role: "alert"
4856
+ }, 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", {
4857
+ className: "pbb-channelBody",
4858
+ id: localOptionsId
4859
+ }, (0, react.createElement)("p", { className: "pbb-controlDescription" }, translateWith(props.t, "local.autoRoute")), 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", {
4860
+ className: "pbb-controlTitle",
4808
4861
  htmlFor: "deeppilot-local-port"
4809
- }, 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", {
4862
+ }, 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", {
4810
4863
  id: "deeppilot-local-port",
4811
4864
  className: "pbb-numberInput",
4812
4865
  type: "number",
@@ -4832,30 +4885,38 @@ window.__ModuleLoader__.load({
4832
4885
  className: "pbb-action",
4833
4886
  disabled: !localPortReady || !localPortView.valid || localPortView.unchanged,
4834
4887
  onClick: applyLocalPort
4835
- }, 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", {
4836
- type: "button",
4837
- role: "switch",
4838
- "aria-checked": remoteEnabled,
4839
- "aria-label": translateWith(props.t, "remote.title"),
4888
+ }, 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", {
4889
+ className: "pbb-metaDivider",
4890
+ "aria-hidden": true
4891
+ }), (0, react.createElement)("span", null, "Tailscale Funnel"))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4892
+ checked: remoteEnabled,
4893
+ label: translateWith(props.t, "remote.title"),
4840
4894
  disabled: !remoteSwitchReady,
4841
- className: "pbb-switch" + (remoteEnabled ? " pbb-switchOn" : ""),
4842
- onClick: () => {
4843
- if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
4895
+ onChange: (next) => {
4896
+ if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(next);
4844
4897
  }
4845
- })), 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", {
4898
+ }), (0, react.createElement)("button", {
4899
+ type: "button",
4900
+ className: "pbb-iconButton pbb-expand",
4901
+ "aria-label": translateWith(props.t, "remote.options"),
4902
+ "aria-expanded": remoteOptionsOpen,
4903
+ "aria-controls": remoteOptionsId,
4904
+ onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4905
+ }, (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", {
4846
4906
  className: "pbb-action pbb-buttonPrimary",
4847
4907
  href: report.remote.authURL,
4848
4908
  target: "_blank",
4849
4909
  rel: "noreferrer"
4850
- }, 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", {
4851
- type: "button",
4852
- className: "pbb-expand",
4853
- "aria-expanded": remoteOptionsOpen,
4854
- onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4855
- }, 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", {
4856
- className: "pbb-switchTitle",
4910
+ }, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", {
4911
+ className: "pbb-diag pbb-diagBad",
4912
+ role: "alert"
4913
+ }, translateWith(props.t, "remote.errorDetails", { message: report.remote.message })) : null, fold(remoteOptionsOpen, (0, react.createElement)("div", {
4914
+ className: "pbb-channelBody",
4915
+ id: remoteOptionsId
4916
+ }, 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", {
4917
+ className: "pbb-controlTitle",
4857
4918
  htmlFor: "deeppilot-funnel-source-limit"
4858
- }, 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", {
4919
+ }, 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", {
4859
4920
  id: "deeppilot-funnel-source-limit",
4860
4921
  className: "pbb-numberInput",
4861
4922
  type: "number",
@@ -4881,56 +4942,83 @@ window.__ModuleLoader__.load({
4881
4942
  className: "pbb-action",
4882
4943
  disabled: !remoteConnectionLimitReady || !remoteLimitView.valid || remoteLimitView.unchanged,
4883
4944
  onClick: applyRemoteLimit
4884
- }, 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", {
4945
+ }, 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", {
4885
4946
  className: "pbb-helpLink",
4886
4947
  href: "https://tailscale.com/docs/features/tailscale-funnel",
4887
4948
  target: "_blank",
4888
4949
  rel: "noreferrer"
4889
- }, 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", {
4890
- className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4950
+ }, translateWith(props.t, "help.funnelDocs"))))))))))), (0, react.createElement)("section", {
4951
+ className: "pbb-devicesSection",
4952
+ "aria-label": translateWith(props.t, "devices.title")
4953
+ }, (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, {
4954
+ variant: "outline",
4955
+ size: "sm",
4956
+ icon: settingsIcon("plus", 16),
4957
+ disabled: panel.busy || report === null || !report.pairingReady || !can("beginPairing"),
4958
+ "aria-expanded": pairingOpen,
4959
+ "aria-controls": pairingId,
4960
+ onClick: () => {
4961
+ if (pairingOpen) {
4962
+ hidePairingQR();
4963
+ setPairingOpen(false);
4964
+ return;
4965
+ }
4966
+ setPairingOpen(true);
4967
+ if (pairingTarget !== null && report?.pairingReady) showPairingQR(pairingTarget);
4968
+ }
4969
+ }, translateWith(props.t, pairingOpen ? "pair.close" : "pair.add")))), pairingSection, (0, react.createElement)("div", { className: "pbb-card pbb-devicesCard" }, deviceMessage ? (0, react.createElement)("p", {
4970
+ className: "pbb-diag pbb-deviceMessage" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4891
4971
  role: "status"
4892
- }, deviceMessage) : null, deviceRows)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
4972
+ }, deviceMessage) : null, deviceRows)), (0, react.createElement)("section", {
4973
+ className: "pbb-card pbb-tools",
4974
+ "aria-label": translateWith(props.t, "tools.title")
4975
+ }, (0, react.createElement)("div", { className: "pbb-toolSection" }, (0, react.createElement)("button", {
4893
4976
  type: "button",
4894
4977
  className: "pbb-cardHead",
4895
4978
  "aria-expanded": troubleshootingOpen,
4979
+ "aria-controls": troubleshootingId,
4896
4980
  onClick: () => setTroubleshootingOpen(!troubleshootingOpen)
4897
- }, (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", {
4981
+ }, 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", {
4982
+ className: "pbb-cardBody",
4983
+ id: troubleshootingId
4984
+ }, (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", {
4898
4985
  type: "button",
4899
4986
  className: "pbb-action",
4900
4987
  disabled: relayTestBusy,
4901
4988
  onClick: runRelayTest
4902
- }, 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", {
4989
+ }, 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", {
4903
4990
  type: "button",
4904
4991
  className: "pbb-action",
4905
4992
  disabled: pushTestBusy,
4906
4993
  onClick: sendPushTest
4907
- }, 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", {
4908
- type: "button",
4909
- role: "switch",
4910
- "aria-checked": debugEnabled,
4911
- "aria-label": translateWith(props.t, "troubleshooting.debugTitle"),
4994
+ }, 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, {
4995
+ checked: debugEnabled,
4996
+ label: translateWith(props.t, "troubleshooting.debugTitle"),
4912
4997
  disabled: !debugSwitchReady,
4913
- className: "pbb-switch" + (debugEnabled ? " pbb-switchOn" : ""),
4914
- onClick: () => {
4915
- if (can("setDebug")) props.setDeepPilotDebug(!debugEnabled);
4998
+ onChange: (next) => {
4999
+ if (can("setDebug")) props.setDeepPilotDebug(next);
4916
5000
  }
4917
5001
  }))), relayTestError ? (0, react.createElement)("p", {
4918
5002
  className: "pbb-diag pbb-diagBad",
4919
5003
  role: "alert"
4920
- }, 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", {
5004
+ }, 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", {
4921
5005
  className: "pbb-diag pbb-diagBad",
4922
5006
  role: "alert"
4923
- }, 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", {
5007
+ }, 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", {
4924
5008
  type: "button",
4925
5009
  className: "pbb-cardHead",
4926
5010
  "aria-expanded": systemOpen,
5011
+ "aria-controls": systemId,
4927
5012
  onClick: () => setSystemOpen(!systemOpen)
4928
- }, (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", {
5013
+ }, 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", {
5014
+ className: "pbb-cardBody",
5015
+ id: systemId
5016
+ }, (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", {
4929
5017
  className: "pbb-updateRow",
4930
5018
  href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
4931
5019
  target: "_blank",
4932
5020
  rel: "noreferrer"
4933
- }, (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", {
5021
+ }, (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", {
4934
5022
  className: "pbb-card pbb-disclosure pbb-diagnostics",
4935
5023
  open: failed
4936
5024
  }, (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);
@@ -5193,19 +5281,10 @@ window.__ModuleLoader__.load({
5193
5281
  if (snap?.status === "ready" && snap.value !== void 0) return snap.value.enabled !== false;
5194
5282
  return enabledStore.getSnapshot().enabled;
5195
5283
  };
5196
- const localEnabledStore = createSnapshotStore({
5197
- status: "loading",
5198
- enabled: true
5199
- });
5200
5284
  const localPortStore = createSnapshotStore({
5201
5285
  status: "loading",
5202
5286
  value: DEFAULT_LOCAL_PORT
5203
5287
  });
5204
- const lastConfirmedLocalEnabled = () => {
5205
- const snap = scope?.getSnapshot();
5206
- if (snap?.status === "ready") return snap.value?.local?.enabled !== false;
5207
- return localEnabledStore.getSnapshot().enabled;
5208
- };
5209
5288
  const lastConfirmedLocalPort = () => {
5210
5289
  const snap = scope?.getSnapshot();
5211
5290
  if (snap?.status === "ready") return normalizeLocalPort(snap.value?.local?.port);
@@ -5240,46 +5319,16 @@ window.__ModuleLoader__.load({
5240
5319
  const adoptLocal = () => {
5241
5320
  if (scope === void 0) return;
5242
5321
  const snap = scope.getSnapshot();
5243
- if (snap.status === "ready") {
5244
- localEnabledStore.set({
5245
- status: "ready",
5246
- enabled: snap.value?.local?.enabled !== false
5247
- });
5248
- localPortStore.set({
5249
- status: "ready",
5250
- value: normalizeLocalPort(snap.value?.local?.port)
5251
- });
5252
- } else if (snap.status === "unavailable") {
5253
- localEnabledStore.set({
5254
- status: "unavailable",
5255
- enabled: true
5256
- });
5257
- localPortStore.set({
5258
- status: "unavailable",
5259
- value: DEFAULT_LOCAL_PORT
5260
- });
5261
- }
5262
- };
5263
- scopeAdoptions.push(adoptLocal);
5264
- const setDeepPilotLocalEnabled = (value) => {
5265
- const previous = lastConfirmedLocalEnabled();
5266
- localEnabledStore.set({
5322
+ if (snap.status === "ready") localPortStore.set({
5267
5323
  status: "ready",
5268
- enabled: value
5324
+ value: normalizeLocalPort(snap.value?.local?.port)
5269
5325
  });
5270
- if (scope === void 0) return;
5271
- const currentLocal = scope.getSnapshot().value?.local ?? {};
5272
- scope.set("local", {
5273
- ...currentLocal,
5274
- enabled: value
5275
- }).then(() => {}, (error) => {
5276
- localEnabledStore.set({
5277
- status: "ready",
5278
- enabled: previous
5279
- });
5280
- console.error("[deeppilot] failed to persist local.enabled=" + String(value) + ": " + (error instanceof Error ? error.message : String(error)));
5326
+ else if (snap.status === "unavailable") localPortStore.set({
5327
+ status: "unavailable",
5328
+ value: DEFAULT_LOCAL_PORT
5281
5329
  });
5282
5330
  };
5331
+ scopeAdoptions.push(adoptLocal);
5283
5332
  const setDeepPilotLocalPort = async (value) => {
5284
5333
  const next = normalizeLocalPort(value);
5285
5334
  if (next !== value) throw new RangeError("local port must be an integer between 1024 and 65535");
@@ -5454,7 +5503,6 @@ window.__ModuleLoader__.load({
5454
5503
  hooks: {
5455
5504
  deepPilotReport: store,
5456
5505
  deepPilotEnabled: enabledStore,
5457
- deepPilotLocalEnabled: localEnabledStore,
5458
5506
  deepPilotLocalPort: localPortStore,
5459
5507
  deepPilotRemoteEnabled: remoteEnabledStore,
5460
5508
  deepPilotRemoteConnectionLimit: remoteConnectionLimitStore,
@@ -5469,7 +5517,6 @@ window.__ModuleLoader__.load({
5469
5517
  testRelay: testRelayConnection,
5470
5518
  testPush: sendTestPush,
5471
5519
  setDeepPilotEnabled,
5472
- setDeepPilotLocalEnabled,
5473
5520
  setDeepPilotLocalPort,
5474
5521
  setDeepPilotRemoteEnabled,
5475
5522
  setDeepPilotRemoteConnectionLimit,