dsh-deeppilot 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -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": "最近失败",
@@ -557,7 +586,7 @@ window.__ModuleLoader__.load({
557
586
  "local.portApplied": "局域网端口已保存,监听器正在重新加载。",
558
587
  "local.portInvalid": "请输入 1024–65535 之间的整数。",
559
588
  "local.portFailed": "局域网端口保存失败:",
560
- "remote.title": "远程连接(Tailscale Funnel)",
589
+ "remote.title": "远程连接",
561
590
  "remote.on": "已配置:内嵌 Funnel 会自动启动并同步状态。",
562
591
  "remote.off": "关闭:仅保留局域网连接。",
563
592
  "remote.openAuth": "打开授权页面",
@@ -605,12 +634,12 @@ window.__ModuleLoader__.load({
605
634
  "panel.tokenRotateWarning": "更换会使当前 Token 立即失效、断开所有手机连接;5 秒内再次点击确认。",
606
635
  "panel.tokenRotated": "新 Token 已生效,旧 Token 已失效;请重新配对所有设备。",
607
636
  "panel.tokenRotateFailed": "更换失败:",
608
- "pair.qrPanelTitle": "扫码添加当前手机",
637
+ "pair.qrPanelTitle": "添加手机",
609
638
  "pair.kind.public": "公网",
610
639
  "pair.kind.lan": "内网",
611
640
  "pair.noAddress": "没有可用地址",
612
641
  "pair.noAddressHelp": "没有可用的局域网或远程入口。请确认局域网连接已启动,或启用远程连接。",
613
- "pair.disabledHelp": "DeepPilot 连接已关闭。先在上方开启连接,再添加手机。",
642
+ "pair.disabledHelp": "请先开启“允许手机连接”,再添加手机。",
614
643
  "pair.qrAlt": "DeepPilot 配对二维码",
615
644
  "pair.qrShow": "显示二维码",
616
645
  "pair.qrHide": "隐藏二维码",
@@ -621,7 +650,7 @@ window.__ModuleLoader__.load({
621
650
  "pair.infoLabel": "配对信息(复制一次,粘贴到 App 即可)",
622
651
  "pair.infoCopyDone": "配对信息已复制",
623
652
  "pair.infoCopyFailed": "配对信息复制失败:",
624
- "pair.qrHint": "二维码与下面的「配对信息」内容相同:包含{kind}地址、一次性配对码、证书指纹(内网时)与主机标识。在局域网与公网之间切换会立即生成对应的新配对信息,配对码 5 分钟后失效。",
653
+ "pair.qrHint": "二维码与「配对信息」内容相同:包含{kind}地址、一次性配对码、证书指纹(内网时)与主机标识。在局域网与公网之间切换会立即生成对应的新配对信息,配对码 5 分钟后失效。",
625
654
  "advanced.summary": "高级信息",
626
655
  "advanced.protocolVersion": "协议版本",
627
656
  "advanced.serverVersion": "服务器版本",
@@ -647,9 +676,9 @@ window.__ModuleLoader__.load({
647
676
  "push.pushNotEnabled": "推送未启用",
648
677
  "push.relayStep.health": "服务可达",
649
678
  "push.relayStep.enroll": "自动注册",
650
- "push.prefix.ok": "✓ ",
651
- "push.prefix.fail": "✗ ",
652
- "devices.title": "设备",
679
+ "push.prefix.ok": "通过:",
680
+ "push.prefix.fail": "未通过:",
681
+ "devices.title": "已配对设备",
653
682
  "devices.col.name": "设备",
654
683
  "devices.col.appVersion": "App 版本",
655
684
  "devices.col.push": "离线推送",
@@ -680,13 +709,13 @@ window.__ModuleLoader__.load({
680
709
  "devices.pushNotRegistered": "未注册",
681
710
  "devices.pushEnvProduction": "生产",
682
711
  "devices.pushEnvDevelopment": "开发",
683
- "devices.empty": "还没有 v2 设备。请在 iPhone 上扫描一次性配对二维码。",
712
+ "devices.empty": "还没有配对设备。点击“添加手机”,然后在 DeepPilot 中扫码连接。",
684
713
  "help.remoteTitle": "远程连接帮助",
685
714
  "help.recommended": "推荐设置流程",
686
- "help.step1": "先打开\"DeepPilot 连接\",再打开\"远程连接(Tailscale Funnel)\"。",
715
+ "help.step1": "先打开“允许手机连接”,再打开“远程连接”。",
687
716
  "help.step2": "状态变为\"等待 Tailscale 授权\"后,点击\"打开授权页面\"。",
688
717
  "help.step3": "使用有权管理此 Tailnet 的账号登录,并按授权页提示启用 Funnel。通常需要 Owner、Admin 或 Network admin 权限。",
689
- "help.step4": "返回此页面并点击\"刷新\"。远程连接标题旁出现绿点后,即可扫描下方二维码添加手机。",
718
+ "help.step4": "返回此页面并点击“刷新”。远程连接显示“已就绪”后,点击“添加手机”并扫码。",
690
719
  "help.funnelHint": "插件已内嵌 Tailscale 网络组件,这台电脑和手机都不需要另外安装 Tailscale App;但首次启用仍需由 Tailnet 管理员授权。",
691
720
  "help.httpsTitle": "授权页未自动完成时:开启 HTTPS",
692
721
  "help.httpsStep1": "打开 Tailscale 管理后台的 Network → DNS。",
@@ -745,11 +774,39 @@ window.__ModuleLoader__.load({
745
774
  "update.badge": "有新版本"
746
775
  },
747
776
  en: {
777
+ "local.options": "LAN connection options",
778
+ "remote.options": "Remote connection options",
779
+ "local.errorDetails": "LAN connection failed. Check the connection options ({message}).",
780
+ "remote.errorDetails": "Remote connection failed. Check authorization and connection options ({message}).",
781
+ "pair.add": "Add phone",
782
+ "pair.close": "Collapse",
783
+ "devices.count": "{count} devices",
784
+ "devices.moreAria": "More actions for “{name}”",
785
+ "tools.title": "Tools and information",
786
+ "diag.reportFailed": "Failed to read connection status",
787
+ "diagnostics.historyCount": "{count} past failures",
788
+ "diagnostics.kind.upgrade": "Connection request rejected",
789
+ "diagnostics.kind.pair": "Pairing request rejected",
790
+ "diagnostics.kind.auth": "Device authentication failed",
791
+ "diagnostics.kind.opened": "Connection opened",
792
+ "diagnostics.kind.closed": "Connection closed unexpectedly",
793
+ "diagnostics.kind.error": "Connection error",
794
+ "diagnostics.kind.identity": "Device identity error",
795
+ "diagnostics.kind.storage": "Saving device information failed",
796
+ "diagnostics.kind.unknown": "Connection problem",
797
+ "push.outcome.sent": "Sent",
798
+ "push.outcome.failed": "Failed to send",
799
+ "push.outcome.invalid": "Invalid push credentials",
800
+ "push.environment.development": "Development",
801
+ "push.environment.production": "Production",
802
+ "push.environment.other": "Other environment ({environment})",
803
+ "push.outcome.other": "Push status ({outcome})",
804
+ "push.resultDetail": "Push result ({message})",
748
805
  "nav": "DeepPilot",
749
806
  "meta.title": "DeepPilot",
750
- "meta.intro": "Connect your iPhone to DeepSeek Harness (DSH) on this computer (protocol v2).",
807
+ "meta.intro": "Use this computer’s DSH from your phone.",
751
808
  "meta.refresh": "Refresh",
752
- "master.title": "DeepPilot connection",
809
+ "master.title": "Allow phone connections",
753
810
  "master.loading": "Loading settings…",
754
811
  "master.on": "On: accepting phone connections.",
755
812
  "master.off": "Off: not accepting phone connections.",
@@ -762,11 +819,11 @@ window.__ModuleLoader__.load({
762
819
  "connection.optionsSummary": "Connection options",
763
820
  "pair.description": "Choose a connection type and generate one-time pairing information.",
764
821
  "devices.description": "Manage iPhones that are already paired.",
765
- "troubleshooting.summary": "Tests and troubleshooting",
822
+ "troubleshooting.summary": "Diagnostic tools",
766
823
  "troubleshooting.title": "Diagnostic tools",
767
824
  "troubleshooting.description": "Use these only when diagnosing connection or push issues.",
768
825
  "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.",
826
+ "troubleshooting.debugDescription": "New phone connections write diagnostic logs to the DSH terminal. Connected phones must reconnect to pick up the change. Logs exclude pairing codes, push credentials, and message content.",
770
827
  "diagnostics.title": "Connection diagnostics",
771
828
  "diagnostics.description": "Recent connection failures; copy them for troubleshooting.",
772
829
  "diagnostics.recent": "Recent failures",
@@ -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.",
@@ -1061,170 +1118,149 @@ window.__ModuleLoader__.load({
1061
1118
  });
1062
1119
  }
1063
1120
  // 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");
1121
+ const CSS = String.raw`
1122
+ .pbb-section{--pbb-ink:var(--dsw-alias-label-primary,#0f1115);--pbb-secondary:var(--dsw-alias-label-secondary,#61666b);--pbb-line:var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l3,#e4e5e7));--pbb-fill:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2,#fff));--pbb-soft:var(--dsw-alias-bg-module-platform,#f5f6f8);container:pbb/inline-size;max-width:760px;color:var(--pbb-ink);font-size:14px;line-height:20px;display:flex;flex-direction:column;gap:20px}
1123
+ .pbb-section *{box-sizing:border-box}
1124
+ .pbb-section :is(.pbb-action,.pbb-refresh,.pbb-iconButton,.pbb-cardHead,.pbb-menuAction,.pbb-targetButton),.pbb-section input{font-family:inherit}
1125
+ .pbb-icon{display:block;flex:none;color:inherit}
1126
+ .pbb-pageHeader{display:flex;align-items:flex-start;gap:12px;min-height:56px}
1127
+ .pbb-pageHeader>div{min-width:0;flex:1;padding-top:2px}
1128
+ .pbb-title{margin:0;font-size:20px;line-height:27px;font-weight:600;letter-spacing:-.3px}
1129
+ .pbb-intro{color:var(--pbb-secondary);font-size:14px;line-height:20px;margin:5px 0 0}
1130
+ .pbb-refresh{display:inline-flex;align-items:center;justify-content:center;gap:5px;height:28px;padding:0 7px;flex:none;border:0;border-radius:6px;background:transparent;color:var(--pbb-secondary);font-size:12px;cursor:pointer}
1131
+ .pbb-card{border:1px solid var(--pbb-line);border-radius:18px;background:var(--pbb-fill)}
1132
+ .pbb-cardTitle{margin:0;font-size:14px;line-height:20px;font-weight:600;color:var(--pbb-ink)}
1133
+ .pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 16px 16px}
1134
+ .pbb-connections>.pbb-cardBody{padding:0;gap:0}
1135
+ .pbb-row{display:flex;align-items:center;gap:8px;min-width:0}
1136
+ .pbb-masterRow{display:flex;align-items:center;gap:12px;min-height:66px;padding:11px 16px}
1137
+ .pbb-controlText{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}
1138
+ .pbb-masterRow>.pbb-controlText{gap:4px}
1139
+ .pbb-controlTitle{font-size:14px;line-height:20px;font-weight:400;color:var(--pbb-ink)}
1140
+ .pbb-masterRow .pbb-controlTitle{font-weight:600}
1141
+ .pbb-controlDescription,.pbb-diag,.pbb-label,.pbb-value{font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1142
+ .pbb-serviceSummary,.pbb-channelState{display:flex;align-items:center;flex-wrap:wrap;gap:6px;font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1143
+ .pbb-serviceSummary{gap:5px}
1144
+ .pbb-serviceState{display:inline-flex}
1145
+ .pbb-metaDivider{height:10px;border-left:1px solid var(--pbb-line);margin:0 2px}
1146
+ .pbb-channel{position:relative}
1147
+ .pbb-channel::before{content:"";position:absolute;top:0;left:16px;right:16px;border-top:1px solid var(--pbb-line)}
1148
+ .pbb-channel+.pbb-channel::before{left:46px}
1149
+ .pbb-channelRow{gap:12px;min-height:60px;padding:8px 16px}
1150
+ .pbb-channelRow>.pbb-icon{color:var(--pbb-secondary)}
1151
+ .pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 16px 14px 46px}
1152
+ .pbb-channel>.pbb-diag,.pbb-channel>.pbb-rowAction{margin:0;padding:0 16px 8px 46px}
1153
+ .pbb-fold[hidden]{display:none}
1154
+ .pbb-action:disabled,.pbb-refresh:disabled,.pbb-menuAction:disabled,.pbb-targetButton:disabled{opacity:.5;cursor:default}
1155
+ .pbb-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;flex:none;height:28px;max-width:100%;padding:0 9px;border:1px solid var(--pbb-line);border-radius:6px;background:transparent;color:var(--pbb-secondary);font:inherit;font-size:14px;line-height:20px;text-decoration:none;cursor:pointer;white-space:nowrap}
1156
+ .pbb-addAction{display:flex;margin-left:auto}
1157
+ .pbb-buttonPrimary{background:var(--dsw-alias-button-primary-fill,var(--pbb-ink));border-color:transparent;color:var(--dsw-alias-label-primary-foreground,#fff)}
1158
+ .pbb-actionDangerGhost{color:var(--dsw-alias-state-error-primary,#bc2222);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary,#bc2222) 35%,transparent)}
1159
+ .pbb-iconButton{display:flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--pbb-secondary);cursor:pointer}
1160
+ .pbb-chev{display:inline-flex;flex:none;color:var(--pbb-secondary)}
1161
+ .pbb-chevIcon{width:14px;height:14px;display:block}
1162
+ [aria-expanded="true"]>.pbb-chev,.pbb-helpDisclosure[open]>summary>.pbb-chev,.pbb-disclosure[open]>summary>.pbb-chev{transform:rotate(90deg)}
1163
+ .pbb-diag{margin:0;overflow-wrap:anywhere}
1164
+ .pbb-diagBad,.pbb-bad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1165
+ .pbb-ok{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#287a45) 65%,var(--pbb-ink))}
1166
+ .pbb-label{flex:1;min-width:0;font-weight:500;color:var(--pbb-ink)}
1167
+ .pbb-value{text-align:right;overflow-wrap:anywhere}
1168
+ .pbb-token{max-width:100%;font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--pbb-secondary);overflow-wrap:anywhere}
1169
+ .pbb-empty{margin:0;padding:16px;font-size:14px;line-height:20px;color:var(--pbb-secondary)}
1170
+ .pbb-limitRow{display:flex;align-items:center;gap:12px}
1171
+ .pbb-limitControl{display:flex;align-items:center;gap:8px;flex:none}
1172
+ .pbb-numberInput,.pbb-deviceNameInput{min-width:0;height:28px;border:1px solid var(--pbb-line);border-radius:6px;background:var(--dsw-alias-bg-layer-3,var(--pbb-fill));color:var(--pbb-ink);font:inherit;font-size:14px;line-height:20px;padding:3px 8px;caret-color:var(--pbb-ink)}
1173
+ .pbb-numberInput{width:72px}
1174
+ .pbb-numberInput:disabled,.pbb-deviceNameInput:disabled{opacity:.5}
1175
+ .pbb-endpoint{user-select:all}
1176
+ .pbb-devicesSection{display:flex;flex-direction:column;gap:8px}
1177
+ .pbb-devicesHeader{display:flex;align-items:center;gap:8px;min-height:44px;padding:0 2px}
1178
+ .pbb-deviceCount{font-size:12px;line-height:18px;color:var(--pbb-secondary);white-space:nowrap}
1179
+ .pbb-devicesCard{overflow:visible}
1180
+ .pbb-deviceList{display:flex;flex-direction:column}
1181
+ .pbb-deviceRow{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:12px;row-gap:0;min-height:56px;padding:6px 14px 6px 16px}
1182
+ .pbb-deviceRow+.pbb-deviceRow::before{content:"";position:absolute;top:0;left:46px;right:16px;border-top:1px solid var(--pbb-line)}
1183
+ .pbb-deviceRow:has(details[open]){z-index:2}
1184
+ .pbb-deviceMain{display:flex;align-items:center;gap:12px;min-width:0}
1185
+ .pbb-deviceMain>.pbb-icon{color:var(--pbb-secondary)}
1186
+ .pbb-deviceText{display:flex;flex-direction:column;gap:2px;min-width:0}
1187
+ .pbb-deviceName{font-size:14px;line-height:20px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1188
+ .pbb-deviceMeta{display:flex;flex-wrap:wrap;gap:0 6px;color:var(--pbb-secondary);font-size:12px;line-height:18px}
1189
+ .pbb-deviceMeta>*+*::before{content:"\00B7";margin-right:6px}
1190
+ .pbb-deviceActions{grid-row:1;grid-column:2;display:flex;align-items:center}
1191
+ .pbb-deviceRenameForm,.pbb-deviceConfirm{grid-column:1/-1;display:flex;gap:8px;padding:8px 0;min-width:0}
1192
+ .pbb-deviceRenameForm{flex-wrap:wrap}
1193
+ .pbb-deviceNameInput{flex:1}
1194
+ .pbb-deviceRenameActions{display:flex;align-items:center;gap:5px;flex:none}
1195
+ .pbb-deviceConfirm{flex-direction:column}
1196
+ .pbb-deviceMessage{padding:10px 16px}
1197
+ .pbb-deviceMenu{position:relative}
1198
+ .pbb-deviceMenu>summary{list-style:none}
1199
+ .pbb-deviceMenu>summary::-webkit-details-marker{display:none}
1200
+ .pbb-deviceMenuBody{position:absolute;z-index:3;top:34px;right:0;width:250px;max-width:calc(100cqw - 32px);padding:5px;border:1px solid var(--pbb-line);border-radius:10px;background:var(--dsw-alias-bg-layer-3,var(--pbb-fill));box-shadow:0 8px 24px color-mix(in srgb,var(--pbb-ink) 12%,transparent)}
1201
+ .pbb-deviceMenuInfo{display:flex;flex-direction:column;gap:3px;padding:7px 8px 10px;border-bottom:1px solid var(--pbb-line)}
1202
+ .pbb-menuAction{display:block;width:100%;min-height:32px;padding:5px 8px;border:0;border-radius:5px;background:transparent;color:var(--pbb-ink);text-align:left;font:inherit;font-size:14px;cursor:pointer}
1203
+ .pbb-menuDanger{color:var(--dsw-alias-state-error-primary,#bc2222)}
1204
+ .pbb-actionBusy{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--pbb-secondary)}
1205
+ .pbb-actionBusy::before{content:"";width:10px;height:10px;border:1.5px solid var(--pbb-line);border-top-color:currentColor;border-radius:50%;animation:pbb-spin .7s linear infinite}
1206
+ @keyframes pbb-spin{to{transform:rotate(360deg)}}
1207
+ .pbb-rowAction{display:flex;flex-wrap:wrap;gap:8px}
1208
+ .pbb-pairingPanel{border:1px solid var(--pbb-line);border-radius:16px;padding:16px;background:var(--pbb-fill)}
1209
+ .pbb-pairingHeader{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}
1210
+ .pbb-pairingBody{display:flex;flex-direction:column;gap:10px}
1211
+ .pbb-targetList{display:flex;flex-wrap:wrap;gap:4px;min-width:0}
1212
+ .pbb-targetButton{max-width:100%;padding:5px 9px;border:1px solid transparent;border-radius:7px;background:var(--pbb-soft);color:var(--pbb-secondary);font:inherit;font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}
1213
+ .pbb-targetSelected{color:var(--pbb-ink);border-color:var(--pbb-line);background:var(--pbb-fill)}
1214
+ .pbb-qrPanel{display:grid;grid-template-columns:224px minmax(0,1fr);gap:20px;align-items:start}
1215
+ .pbb-qrImage{display:block;width:224px;height:224px;max-width:100%;padding:8px;border-radius:12px;background:#fff}
1216
+ .pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}
1217
+ .pbb-countdownBar{height:4px;border-radius:99px;background:var(--pbb-soft);overflow:hidden}
1218
+ .pbb-countdownBar i{display:block;width:100%;height:100%;transform-origin:left center;background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:99px}
1219
+ .pbb-countdownText{font-size:12px;line-height:18px;color:var(--pbb-secondary);font-variant-numeric:tabular-nums}
1220
+ .pbb-pairInfo,.pbb-pairCodeBlock{display:flex;flex-direction:column;gap:8px;min-width:0}
1221
+ .pbb-pairCodeLabel,.pbb-qrHint{font-size:12px;line-height:18px;color:var(--pbb-secondary);margin:0}
1222
+ .pbb-pairCodeRow{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
1223
+ .pbb-pairCode{font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere;user-select:all}
1224
+ .pbb-tools{border-radius:16px;overflow:hidden}
1225
+ .pbb-toolSection+.pbb-toolSection{border-top:1px solid var(--pbb-line)}
1226
+ .pbb-cardHead{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:0 16px;border:0;background:transparent;color:var(--pbb-secondary);font:inherit;text-align:left;cursor:pointer}
1227
+ .pbb-cardHead>.pbb-cardTitle{flex:1;font-weight:400}
1228
+ .pbb-toolMeta{max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1229
+ .pbb-toolMeta.pbb-ok{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#287a45) 65%,var(--pbb-ink))}
1230
+ .pbb-toolMeta.pbb-bad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1231
+ .pbb-testGrid{display:flex;flex-direction:column;gap:0}
1232
+ .pbb-testItem{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--pbb-line)}
1233
+ .pbb-testTitle{font-size:14px;line-height:20px;font-weight:500;color:var(--pbb-ink)}
1234
+ .pbb-testDescription{font-size:12px;line-height:18px;color:var(--pbb-secondary)}
1235
+ .pbb-helpBody,.pbb-disclosureBody{display:flex;flex-direction:column;gap:14px}
1236
+ .pbb-helpSection{display:flex;flex-direction:column;gap:6px}
1237
+ .pbb-helpHeading{font-size:14px;line-height:20px;font-weight:500;color:var(--pbb-ink)}
1238
+ .pbb-helpList{margin:0;padding-left:20px;font-size:14px;line-height:22px;color:var(--pbb-secondary)}
1239
+ .pbb-helpList li+li{margin-top:4px}
1240
+ .pbb-helpText{margin:0;font-size:14px;line-height:22px;color:var(--pbb-secondary)}
1241
+ .pbb-helpCode{display:block;margin:0;padding:10px;border:1px solid var(--pbb-line);border-radius:8px;background:var(--pbb-soft);color:var(--pbb-ink);font:12px/18px ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre-wrap;overflow-wrap:anywhere}
1242
+ .pbb-helpLink{color:var(--pbb-ink);text-decoration:underline;text-underline-offset:3px}
1243
+ .pbb-helpDisclosure>summary{display:flex;align-items:center;gap:6px;min-height:28px;list-style:none;cursor:pointer;color:var(--pbb-secondary);font-size:12px}
1244
+ .pbb-helpDisclosure>summary::-webkit-details-marker{display:none}
1245
+ .pbb-helpDisclosure[open]>.pbb-helpBody{padding-top:10px}
1246
+ .pbb-runtimeList{display:flex;flex-direction:column}
1247
+ .pbb-runtimeRow{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--pbb-line)}
1248
+ .pbb-runtimeRow>.pbb-row{flex:1;min-width:0}
1249
+ .pbb-updateRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;color:var(--pbb-ink);font-size:14px;text-decoration:none}
1250
+ .pbb-resultRow{display:flex;align-items:flex-start;gap:6px}
1251
+ .pbb-resultIcon{display:inline-flex;flex:none;margin-top:3px;color:var(--dsw-alias-state-success-primary,#287a45)}
1252
+ .pbb-resultIconBad{color:var(--dsw-alias-state-error-primary,#bc2222)}
1253
+ .pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;min-height:44px;padding:0 16px;cursor:pointer;font-size:14px}
1254
+ .pbb-disclosure>summary::-webkit-details-marker{display:none}
1255
+ .pbb-disclosureBody{padding:2px 16px 16px}
1256
+ .pbb-disclosureTitle{flex:1}
1257
+ .pbb-section :is(.pbb-action,.pbb-refresh,.pbb-iconButton,.pbb-cardHead,.pbb-menuAction,.pbb-targetButton,input,summary,a):focus-visible{outline:2px solid var(--dsw-focus-ring-color,#5276b4);outline-offset:2px}
1258
+ @media (hover:hover) and (pointer:fine){.pbb-refresh:hover:not(:disabled),.pbb-iconButton:hover,.pbb-action:hover:not(:disabled),.pbb-cardHead:hover,.pbb-menuAction:hover:not(:disabled){background:var(--pbb-soft)}.pbb-buttonPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#30343b)}.pbb-action:active:not(:disabled),.pbb-refresh:active:not(:disabled),.pbb-iconButton:active{transform:scale(.97)}}
1259
+ @media (prefers-reduced-motion:reduce){.pbb-action,.pbb-refresh,.pbb-iconButton{transition:none;transform:none!important}.pbb-actionBusy::before{animation:none}}
1260
+ @container pbb (max-width:480px){.pbb-qrPanel{grid-template-columns:1fr}.pbb-limitRow{align-items:flex-start;flex-direction:column}.pbb-limitControl{align-self:flex-end}.pbb-deviceMeta{column-gap:4px}.pbb-toolMeta{max-width:42%}.pbb-channelRow{gap:8px}.pbb-testItem{flex-wrap:wrap}.pbb-testItem>.pbb-controlText{flex-basis:65%}}
1261
+ @container pbb (max-width:340px){.pbb-devicesHeader{flex-wrap:wrap}.pbb-deviceRow{column-gap:6px;padding-right:10px}.pbb-channelBody{padding-left:16px}.pbb-toolMeta{max-width:38%}}
1262
+ @media (pointer:coarse){.pbb-action,.pbb-iconButton,.pbb-refresh,.pbb-targetButton,.pbb-menuAction,.pbb-helpDisclosure>summary{min-width:44px;min-height:44px}.pbb-numberInput,.pbb-deviceNameInput{height:44px;font-size:16px}.pbb-deviceMenuBody{top:48px}}
1263
+ `;
1228
1264
  /**
1229
1265
  * Client bundle id, identical to the `window.__ModuleLoader__.load({ id })`
1230
1266
  * identity emitted by tsdown.config.ts. DSH's module system owns style
@@ -1252,6 +1288,102 @@ window.__ModuleLoader__.load({
1252
1288
  tag.textContent = CSS;
1253
1289
  document.head.appendChild(tag);
1254
1290
  }
1291
+ // src/client/settings-icons.ts
1292
+ /** 设置页的小图标共用线宽与 currentColor,随宿主主题变色,不额外引入图标包。 */
1293
+ function settingsIcon(name, size = 18) {
1294
+ const path = (d) => (0, react.createElement)("path", { d });
1295
+ const circle = (cx, cy, r) => (0, react.createElement)("circle", {
1296
+ cx,
1297
+ cy,
1298
+ r
1299
+ });
1300
+ return (0, react.createElement)("svg", {
1301
+ className: "pbb-icon",
1302
+ viewBox: "0 0 24 24",
1303
+ width: size,
1304
+ height: size,
1305
+ fill: "none",
1306
+ stroke: "currentColor",
1307
+ strokeWidth: 1.6,
1308
+ strokeLinecap: "round",
1309
+ strokeLinejoin: "round",
1310
+ "aria-hidden": true
1311
+ }, ...{
1312
+ phone: () => [(0, react.createElement)("rect", {
1313
+ x: 6.5,
1314
+ y: 2.5,
1315
+ width: 11,
1316
+ height: 19,
1317
+ rx: 2.2
1318
+ }), path("M10 5h4M11 18.5h2")],
1319
+ wifi: () => [path("M2 8.8a16 16 0 0 1 20 0M5 12.1a11 11 0 0 1 14 0M8.3 15.5a6 6 0 0 1 7.4 0"), circle(12, 19, .7)],
1320
+ globe: () => [circle(12, 12, 9), path("M3 12h18M12 3a17 17 0 0 1 0 18 17 17 0 0 1 0-18")],
1321
+ refresh: () => [path("M20 7v5h-5M4 17v-5h5M6.1 7a7 7 0 0 1 11.5-1L20 9M4 15l2.4 3A7 7 0 0 0 18 17")],
1322
+ plus: () => [path("M12 5v14M5 12h14")],
1323
+ more: () => [
1324
+ circle(5, 12, 1.2),
1325
+ circle(12, 12, 1.2),
1326
+ circle(19, 12, 1.2)
1327
+ ],
1328
+ activity: () => [path("M3 12h4l3-7 4 14 3-7h4")],
1329
+ info: () => [circle(12, 12, 9), path("M12 11v6M12 7h.01")],
1330
+ ready: () => [circle(12, 12, 9), path("m8 12 3 3 5-6")],
1331
+ attention: () => [circle(12, 12, 9), path("M12 7v6M12 17h.01")]
1332
+ }[name]());
1333
+ }
1334
+ // src/client/device-actions.ts
1335
+ /**
1336
+ * 原生 disclosure(折叠控件)负责 Enter/Space 与焦点顺序;操作仍是普通按钮,
1337
+ * 不冒充需要方向键导航的 ARIA menu。详情只在展开时进入无障碍读取顺序。
1338
+ */
1339
+ function DeviceActions(props) {
1340
+ const ref = (0, react.useRef)(null);
1341
+ const close = (restoreFocus = false) => {
1342
+ const details = ref.current;
1343
+ if (details === null) return;
1344
+ details.open = false;
1345
+ if (restoreFocus) details.querySelector("summary")?.focus();
1346
+ };
1347
+ (0, react.useEffect)(() => {
1348
+ const outside = (event) => {
1349
+ if (event.target instanceof Node && !ref.current?.contains(event.target)) close();
1350
+ };
1351
+ document.addEventListener("pointerdown", outside);
1352
+ return () => document.removeEventListener("pointerdown", outside);
1353
+ }, []);
1354
+ const act = (action) => {
1355
+ close();
1356
+ action();
1357
+ };
1358
+ return (0, react.createElement)("details", {
1359
+ ref,
1360
+ className: "pbb-deviceMenu",
1361
+ onKeyDown: (event) => {
1362
+ if (event.key !== "Escape" || !ref.current?.open) return;
1363
+ event.preventDefault();
1364
+ event.stopPropagation();
1365
+ close(true);
1366
+ }
1367
+ }, (0, react.createElement)("summary", {
1368
+ className: "pbb-iconButton",
1369
+ "aria-label": props.t("devices.moreAria", { name: props.name })
1370
+ }, settingsIcon("more", 16)), (0, react.createElement)("div", {
1371
+ className: "pbb-deviceMenuBody",
1372
+ "aria-label": props.t("devices.col.actions")
1373
+ }, (0, react.createElement)("div", { className: "pbb-deviceMenuInfo" }, props.customName ? (0, react.createElement)("span", { className: "pbb-diag" }, props.t("devices.customBadge")) : null, (0, react.createElement)("span", { className: "pbb-diag" }, props.t("devices.col.fingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, props.fingerprint || "—")), (0, react.createElement)("button", {
1374
+ type: "button",
1375
+ className: "pbb-menuAction",
1376
+ disabled: !props.canRename,
1377
+ "aria-label": props.t("devices.renameAria", { name: props.name }),
1378
+ onClick: () => act(props.onRename)
1379
+ }, props.t("devices.rename")), (0, react.createElement)("button", {
1380
+ type: "button",
1381
+ className: "pbb-menuAction pbb-menuDanger",
1382
+ disabled: !props.canRevoke,
1383
+ "aria-label": props.t("devices.revokeAria", { name: props.name }),
1384
+ onClick: () => act(props.onRevoke)
1385
+ }, props.t("devices.revoke"))));
1386
+ }
1255
1387
  // node_modules/qrcode/lib/can-promise.js
1256
1388
  var require_can_promise = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1257
1389
  module.exports = function() {
@@ -3911,15 +4043,6 @@ window.__ModuleLoader__.load({
3911
4043
  };
3912
4044
  }
3913
4045
  // 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
4046
  const REMOTE_PHASE_META = {
3924
4047
  disabled: {
3925
4048
  dot: "",
@@ -4055,24 +4178,9 @@ window.__ModuleLoader__.load({
4055
4178
  return (device.customName ?? device.deviceName).trim();
4056
4179
  }
4057
4180
  // 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
- /** 状态条用的短状态:列标签已经写明是哪条通道,值只说状态,免得在窄格里被截断。 */
4181
+ /** 中文结论由调用处提供,原始上游信息统一放入括号,保留排查依据。 */
4182
+ const errorDetails = (error) => "(" + (error instanceof Error ? error.message : String(error)) + ")";
4183
+ /** 通道标题旁的短状态;完整端点和参数放入对应的连接选项。 */
4076
4184
  const CHANNEL_STATE_KEY = {
4077
4185
  disabled: "channel.state.disabled",
4078
4186
  starting: "channel.state.starting",
@@ -4089,21 +4197,6 @@ window.__ModuleLoader__.load({
4089
4197
  attention: "status.attention",
4090
4198
  ready: "status.ready"
4091
4199
  };
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
4200
  /** Chevron icon (linear, currentColor). CSS rotates it 90° while its owner is
4108
4201
  * expanded, so collapsed always means "points right, more content behind". */
4109
4202
  const ChevronIcon = () => (0, react.createElement)("svg", {
@@ -4118,8 +4211,7 @@ window.__ModuleLoader__.load({
4118
4211
  strokeLinejoin: "round",
4119
4212
  "aria-hidden": true
4120
4213
  }, (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. */
4214
+ /** 诊断结果使用线性 SVG,与页面其余图标保持一致。 */
4123
4215
  const CheckIcon = () => (0, react.createElement)("svg", {
4124
4216
  viewBox: "0 0 16 16",
4125
4217
  width: 12,
@@ -4186,6 +4278,13 @@ window.__ModuleLoader__.load({
4186
4278
  const [remoteLimitDraft, dispatchRemoteLimitDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, REMOTE_LIMIT_RANGE), initialNumericDraft(REMOTE_LIMIT_RANGE));
4187
4279
  const [localPortDraft, dispatchLocalPortDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, LOCAL_PORT_RANGE), initialNumericDraft(LOCAL_PORT_RANGE));
4188
4280
  const [selectedPairingHost, setSelectedPairingHost] = (0, react.useState)(null);
4281
+ const [pairingOpen, setPairingOpen] = (0, react.useState)(false);
4282
+ const pageId = (0, react.useId)();
4283
+ const localOptionsId = pageId + "-local";
4284
+ const remoteOptionsId = pageId + "-remote";
4285
+ const troubleshootingId = pageId + "-troubleshooting";
4286
+ const systemId = pageId + "-system";
4287
+ const pairingId = pageId + "-pairing";
4189
4288
  const [pairingPanel, dispatchPairingPanel] = (0, react.useReducer)(pairingPanelReduce, initialPairingPanelState);
4190
4289
  const qrRequestId = (0, react.useRef)(0);
4191
4290
  const [troubleshootingOpen, setTroubleshootingOpen] = (0, react.useState)(false);
@@ -4196,7 +4295,6 @@ window.__ModuleLoader__.load({
4196
4295
  const [pushTestResult, setPushTestResult] = (0, react.useState)(null);
4197
4296
  const [pushTestError, setPushTestError] = (0, react.useState)("");
4198
4297
  const [deviceBusy, setDeviceBusy] = (0, react.useState)(null);
4199
- const [deviceBusyAction, setDeviceBusyAction] = (0, react.useState)(null);
4200
4298
  const [deviceMessage, setDeviceMessage] = (0, react.useState)("");
4201
4299
  const [editingDeviceId, setEditingDeviceId] = (0, react.useState)(null);
4202
4300
  const [deviceNameDraft, setDeviceNameDraft] = (0, react.useState)("");
@@ -4230,7 +4328,7 @@ window.__ModuleLoader__.load({
4230
4328
  setPushTestResult(result);
4231
4329
  setPushTestBusy(false);
4232
4330
  }, (error) => {
4233
- setPushTestError(error instanceof Error ? error.message : String(error));
4331
+ setPushTestError(translateWith(props.t, "push.pushFailed") + errorDetails(error));
4234
4332
  setPushTestBusy(false);
4235
4333
  });
4236
4334
  };
@@ -4247,7 +4345,7 @@ window.__ModuleLoader__.load({
4247
4345
  setRelayTestResult(result);
4248
4346
  setRelayTestBusy(false);
4249
4347
  }, (error) => {
4250
- setRelayTestError(error instanceof Error ? error.message : String(error));
4348
+ setRelayTestError(translateWith(props.t, "push.relayBad") + errorDetails(error));
4251
4349
  setRelayTestBusy(false);
4252
4350
  });
4253
4351
  };
@@ -4283,7 +4381,7 @@ window.__ModuleLoader__.load({
4283
4381
  const reportState = props.useDeepPilotReport((s) => s);
4284
4382
  hooks.report = reportState.report;
4285
4383
  if (reportState.status === "error" && reportState.message) {
4286
- diag.push(reportState.message);
4384
+ diag.push(translateWith(props.t, "diag.reportFailed") + errorDetails(reportState.message));
4287
4385
  failed = true;
4288
4386
  }
4289
4387
  const enabledState = props.useDeepPilotEnabled((s) => s);
@@ -4324,7 +4422,7 @@ window.__ModuleLoader__.load({
4324
4422
  try {
4325
4423
  readHooks();
4326
4424
  } catch (error) {
4327
- diag.push(translateWith(props.t, "diag.renderError") + (error instanceof Error ? error.message : String(error)));
4425
+ diag.push(translateWith(props.t, "diag.renderError") + errorDetails(error));
4328
4426
  failed = true;
4329
4427
  }
4330
4428
  const report = hooks.report;
@@ -4371,7 +4469,7 @@ window.__ModuleLoader__.load({
4371
4469
  }, (error) => {
4372
4470
  dispatchLocalPortDraft({
4373
4471
  type: "failed",
4374
- message: translateWith(props.t, "local.portFailed") + (error instanceof Error ? error.message : String(error))
4472
+ message: translateWith(props.t, "local.portFailed") + errorDetails(error)
4375
4473
  });
4376
4474
  });
4377
4475
  };
@@ -4392,7 +4490,7 @@ window.__ModuleLoader__.load({
4392
4490
  }, (error) => {
4393
4491
  dispatchRemoteLimitDraft({
4394
4492
  type: "failed",
4395
- message: translateWith(props.t, "remote.limitFailed") + (error instanceof Error ? error.message : String(error))
4493
+ message: translateWith(props.t, "remote.limitFailed") + errorDetails(error)
4396
4494
  });
4397
4495
  });
4398
4496
  };
@@ -4442,7 +4540,7 @@ window.__ModuleLoader__.load({
4442
4540
  dispatchPairingPanel({
4443
4541
  type: "failed",
4444
4542
  requestId,
4445
- message: translateWith(props.t, "pair.qrFailed") + (error instanceof Error ? error.message : String(error))
4543
+ message: translateWith(props.t, "pair.qrFailed") + errorDetails(error)
4446
4544
  });
4447
4545
  });
4448
4546
  };
@@ -4465,7 +4563,7 @@ window.__ModuleLoader__.load({
4465
4563
  }, (error) => {
4466
4564
  dispatchPairingPanel({
4467
4565
  type: "notice",
4468
- message: translateWith(props.t, "pair.infoCopyFailed") + (error instanceof Error ? error.message : String(error))
4566
+ message: translateWith(props.t, "pair.infoCopyFailed") + errorDetails(error)
4469
4567
  });
4470
4568
  });
4471
4569
  };
@@ -4486,15 +4584,13 @@ window.__ModuleLoader__.load({
4486
4584
  const revokeDevice = (deviceId, _name) => {
4487
4585
  if (!can("revokeDevice")) return;
4488
4586
  setDeviceBusy(deviceId);
4489
- setDeviceBusyAction("revoke");
4490
4587
  setDeviceMessage("");
4491
4588
  props.revokeDevice(deviceId).then(() => {
4492
4589
  setDeviceMessage(translateWith(props.t, "devices.revoked"));
4493
4590
  }, (error) => {
4494
- setDeviceMessage(translateWith(props.t, "devices.revokeFailed") + (error instanceof Error ? error.message : String(error)));
4591
+ setDeviceMessage(translateWith(props.t, "devices.revokeFailed") + errorDetails(error));
4495
4592
  }).finally(() => {
4496
4593
  setDeviceBusy(null);
4497
- setDeviceBusyAction(null);
4498
4594
  });
4499
4595
  };
4500
4596
  const deviceLabel$1 = (device) => deviceLabel(device) || translateWith(props.t, "devices.unnamed");
@@ -4517,17 +4613,15 @@ window.__ModuleLoader__.load({
4517
4613
  return;
4518
4614
  }
4519
4615
  setDeviceBusy(deviceId);
4520
- setDeviceBusyAction("rename");
4521
4616
  setDeviceMessage("");
4522
4617
  props.setDeviceName(deviceId, normalized === "" ? null : normalized).then(() => {
4523
4618
  setDeviceMessage(normalized === "" ? translateWith(props.t, "devices.nameCleared") : translateWith(props.t, "devices.renamed"));
4524
4619
  setEditingDeviceId(null);
4525
4620
  setDeviceNameDraft("");
4526
4621
  }, (error) => {
4527
- setDeviceMessage(translateWith(props.t, "devices.renameFailed") + (error instanceof Error ? error.message : String(error)));
4622
+ setDeviceMessage(translateWith(props.t, "devices.renameFailed") + errorDetails(error));
4528
4623
  }).finally(() => {
4529
4624
  setDeviceBusy(null);
4530
- setDeviceBusyAction(null);
4531
4625
  });
4532
4626
  };
4533
4627
  const visibleDevices$1 = visibleDevices(report);
@@ -4543,10 +4637,10 @@ window.__ModuleLoader__.load({
4543
4637
  "data-armed": armed || void 0,
4544
4638
  "aria-busy": busy,
4545
4639
  "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", {
4640
+ }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, settingsIcon("phone"), (0, react.createElement)("div", { className: "pbb-deviceText" }, (0, react.createElement)("span", {
4547
4641
  className: "pbb-deviceName",
4548
4642
  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", {
4643
+ }, label), (0, react.createElement)("div", { className: "pbb-deviceMeta" }, (0, react.createElement)("span", null, d.appVersion), (0, react.createElement)("span", null, translateWith(props.t, "devices.col.lastSeen") + " " + formatLastSeen(d.lastSeenTs))))), editing ? (0, react.createElement)("form", {
4550
4644
  className: "pbb-deviceRenameForm",
4551
4645
  onSubmit: (event) => {
4552
4646
  event.preventDefault();
@@ -4584,25 +4678,22 @@ window.__ModuleLoader__.load({
4584
4678
  title: translateWith(props.t, "devices.nameCancel"),
4585
4679
  "aria-label": translateWith(props.t, "devices.nameCancel"),
4586
4680
  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: () => {
4681
+ }, translateWith(props.t, "devices.nameCancel"))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : null, armed || editing ? null : (0, react.createElement)("div", { className: "pbb-deviceActions" }, busy ? (0, react.createElement)("span", {
4682
+ className: "pbb-actionBusy",
4683
+ role: "status"
4684
+ }, translateWith(props.t, "devices.deleting")) : (0, react.createElement)(DeviceActions, {
4685
+ name: label,
4686
+ fingerprint,
4687
+ customName: d.customName,
4688
+ canRename: editingDeviceId === null && can("setDeviceName"),
4689
+ canRevoke: can("revokeDevice"),
4690
+ t: props.t,
4691
+ onRename: () => beginDeviceRename(d),
4692
+ onRevoke: () => {
4602
4693
  cancelDeviceRename();
4603
4694
  setArmedRevokeDeviceId(d.deviceId);
4604
4695
  }
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", {
4696
+ })), armed && !busy ? (0, react.createElement)("div", { className: "pbb-deviceConfirm" }, (0, react.createElement)("p", {
4606
4697
  className: "pbb-diag pbb-diagBad",
4607
4698
  role: "alert"
4608
4699
  }, translateWith(props.t, "devices.revokeConfirm", { name: label })), (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
@@ -4620,27 +4711,32 @@ window.__ModuleLoader__.load({
4620
4711
  }, translateWith(props.t, "devices.nameCancel")))) : null);
4621
4712
  })) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
4622
4713
  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
4714
  const localMeta = report === null ? null : LOCAL_PHASE_META[report.local.phase];
4625
4715
  const remoteMeta = report === null ? null : REMOTE_PHASE_META[report.remote.phase];
4626
4716
  const service = serviceStatus({
4627
4717
  enabled,
4628
- switchReady,
4718
+ switchReady: switchReady && report !== null,
4629
4719
  localDot: localMeta?.dot,
4630
4720
  remoteDot: remoteMeta?.dot
4631
4721
  });
4632
4722
  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
4723
  const localStateText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[localMeta.label]);
4637
4724
  const remoteStateText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[remoteMeta.label]);
4638
4725
  /** 一条失败事件的单行文本:时间 · 类型 · 分因,附带可选排查字段。 */
4639
4726
  const diagnosticLine = (failure) => {
4640
4727
  const base = translateWith(props.t, "diagnostics.item", {
4641
4728
  time: new Date(failure.ts).toLocaleString(),
4642
- kind: failure.kind,
4643
- reason: failure.reason
4729
+ kind: translateWith(props.t, {
4730
+ "upgrade-rejected": "diagnostics.kind.upgrade",
4731
+ "pair-rejected": "diagnostics.kind.pair",
4732
+ "auth-failed": "diagnostics.kind.auth",
4733
+ "connection-opened": "diagnostics.kind.opened",
4734
+ "connection-closed": "diagnostics.kind.closed",
4735
+ "connection-error": "diagnostics.kind.error",
4736
+ identity: "diagnostics.kind.identity",
4737
+ storage: "diagnostics.kind.storage"
4738
+ }[failure.kind] ?? "diagnostics.kind.unknown"),
4739
+ reason: "(" + failure.kind + " / " + failure.reason + ")"
4644
4740
  });
4645
4741
  const extras = [];
4646
4742
  if (failure.attemptId !== void 0) extras.push(translateWith(props.t, "diagnostics.attempt", { id: failure.attemptId }));
@@ -4677,12 +4773,12 @@ window.__ModuleLoader__.load({
4677
4773
  });
4678
4774
  }, (error) => {
4679
4775
  setDiagnosticsCopy({
4680
- message: translateWith(props.t, "diagnostics.copyFailed") + (error instanceof Error ? error.message : String(error)),
4776
+ message: translateWith(props.t, "diagnostics.copyFailed") + errorDetails(error),
4681
4777
  failed: true
4682
4778
  });
4683
4779
  });
4684
4780
  };
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", {
4781
+ const diagnosticsSection = report === null ? null : (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "diagnostics.title")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "diagnostics.description"))), (0, react.createElement)("button", {
4686
4782
  type: "button",
4687
4783
  className: "pbb-action",
4688
4784
  onClick: copyDiagnostics,
@@ -4694,14 +4790,10 @@ window.__ModuleLoader__.load({
4694
4790
  className: "pbb-diag",
4695
4791
  key: String(index)
4696
4792
  }, 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));
4793
+ const fold = (open, body) => (0, react.createElement)("div", {
4794
+ className: "pbb-fold",
4795
+ hidden: !open
4796
+ }, body);
4705
4797
  /** 配对倒计时:仅在 QR 面板打开且 grant 在场时每秒刷新一次。 */
4706
4798
  (0, react.useEffect)(() => {
4707
4799
  if (!panel.open || pairingPanel.grant === null) return;
@@ -4711,44 +4803,19 @@ window.__ModuleLoader__.load({
4711
4803
  const grantRemainingMs = pairingPanel.grant === null ? 0 : Math.max(0, pairingPanel.grant.expiresAt - nowMs);
4712
4804
  const grantRemainingText = String(Math.floor(grantRemainingMs / 6e4)) + ":" + String(Math.floor(grantRemainingMs / 1e3) % 60).padStart(2, "0");
4713
4805
  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
4806
  const recentFailures = report?.diagnostics?.recentFailures ?? [];
4733
4807
  const troubleGood = relayTestResult !== null ? relayTestResult.overall === "ok" : pushTestResult !== null ? pushTestResult.overall === "sent" : false;
4734
4808
  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", {
4737
- 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", {
4809
+ const troubleConclusion = relayTestResult !== null ? relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad") : pushTestResult !== null ? pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : translateWith(props.t, "push.pushFailed") : recentFailures.length > 0 ? translateWith(props.t, "diagnostics.historyCount", { count: recentFailures.length }) : "";
4810
+ const pairingSection = (0, react.createElement)("div", {
4811
+ id: pairingId,
4812
+ hidden: !pairingOpen
4813
+ }, !pairingOpen || report === null ? null : (0, react.createElement)("div", { className: "pbb-pairingPanel" }, (0, react.createElement)("div", { className: "pbb-pairingHeader" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "pair.qrPanelTitle")), !panel.open && pairingTarget !== null ? (0, react.createElement)("button", {
4743
4814
  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", {
4815
+ className: "pbb-action",
4816
+ disabled: panel.busy || !report.pairingReady,
4817
+ onClick: () => showPairingQR(pairingTarget)
4818
+ }, translateWith(props.t, "pair.qrShow")) : null), (0, react.createElement)("div", { className: "pbb-pairingBody" }, pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag" }, enabled ? translateWith(props.t, "pair.noAddressHelp") : translateWith(props.t, "pair.disabledHelp")) : null, pairingTargets.length < 2 ? null : (0, react.createElement)("div", { className: "pbb-targetList" }, pairingTargets.map((target) => (0, react.createElement)("button", {
4752
4819
  type: "button",
4753
4820
  key: target.host,
4754
4821
  className: "pbb-targetButton" + (target.host === pairingTarget?.host ? " pbb-targetSelected" : ""),
@@ -4778,35 +4845,49 @@ window.__ModuleLoader__.load({
4778
4845
  className: "pbb-action pbb-buttonPrimary",
4779
4846
  onClick: copyPairingInfo,
4780
4847
  "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", {
4848
+ }, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") })))) : null)));
4849
+ return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
4782
4850
  type: "button",
4783
- role: "switch",
4784
- "aria-checked": enabled,
4785
- "aria-label": switchTitle,
4851
+ className: "pbb-refresh",
4852
+ disabled: !can("refresh"),
4853
+ onClick: () => props.refresh?.()
4854
+ }, settingsIcon("refresh", 14), translateWith(props.t, "meta.refresh"))), (0, react.createElement)("section", {
4855
+ className: "pbb-card pbb-connections",
4856
+ "aria-label": translateWith(props.t, "connection.title")
4857
+ }, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-masterRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, switchTitle), (0, react.createElement)("div", { className: "pbb-serviceSummary" }, (0, react.createElement)("span", { className: "pbb-serviceState" + (service.kind === "ready" ? " pbb-ok" : service.kind === "attention" ? " pbb-bad" : "") }, settingsIcon(service.kind === "ready" ? "ready" : service.kind === "attention" ? "attention" : "info", 13)), (0, react.createElement)("span", null, serviceStatusText), (0, react.createElement)("span", {
4858
+ className: "pbb-metaDivider",
4859
+ "aria-hidden": true
4860
+ }), (0, react.createElement)("span", null, translateWith(props.t, "panel.activeConnections") + " " + (report === null ? "—" : String(report.activeConnections))))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4861
+ checked: enabled,
4862
+ label: switchTitle,
4786
4863
  disabled: !switchReady,
4787
- className: "pbb-switch" + (enabled ? " pbb-switchOn" : ""),
4788
- onClick: () => {
4789
- if (can("setEnabled")) props.setDeepPilotEnabled(!enabled);
4864
+ onChange: (next) => {
4865
+ if (can("setEnabled")) props.setDeepPilotEnabled(next);
4790
4866
  }
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"),
4867
+ })), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row pbb-channelRow" }, settingsIcon("wifi"), (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, translateWith(props.t, "local.title")), (0, react.createElement)("div", { className: "pbb-channelState" }, (0, react.createElement)("span", { className: localMeta?.label === "online" ? "pbb-ok" : localMeta?.label === "error" ? "pbb-bad" : void 0 }, localStateText))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4868
+ checked: localEnabled,
4869
+ label: translateWith(props.t, "local.title"),
4796
4870
  disabled: !localSwitchReady,
4797
- className: "pbb-switch" + (localEnabled ? " pbb-switchOn" : ""),
4798
- onClick: () => {
4799
- if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
4871
+ onChange: (next) => {
4872
+ if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(next);
4800
4873
  }
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", {
4874
+ }), (0, react.createElement)("button", {
4802
4875
  type: "button",
4803
- className: "pbb-expand",
4876
+ className: "pbb-iconButton pbb-expand",
4877
+ "aria-label": translateWith(props.t, "local.options"),
4804
4878
  "aria-expanded": localOptionsOpen,
4879
+ "aria-controls": localOptionsId,
4805
4880
  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",
4881
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()))), report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", {
4882
+ className: "pbb-diag pbb-diagBad",
4883
+ role: "alert"
4884
+ }, translateWith(props.t, "local.errorDetails", { message: report.local.message })) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null, fold(localOptionsOpen, (0, react.createElement)("div", {
4885
+ className: "pbb-channelBody",
4886
+ id: localOptionsId
4887
+ }, report !== null && report.local.phase === "online" ? (0, react.createElement)("p", { className: "pbb-diag pbb-endpoint" }, report.local.endpoints.join(" · ")) : null, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("label", {
4888
+ className: "pbb-controlTitle",
4808
4889
  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", {
4890
+ }, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-controlDescription" }, translateWith(props.t, "local.portDescription")), !localPortView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.portInvalid")) : localPortDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (localPortDraft.failed ? " pbb-diagBad" : "") }, localPortDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
4810
4891
  id: "deeppilot-local-port",
4811
4892
  className: "pbb-numberInput",
4812
4893
  type: "number",
@@ -4832,30 +4913,38 @@ window.__ModuleLoader__.load({
4832
4913
  className: "pbb-action",
4833
4914
  disabled: !localPortReady || !localPortView.valid || localPortView.unchanged,
4834
4915
  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"),
4916
+ }, translateWith(props.t, "remote.limitApply")))), report !== null && report.local.phase === "online" && report.local.tlsFingerprint ? (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, translateWith(props.t, "local.tlsFingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, report.local.tlsFingerprint))) : null))), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row pbb-channelRow" }, settingsIcon("globe"), (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-controlTitle" }, translateWith(props.t, "remote.title")), (0, react.createElement)("div", { className: "pbb-channelState" }, (0, react.createElement)("span", { className: remoteMeta?.label === "online" ? "pbb-ok" : remoteMeta?.label === "error" || remoteMeta?.label === "unavailable" ? "pbb-bad" : void 0 }, remoteStateText), (0, react.createElement)("span", {
4917
+ className: "pbb-metaDivider",
4918
+ "aria-hidden": true
4919
+ }), (0, react.createElement)("span", null, "Tailscale Funnel"))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
4920
+ checked: remoteEnabled,
4921
+ label: translateWith(props.t, "remote.title"),
4840
4922
  disabled: !remoteSwitchReady,
4841
- className: "pbb-switch" + (remoteEnabled ? " pbb-switchOn" : ""),
4842
- onClick: () => {
4843
- if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
4923
+ onChange: (next) => {
4924
+ if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(next);
4844
4925
  }
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", {
4926
+ }), (0, react.createElement)("button", {
4927
+ type: "button",
4928
+ className: "pbb-iconButton pbb-expand",
4929
+ "aria-label": translateWith(props.t, "remote.options"),
4930
+ "aria-expanded": remoteOptionsOpen,
4931
+ "aria-controls": remoteOptionsId,
4932
+ onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4933
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()))), report !== null && report.remote.phase === "login_required" && typeof report.remote.authURL === "string" && report.remote.authURL.startsWith("https://") ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("a", {
4846
4934
  className: "pbb-action pbb-buttonPrimary",
4847
4935
  href: report.remote.authURL,
4848
4936
  target: "_blank",
4849
4937
  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",
4938
+ }, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", {
4939
+ className: "pbb-diag pbb-diagBad",
4940
+ role: "alert"
4941
+ }, translateWith(props.t, "remote.errorDetails", { message: report.remote.message })) : null, fold(remoteOptionsOpen, (0, react.createElement)("div", {
4942
+ className: "pbb-channelBody",
4943
+ id: remoteOptionsId
4944
+ }, report !== null && report.remote.phase === "online" ? (0, react.createElement)("p", { className: "pbb-diag pbb-endpoint" }, report.remote.publicURL) : null, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("label", {
4945
+ className: "pbb-controlTitle",
4857
4946
  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", {
4947
+ }, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-controlDescription" }, translateWith(props.t, "remote.limitDescription")), !remoteLimitView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "remote.limitInvalid")) : remoteLimitDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (remoteLimitDraft.failed ? " pbb-diagBad" : "") }, remoteLimitDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
4859
4948
  id: "deeppilot-funnel-source-limit",
4860
4949
  className: "pbb-numberInput",
4861
4950
  type: "number",
@@ -4881,56 +4970,83 @@ window.__ModuleLoader__.load({
4881
4970
  className: "pbb-action",
4882
4971
  disabled: !remoteConnectionLimitReady || !remoteLimitView.valid || remoteLimitView.unchanged,
4883
4972
  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", {
4973
+ }, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("details", { className: "pbb-helpDisclosure" }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), translateWith(props.t, "help.remoteTitle")), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.recommended")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.step1")), (0, react.createElement)("li", null, translateWith(props.t, "help.step2")), (0, react.createElement)("li", null, translateWith(props.t, "help.step3")), (0, react.createElement)("li", null, translateWith(props.t, "help.step4"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.funnelHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.httpsTitle")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep1")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep2")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep3"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.httpsHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.allowTitle")), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowBody")), (0, react.createElement)("code", { className: "pbb-helpCode" }, "\"nodeAttrs\": [\n {\n \"target\": [\"autogroup:member\"],\n \"attr\": [\"funnel\"],\n },\n],"), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.faqTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.faq1")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq2")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq3")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq4")))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.securityTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.security1")), (0, react.createElement)("li", null, translateWith(props.t, "help.security2")), (0, react.createElement)("li", null, translateWith(props.t, "help.security3")), (0, react.createElement)("li", null, translateWith(props.t, "help.security4")), (0, react.createElement)("li", null, translateWith(props.t, "help.security5"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.docsPrefix"), (0, react.createElement)("a", {
4885
4974
  className: "pbb-helpLink",
4886
4975
  href: "https://tailscale.com/docs/features/tailscale-funnel",
4887
4976
  target: "_blank",
4888
4977
  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" : ""),
4978
+ }, translateWith(props.t, "help.funnelDocs"))))))))))), (0, react.createElement)("section", {
4979
+ className: "pbb-devicesSection",
4980
+ "aria-label": translateWith(props.t, "devices.title")
4981
+ }, (0, react.createElement)("div", { className: "pbb-devicesHeader" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("span", { className: "pbb-deviceCount" }, translateWith(props.t, "devices.count", { count: visibleDevices$1.length })), (0, react.createElement)("div", { className: "pbb-addAction" }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4982
+ variant: "outline",
4983
+ size: "sm",
4984
+ icon: settingsIcon("plus", 16),
4985
+ disabled: panel.busy || report === null || !report.pairingReady || !can("beginPairing"),
4986
+ "aria-expanded": pairingOpen,
4987
+ "aria-controls": pairingId,
4988
+ onClick: () => {
4989
+ if (pairingOpen) {
4990
+ hidePairingQR();
4991
+ setPairingOpen(false);
4992
+ return;
4993
+ }
4994
+ setPairingOpen(true);
4995
+ if (pairingTarget !== null && report?.pairingReady) showPairingQR(pairingTarget);
4996
+ }
4997
+ }, translateWith(props.t, pairingOpen ? "pair.close" : "pair.add")))), pairingSection, (0, react.createElement)("div", { className: "pbb-card pbb-devicesCard" }, deviceMessage ? (0, react.createElement)("p", {
4998
+ className: "pbb-diag pbb-deviceMessage" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4891
4999
  role: "status"
4892
- }, deviceMessage) : null, deviceRows)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
5000
+ }, deviceMessage) : null, deviceRows)), (0, react.createElement)("section", {
5001
+ className: "pbb-card pbb-tools",
5002
+ "aria-label": translateWith(props.t, "tools.title")
5003
+ }, (0, react.createElement)("div", { className: "pbb-toolSection" }, (0, react.createElement)("button", {
4893
5004
  type: "button",
4894
5005
  className: "pbb-cardHead",
4895
5006
  "aria-expanded": troubleshootingOpen,
5007
+ "aria-controls": troubleshootingId,
4896
5008
  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", {
5009
+ }, settingsIcon("activity", 17), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "troubleshooting.summary")), (0, react.createElement)("span", { className: "pbb-toolMeta" + (troubleGood ? " pbb-ok" : troubleBad && relayTestResult !== null ? " pbb-bad" : "") }, troubleConclusion), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(troubleshootingOpen, (0, react.createElement)("div", {
5010
+ className: "pbb-cardBody",
5011
+ id: troubleshootingId
5012
+ }, (0, react.createElement)("div", { className: "pbb-testGrid" }, (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.relayTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.relayDefault"))), (0, react.createElement)("button", {
4898
5013
  type: "button",
4899
5014
  className: "pbb-action",
4900
5015
  disabled: relayTestBusy,
4901
5016
  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", {
5017
+ }, relayTestBusy ? translateWith(props.t, "push.relayTesting") : translateWith(props.t, "push.testRelay"))), (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.testPush")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.pushDefault"))), (0, react.createElement)("button", {
4903
5018
  type: "button",
4904
5019
  className: "pbb-action",
4905
5020
  disabled: pushTestBusy,
4906
5021
  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"),
5022
+ }, pushTestBusy ? translateWith(props.t, "push.pushSending") : translateWith(props.t, "push.testPush"))), (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-controlText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "troubleshooting.debugTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "troubleshooting.debugDescription"))), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
5023
+ checked: debugEnabled,
5024
+ label: translateWith(props.t, "troubleshooting.debugTitle"),
4912
5025
  disabled: !debugSwitchReady,
4913
- className: "pbb-switch" + (debugEnabled ? " pbb-switchOn" : ""),
4914
- onClick: () => {
4915
- if (can("setDebug")) props.setDeepPilotDebug(!debugEnabled);
5026
+ onChange: (next) => {
5027
+ if (can("setDebug")) props.setDeepPilotDebug(next);
4916
5028
  }
4917
5029
  }))), relayTestError ? (0, react.createElement)("p", {
4918
5030
  className: "pbb-diag pbb-diagBad",
4919
5031
  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", {
5032
+ }, relayTestError) : null, relayTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || translateWith(props.t, "push.relayUrlEmpty")), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad"))), relayTestResult.steps.map((step, index) => resultRow(step.ok, (step.id === "health" ? translateWith(props.t, "push.relayStep.health") : translateWith(props.t, "push.relayStep.enroll")) + (step.latencyMs !== void 0 ? ` (${String(step.latencyMs)}ms)` : "") + "(" + step.message + ")", String(index)))), pushTestError ? (0, react.createElement)("p", {
4921
5033
  className: "pbb-diag pbb-diagBad",
4922
5034
  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", {
5035
+ }, pushTestError) : null, pushTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token " + (pushTestResult.overall === "sent" ? "pbb-ok" : pushTestResult.overall === "failed" ? "pbb-bad" : "") }, pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : pushTestResult.overall === "failed" ? translateWith(props.t, "push.pushFailed") : pushTestResult.overall === "no-targets" ? translateWith(props.t, "push.pushNoTargets") : translateWith(props.t, "push.pushNotEnabled"))), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "push.resultDetail", { message: pushTestResult.message })) : null, pushTestResult.results.map((r, index) => resultRow(r.outcome === "sent", r.name + "(" + translateWith(props.t, r.environment === "production" ? "push.environment.production" : r.environment === "development" ? "push.environment.development" : "push.environment.other", { environment: r.environment }) + ") · " + translateWith(props.t, r.outcome === "sent" ? "push.outcome.sent" : r.outcome === "invalid-token" ? "push.outcome.invalid" : r.outcome === "failed" ? "push.outcome.failed" : "push.outcome.other", { outcome: r.outcome }) + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : ""), String(index)))), diagnosticsSection))), report === null ? null : (0, react.createElement)("div", { className: "pbb-toolSection" }, (0, react.createElement)("button", {
4924
5036
  type: "button",
4925
5037
  className: "pbb-cardHead",
4926
5038
  "aria-expanded": systemOpen,
5039
+ "aria-controls": systemId,
4927
5040
  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", {
5041
+ }, settingsIcon("info", 17), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-toolMeta" + (report.updateAvailable === true ? " pbb-ok" : "") }, "v" + report.pluginVersion + (report.updateAvailable === true ? " · " + translateWith(props.t, "update.badge") : "")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(systemOpen, (0, react.createElement)("div", {
5042
+ className: "pbb-cardBody",
5043
+ id: systemId
5044
+ }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.tailscaleVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.remote.tailscaleVersion ?? translateWith(props.t, "advanced.tailscaleVersionUnknown"))), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "panel.identity")), (0, react.createElement)("span", { className: "pbb-value" }, translateWith(props.t, report.pairingReady ? "panel.identityReady" : "panel.identityNotReady"))), advancedRows, report.updateAvailable === true ? (0, react.createElement)("a", {
4929
5045
  className: "pbb-updateRow",
4930
5046
  href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
4931
5047
  target: "_blank",
4932
5048
  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", {
5049
+ }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())) : null))))), diag.length > 0 ? (0, react.createElement)("details", {
4934
5050
  className: "pbb-card pbb-disclosure pbb-diagnostics",
4935
5051
  open: failed
4936
5052
  }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-disclosureTitle" }, translateWith(props.t, "diag.summary"))), (0, react.createElement)("div", { className: "pbb-disclosureBody" }, (0, react.createElement)("p", { className: "pbb-diag" + (failed ? " pbb-diagBad" : "") }, translateWith(props.t, "diag.prefix") + diag.join(" | ")))) : null);