dsh-deeppilot 0.9.9 → 0.9.13

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
@@ -46,6 +46,30 @@ window.__ModuleLoader__.load({
46
46
  };
47
47
  }
48
48
  //#endregion
49
+ //#region src/tailscale-version.ts
50
+ /**
51
+ * 内嵌 helper 上报的 tailscale.com 模块版本。
52
+ *
53
+ * 版本的唯一来源是 helper 启动事件(`helper/main.go` 读 Go 构建信息里的
54
+ * `tailscale.com` 依赖版本),不是 go.mod、也不是插件常量:用户可能通过
55
+ * `remote.helperPath` 换成自己编译的 helper,界面必须显示实际在跑的那一个。
56
+ *
57
+ * 两条边界都要守卫:
58
+ * - 宿主侧 `parseHelperEvent` 把 helper 的 stdout 当成不可信输入,格式不对
59
+ * 就丢弃,避免把任意文本渲染进设置页;
60
+ * - 设置页侧 `reportSchema` 同样校验 Host RPC 返回值,旧 Host 不带该字段
61
+ * 时保持 undefined(页面显示「未获取」),不当作错误。
62
+ *
63
+ * 接受 Go 模块版本形态:`v1.104.2`、`v1.104.2-pre.1`、
64
+ * `v0.0.0-20260101000000-abcdef123456`。长度上限防止异常输入撑爆界面。
65
+ */
66
+ const TAILSCALE_VERSION_PATTERN = /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]{1,64})?$/;
67
+ const MAX_TAILSCALE_VERSION_LENGTH = 80;
68
+ /** 判断外部输入是否是合法的 Tailscale 模块版本字符串。 */
69
+ function isTailscaleVersion(value) {
70
+ return typeof value === "string" && value.length <= MAX_TAILSCALE_VERSION_LENGTH && TAILSCALE_VERSION_PATTERN.test(value);
71
+ }
72
+ //#endregion
49
73
  //#region src/device-auth.ts
50
74
  const DEVICE_SCOPES = [
51
75
  "sessions.read",
@@ -144,15 +168,18 @@ window.__ModuleLoader__.load({
144
168
  const publicURL = s.publicURL;
145
169
  const authURL = s.authURL;
146
170
  const message = s.message;
171
+ const tailscaleVersion = s.tailscaleVersion;
147
172
  if (publicURL !== void 0 && typeof publicURL !== "string") reject("remote.publicURL");
148
173
  if (authURL !== void 0 && typeof authURL !== "string") reject("remote.authURL");
149
174
  if (message !== void 0 && typeof message !== "string") reject("remote.message");
175
+ if (tailscaleVersion !== void 0 && !isTailscaleVersion(tailscaleVersion)) reject("remote.tailscaleVersion");
150
176
  return {
151
177
  provider,
152
178
  phase,
153
179
  ...typeof publicURL === "string" ? { publicURL } : {},
154
180
  ...typeof authURL === "string" ? { authURL } : {},
155
181
  ...typeof message === "string" ? { message } : {},
182
+ ...typeof tailscaleVersion === "string" ? { tailscaleVersion } : {},
156
183
  updatedAt: int(s, "updatedAt", "remote.updatedAt")
157
184
  };
158
185
  }
@@ -555,6 +582,13 @@ window.__ModuleLoader__.load({
555
582
  "phase.unavailable": "远程 helper 不可用",
556
583
  "phase.stopped": "已停止",
557
584
  "phase.unknown": "状态未知",
585
+ "channel.state.disabled": "未启用",
586
+ "channel.state.starting": "正在启动",
587
+ "channel.state.login_required": "待授权",
588
+ "channel.state.online": "已就绪",
589
+ "channel.state.error": "失败",
590
+ "channel.state.unavailable": "不可用",
591
+ "channel.state.stopped": "已停止",
558
592
  "panel.activeConnections": "当前连接",
559
593
  "panel.identity": "设备身份认证",
560
594
  "panel.identityReady": "v2 已就绪",
@@ -582,6 +616,7 @@ window.__ModuleLoader__.load({
582
616
  "pair.kind.lan": "内网",
583
617
  "pair.noAddress": "没有可用地址",
584
618
  "pair.noAddressHelp": "没有可用的局域网或远程入口。请确认局域网连接已启动,或启用远程连接。",
619
+ "pair.disabledHelp": "DeepPilot 连接已关闭。先在上方开启连接,再添加手机。",
585
620
  "pair.qrAlt": "DeepPilot 配对二维码",
586
621
  "pair.qrShow": "显示二维码",
587
622
  "pair.qrHide": "隐藏二维码",
@@ -596,6 +631,8 @@ window.__ModuleLoader__.load({
596
631
  "advanced.summary": "高级信息",
597
632
  "advanced.protocolVersion": "协议版本",
598
633
  "advanced.serverVersion": "服务器版本",
634
+ "advanced.tailscaleVersion": "Tailscale 版本",
635
+ "advanced.tailscaleVersionUnknown": "未获取",
599
636
  "advanced.tokenPath": "Token 路径",
600
637
  "advanced.identityPath": "设备注册表路径",
601
638
  "advanced.bufferMax": "重放缓冲上限",
@@ -798,6 +835,13 @@ window.__ModuleLoader__.load({
798
835
  "phase.unavailable": "Remote helper unavailable",
799
836
  "phase.stopped": "Stopped",
800
837
  "phase.unknown": "Status unknown",
838
+ "channel.state.disabled": "Off",
839
+ "channel.state.starting": "Starting",
840
+ "channel.state.login_required": "Needs sign-in",
841
+ "channel.state.online": "Ready",
842
+ "channel.state.error": "Failed",
843
+ "channel.state.unavailable": "Unavailable",
844
+ "channel.state.stopped": "Stopped",
801
845
  "panel.activeConnections": "Active connections",
802
846
  "panel.identity": "Device authentication",
803
847
  "panel.identityReady": "v2 ready",
@@ -825,6 +869,7 @@ window.__ModuleLoader__.load({
825
869
  "pair.kind.lan": "LAN",
826
870
  "pair.noAddress": "No address available",
827
871
  "pair.noAddressHelp": "No LAN or remote endpoint is available. Make sure LAN is running or enable remote access.",
872
+ "pair.disabledHelp": "DeepPilot is turned off. Turn the connection on above before adding a phone.",
828
873
  "pair.qrAlt": "DeepPilot pairing QR code",
829
874
  "pair.qrShow": "Show QR code",
830
875
  "pair.qrHide": "Hide QR code",
@@ -839,6 +884,8 @@ window.__ModuleLoader__.load({
839
884
  "advanced.summary": "Advanced info",
840
885
  "advanced.protocolVersion": "Protocol version",
841
886
  "advanced.serverVersion": "Server version",
887
+ "advanced.tailscaleVersion": "Tailscale version",
888
+ "advanced.tailscaleVersionUnknown": "Not reported",
842
889
  "advanced.tokenPath": "Token path",
843
890
  "advanced.identityPath": "Device registry path",
844
891
  "advanced.bufferMax": "Replay buffer cap",
@@ -1021,155 +1068,169 @@ window.__ModuleLoader__.load({
1021
1068
  }
1022
1069
  //#endregion
1023
1070
  //#region src/client/styles.ts
1071
+ const EASE = "cubic-bezier(0.22,1,0.36,1)";
1024
1072
  const CSS = [
1025
- ".pbb-section{max-width:760px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:10px}",
1073
+ ".pbb-section{container:pbb/inline-size;max-width:760px;color:var(--dsw-alias-label-primary);font-size:14px;line-height:1.5;display:flex;flex-direction:column;gap:16px}",
1074
+ ".pbb-section>*{animation:pbb-cardIn 160ms " + EASE + " backwards}",
1075
+ ".pbb-section>*:nth-child(2){animation-delay:24ms}",
1076
+ ".pbb-section>*:nth-child(3){animation-delay:48ms}",
1077
+ ".pbb-section>*:nth-child(4){animation-delay:72ms}",
1078
+ ".pbb-section>*:nth-child(5){animation-delay:96ms}",
1079
+ ".pbb-section>*:nth-child(6){animation-delay:120ms}",
1080
+ ".pbb-section>*:nth-child(7){animation-delay:144ms}",
1081
+ ".pbb-section>*:nth-child(n+8){animation-delay:144ms}",
1082
+ "@keyframes pbb-cardIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}",
1026
1083
  ".pbb-pageHeader{display:flex;align-items:flex-start;gap:16px;padding:2px 2px 4px}",
1027
1084
  ".pbb-title{margin:0;font-size:18px;font-weight:600;letter-spacing:-.01em}",
1028
- ".pbb-intro{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;margin:4px 0 0;max-width:560px}",
1029
- ".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}",
1030
- ".pbb-refresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}",
1085
+ ".pbb-intro{color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1.5;margin:4px 0 0;max-width:600px}",
1086
+ ".pbb-refresh{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:none;padding:3px 0;margin-left:auto;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + "}",
1087
+ ".pbb-refresh:active{transform:scale(0.97)}",
1031
1088
  ".pbb-refresh:disabled{cursor:default;opacity:.5}",
1032
- ".pbb-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;overflow:hidden}",
1089
+ ".pbb-statusStrip{display:grid;grid-template-columns:repeat(4,1fr);background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2));border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4));border-radius:var(--dsw-radius-xl,20px);overflow:hidden}",
1090
+ ".pbb-statusCell{padding:14px 16px;min-width:0}",
1091
+ ".pbb-statusCell+.pbb-statusCell{border-left:1px solid var(--dsw-alias-border-l2)}",
1092
+ ".pbb-statusLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
1093
+ ".pbb-statusValue{margin-top:2px;font-size:16px;font-weight:600;line-height:22px;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
1094
+ ".pbb-statusValue .pbb-dot{margin-right:6px}",
1095
+ ".pbb-statusSub{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}",
1096
+ ".pbb-card{border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4));background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2));border-radius:var(--dsw-radius-xl,20px);overflow:hidden}",
1033
1097
  ".pbb-cardHeader{display:flex;align-items:flex-start;gap:16px;padding:15px 18px 13px}",
1034
1098
  ".pbb-cardHeader>div:first-child{min-width:0;flex:1}",
1035
- ".pbb-cardTitle{margin:0;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1036
- ".pbb-cardDescription{margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45}",
1037
- ".pbb-cardBody,.pbb-pairingBody{display:flex;flex-direction:column;gap:10px;padding:0 18px 15px}",
1099
+ ".pbb-cardTitle{margin:0;font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1100
+ ".pbb-cardDescription{margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
1101
+ ".pbb-cardHead{display:flex;align-items:center;gap:10px;width:100%;min-height:52px;padding:0 18px;border:0;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background-color 160ms " + EASE + "}",
1102
+ ".pbb-cardHead:active{background:var(--dsw-alias-bg-module-platform)}",
1103
+ ".pbb-cardSummary{flex:1;min-width:0;text-align:right;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
1104
+ ".pbb-chev{flex:none;display:inline-flex;align-items:center;color:var(--dsw-alias-label-tertiary);transition:transform 160ms " + EASE + "}",
1105
+ ".pbb-chevIcon{display:block}",
1106
+ ".pbb-cardHead[aria-expanded=\"true\"] .pbb-chev,.pbb-expand[aria-expanded=\"true\"] .pbb-chev{transform:rotate(90deg)}",
1107
+ ".pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 18px 16px}",
1108
+ ".pbb-pairingBody{display:flex;flex-direction:column;gap:10px;padding:0 18px 15px}",
1038
1109
  ".pbb-sectionIntro{padding-bottom:2px}",
1110
+ ".pbb-fold{display:grid;grid-template-rows:1fr;transition:grid-template-rows 240ms " + EASE + ",opacity 200ms " + EASE + ";opacity:1}",
1111
+ ".pbb-foldClosed{grid-template-rows:0fr;opacity:0}",
1112
+ ".pbb-fold>div{overflow:hidden;min-height:0}",
1039
1113
  ".pbb-row{display:flex;align-items:center;gap:8px}",
1040
1114
  ".pbb-label{flex:1;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary);min-width:0}",
1041
1115
  ".pbb-value{font-size:12px;color:var(--dsw-alias-label-secondary);word-break:break-all;text-align:right}",
1042
- ".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:11px;font-weight:500;white-space:nowrap}",
1116
+ ".pbb-badge,.pbb-countBadge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:2px 8px;font-size:12px;font-weight:500;white-space:nowrap}",
1117
+ ".pbb-badgeOk{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22a06b) 12%,transparent);color:var(--dsw-alias-state-success-primary,#22a06b)}",
1118
+ ".pbb-badgeWarn{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 14%,transparent);color:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1043
1119
  ".pbb-countBadge{min-width:24px;text-align:center}",
1044
- ".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:11px;font-weight:500;white-space:nowrap}",
1120
+ ".pbb-statusBadge{display:inline-flex;align-items:center;gap:6px;flex:none;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;white-space:nowrap}",
1045
1121
  ".pbb-ok{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));font-weight:500}",
1046
- ".pbb-bad{color:var(--dsw-alias-label-error);font-weight:500}",
1047
- ".pbb-diag{font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5;overflow-wrap:anywhere}",
1048
- ".pbb-diagBad{color:var(--dsw-alias-label-error)}",
1049
- ".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-l2);border-radius:8px}",
1050
- ".pbb-tokenRow{flex-wrap:wrap}",
1051
- ".pbb-token{max-width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--dsw-alias-label-secondary);word-break:break-all}",
1052
- ".pbb-tokenActions{display:flex;align-items:center;gap:8px}",
1053
- ".pbb-action{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;padding:5px 9px;line-height:1.2;white-space:nowrap}",
1054
- ".pbb-action:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}",
1122
+ ".pbb-bad{color:var(--dsw-alias-state-error-primary);font-weight:500}",
1123
+ ".pbb-diag{font-size:12px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5;overflow-wrap:anywhere}",
1124
+ ".pbb-diagBad{color:var(--dsw-alias-state-error-primary)}",
1125
+ ".pbb-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.55;margin:0;padding:12px;border:1px dashed var(--dsw-alias-border-l3);border-radius:8px}",
1126
+ ".pbb-token{max-width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--dsw-alias-label-secondary);word-break:break-all}",
1127
+ ".pbb-action{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:1px solid var(--dsw-alias-border-l3);border-radius:7px;padding:5px 9px;line-height:1.2;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + ",background-color 160ms " + EASE + ",border-color 160ms " + EASE + "}",
1128
+ ".pbb-action:active{transform:scale(0.97)}",
1055
1129
  ".pbb-action:disabled{cursor:default;opacity:.5}",
1056
- ".pbb-buttonPrimary{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-state-success-primary,#3b82f6);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#3b82f6) 12%,transparent)}",
1057
- ".pbb-buttonPrimary:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#3b82f6) 18%,transparent)}",
1058
- ".pbb-actionSelected{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-state-success-primary,#22a06b);background:var(--dsw-alias-bg-module-platform)}",
1059
- ".pbb-actionDangerGhost{color:#dc2626;border-color:rgba(220,38,38,.38);background:transparent;line-height:1.35;padding:4px 9px;transition:color .15s ease,background-color .15s ease,border-color .15s ease,transform .1s ease}",
1060
- ".pbb-actionDangerGhost:hover:not(:disabled){color:#b91c1c;background:rgba(220,38,38,.09);border-color:rgba(185,28,28,.5)}",
1061
- ".pbb-actionDangerGhost:active:not(:disabled){transform:translateY(1px)}",
1062
- ".pbb-actionDangerGhost:focus-visible{outline:2px solid rgba(220,38,38,.32);outline-offset:1px}",
1130
+ ".pbb-buttonPrimary{font-size:13px;padding:6px 12px;color:var(--dsw-alias-label-primary-foreground);border-color:transparent;background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary))}",
1131
+ ".pbb-actionDangerGhost{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 38%,transparent);background:transparent;line-height:1.35;padding:4px 9px}",
1132
+ ".pbb-actionDangerGhost:active{transform:scale(0.97)}",
1063
1133
  ".pbb-actionDangerGhost:disabled{cursor:default;opacity:.62}",
1064
1134
  ".pbb-actionIcon{flex:none;display:inline-block;vertical-align:-2px;margin-right:5px;pointer-events:none}",
1065
1135
  ".pbb-actionBusy{display:inline-flex;align-items:center;gap:6px}",
1066
- ".pbb-actionBusy::before{content:\"\";width:9px;height:9px;border-radius:50%;border:1.5px solid rgba(220,38,38,.35);border-top-color:currentColor;animation:pbb-spin .7s linear infinite}",
1136
+ ".pbb-actionBusy::before{content:\"\";width:9px;height:9px;border-radius:50%;border:1.5px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,transparent);border-top-color:currentColor;animation:pbb-spin .7s linear infinite}",
1067
1137
  "@keyframes pbb-spin{to{transform:rotate(360deg)}}",
1068
- ".pbb-masterRow{display:flex;align-items:center;gap:14px;padding:0 18px 14px}",
1138
+ ".pbb-masterRow{display:flex;align-items:center;gap:14px}",
1069
1139
  ".pbb-switchText{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}",
1070
- ".pbb-switchTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1071
- ".pbb-switchDesc{font-size:11px;color:var(--dsw-alias-label-tertiary);line-height:1.4}",
1072
- ".pbb-switch{appearance:none;-webkit-appearance:none;width:34px;height:20px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);position:relative;cursor:pointer;padding:0;flex:none;transition:background .15s ease,border-color .15s ease}",
1073
- ".pbb-switch::after{content:\"\";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--dsw-alias-label-secondary);transition:transform .15s ease,background .15s ease}",
1074
- ".pbb-switchOn{background:var(--dsw-alias-state-success-primary,#22a06b);border-color:transparent}",
1075
- ".pbb-switchOn::after{transform:translateX(14px);background:#fff}",
1140
+ ".pbb-switchTitle{font-size:14px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1141
+ ".pbb-switchDesc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.45}",
1142
+ ".pbb-switch{appearance:none;-webkit-appearance:none;width:40px;height:24px;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent);border:1px solid transparent;position:relative;cursor:pointer;padding:0;flex:none;transition:background-color 160ms " + EASE + ",border-color 160ms " + EASE + ",transform 160ms " + EASE + "}",
1143
+ ".pbb-switch::after{content:\"\";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.24);transition:transform 160ms " + EASE + ",background-color 160ms " + EASE + "}",
1144
+ ".pbb-switchOn{background:var(--dsw-alias-brand-primary)}",
1145
+ ".pbb-switchOn::after{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}",
1146
+ ".pbb-switch:active{transform:scale(0.97)}",
1076
1147
  ".pbb-switch:disabled{opacity:.5;cursor:default}",
1077
- ".pbb-switch:focus-visible,.pbb-action:focus-visible,.pbb-targetButton:focus-visible{outline:2px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#3b82f6) 55%,transparent);outline-offset:2px}",
1078
- ".pbb-connectionGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:0 18px 14px}",
1079
- ".pbb-connectionTile{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-module-platform);padding:11px 12px;display:flex;flex-direction:column;gap:5px}",
1080
- ".pbb-connectionTileHeader{display:flex;align-items:center;gap:10px}",
1081
- ".pbb-dotRow{display:flex;align-items:center;gap:7px;flex:1;min-width:0}",
1082
- ".pbb-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
1148
+ ".pbb-switch:focus-visible,.pbb-action:focus-visible,.pbb-targetButton:focus-visible,.pbb-cardHead:focus-visible,.pbb-expand:focus-visible,.pbb-refresh:focus-visible,.pbb-numberInput:focus-visible,.pbb-deviceNameInput:focus-visible,.pbb-disclosure>summary:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}",
1149
+ ".pbb-dot{display:inline-block;vertical-align:1px;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
1083
1150
  ".pbb-dotOk{background:var(--dsw-alias-state-success-primary,#22a06b)}",
1084
- ".pbb-dotWarn{background:var(--dsw-alias-state-warning-primary,#e2b203)}",
1085
- ".pbb-dotBad{background:var(--dsw-alias-label-error)}",
1086
- ".pbb-connectionState{font-size:11px;color:var(--dsw-alias-label-secondary);font-weight:500}",
1087
- ".pbb-connectionDescription{font-size:11px;color:var(--dsw-alias-label-tertiary);line-height:1.45;margin:0}",
1088
- ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
1089
- ".pbb-statGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:0 18px 14px}",
1090
- ".pbb-stat{display:flex;align-items:center;gap:8px;min-width:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-bg-module-platform)}",
1091
- ".pbb-statLabel{font-size:11px;color:var(--dsw-alias-label-tertiary);flex:1}",
1092
- ".pbb-statValue{font-size:12px;color:var(--dsw-alias-label-primary);font-weight:600}",
1093
- ".pbb-inlineDetails,.pbb-help{border-top:1px solid var(--dsw-alias-border-l2)}",
1094
- ".pbb-inlineDetails>summary,.pbb-help>summary{cursor:pointer;color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;user-select:none;list-style:none;display:flex;align-items:center;padding:12px 18px}",
1095
- ".pbb-inlineDetails>summary::-webkit-details-marker,.pbb-help>summary::-webkit-details-marker{display:none}",
1096
- ".pbb-inlineDetails>summary::after,.pbb-help>summary::after{content:\"›\";margin-left:auto;color:var(--dsw-alias-label-tertiary);font-size:16px;line-height:1;transform:rotate(90deg);transition:transform .15s ease}",
1097
- ".pbb-inlineDetails[open]>summary::after,.pbb-help[open]>summary::after{transform:rotate(-90deg)}",
1098
- ".pbb-inlineDetails[open]>summary,.pbb-help[open]>summary{color:var(--dsw-alias-label-secondary)}",
1099
- ".pbb-helpBody{display:flex;flex-direction:column;gap:14px;padding:2px 18px 15px}",
1100
- ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
1101
- ".pbb-helpHeading{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1102
- ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.6}",
1103
- ".pbb-helpList li+li{margin-top:4px}",
1104
- ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.6}",
1105
- ".pbb-helpCode{display:block;margin:2px 0 0;padding:10px;overflow:auto;white-space:pre-wrap;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:11px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}",
1106
- ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
1151
+ ".pbb-dotWarn{background:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1152
+ ".pbb-dotBad{background:var(--dsw-alias-state-error-primary)}",
1153
+ ".pbb-switchTitle .pbb-dot{margin-right:6px;vertical-align:1px}",
1154
+ ".pbb-channel{border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px;display:flex;flex-direction:column;gap:10px}",
1155
+ ".pbb-expand{border:0;background:none;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;padding:0;display:flex;align-items:center;gap:6px;align-self:flex-start;transition:color 160ms " + EASE + ",transform 160ms " + EASE + "}",
1156
+ ".pbb-expand:active{transform:scale(0.97)}",
1157
+ ".pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 0 0 14px}",
1107
1158
  ".pbb-limitRow{display:flex;align-items:center;gap:12px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1108
1159
  ".pbb-limitRow:first-child{border-top:0;padding-top:2px}",
1109
1160
  ".pbb-limitControl{display:flex;align-items:center;gap:8px;flex:none}",
1110
- ".pbb-numberInput{width:72px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:5px 8px}",
1111
- ".pbb-numberInput:focus{outline:2px solid var(--dsw-alias-state-success-primary,#22a06b);outline-offset:1px}",
1161
+ ".pbb-numberInput{width:72px;box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:6px 8px}",
1162
+ ".pbb-numberInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1112
1163
  ".pbb-numberInput:disabled{opacity:.5}",
1113
- ".pbb-targetList{display:flex;flex-wrap:wrap;gap:6px}",
1114
- ".pbb-targetButton{font:inherit;font-size:11px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;padding:6px 9px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1115
- ".pbb-targetButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}",
1164
+ ".pbb-targetList{display:inline-flex;flex-wrap:wrap;gap:2px;background:var(--dsw-alias-bg-module-platform);border-radius:999px;padding:2px;align-self:flex-start}",
1165
+ ".pbb-targetButton{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:0;border-radius:999px;padding:6px 9px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + ",background-color 160ms " + EASE + ",box-shadow 160ms " + EASE + "}",
1166
+ ".pbb-targetButton:active{transform:scale(0.97)}",
1116
1167
  ".pbb-targetButton:disabled{cursor:default;opacity:.5}",
1117
- ".pbb-targetSelected{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-state-success-primary,#22a06b);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22a06b) 10%,transparent)}",
1118
- ".pbb-qrPanel{display:flex;flex-direction:column;align-items:center;gap:9px;padding:14px 0 4px;border-top:1px solid var(--dsw-alias-border-l2)}",
1119
- ".pbb-qrImage{width:224px;height:224px;max-width:100%;background:#fff;border-radius:10px;padding:8px;box-sizing:border-box}",
1120
- ".pbb-pairCodeBlock{width:min(100%,460px);display:flex;flex-direction:column;gap:6px}",
1121
- ".pbb-pairCodeLabel{font-size:11px;color:var(--dsw-alias-label-tertiary);text-align:center}",
1122
- ".pbb-pairCodeRow{display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap}",
1123
- ".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;text-align:center;user-select:all}",
1124
- ".pbb-qrHint{max-width:460px;text-align:center;font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
1125
- ".pbb-tableWrap{width:100%;overflow-x:auto}",
1126
- ".pbb-table{width:100%;border-collapse:collapse;font-size:12px;table-layout:fixed}",
1127
- ".pbb-table th{color:var(--dsw-alias-label-tertiary);text-align:left;font-weight:500;padding:7px 8px;border-bottom:1px solid var(--dsw-alias-border-l2)}",
1128
- ".pbb-table td{padding:9px 8px;border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);vertical-align:middle}",
1129
- ".pbb-table tbody tr:last-child td{border-bottom:0}",
1130
- ".pbb-table th:first-child{width:48%}",
1131
- ".pbb-table th:nth-child(2){width:32%}",
1132
- ".pbb-tableActionHead,.pbb-tableActionCell{text-align:right;width:142px!important}",
1133
- ".pbb-tableActionCell{white-space:nowrap}",
1134
- ".pbb-tableActionCell>.pbb-action+.pbb-action{margin-left:5px}",
1168
+ ".pbb-targetSelected{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);box-shadow:0 1px 3px color-mix(in srgb,var(--dsw-alias-label-primary) 14%,transparent);font-weight:500}",
1169
+ ".pbb-qrPanel{display:grid;grid-template-columns:224px 1fr;gap:20px;align-items:start;padding:14px 0 4px;border-top:1px solid var(--dsw-alias-border-l2)}",
1170
+ ".pbb-qrImage{width:224px;height:224px;max-width:100%;background:#fff;border-radius:12px;padding:8px;box-sizing:border-box}",
1171
+ ".pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}",
1172
+ ".pbb-countdownBar{height:4px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);overflow:hidden}",
1173
+ ".pbb-countdownBar i{display:block;width:100%;height:100%;transform-origin:left center;background:var(--dsw-alias-state-business-primary,var(--dsw-alias-state-success-primary,#22a06b));border-radius:999px;transition:transform 1s linear}",
1174
+ ".pbb-countdownText{font-size:12px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}",
1175
+ ".pbb-pairInfo{display:flex;flex-direction:column;gap:10px;min-width:0}",
1176
+ ".pbb-pairCodeBlock{width:100%;display:flex;flex-direction:column;gap:6px}",
1177
+ ".pbb-pairCodeLabel{font-size:12px;color:var(--dsw-alias-label-tertiary)}",
1178
+ ".pbb-pairCodeRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
1179
+ ".pbb-pairCode{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;line-height:1.4;letter-spacing:.03em;color:var(--dsw-alias-label-primary);word-break:break-all;user-select:all}",
1180
+ ".pbb-qrHint{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
1181
+ ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
1182
+ ".pbb-resultRow{display:flex;align-items:flex-start;gap:6px}",
1183
+ ".pbb-resultIcon{flex:none;display:inline-flex;margin-top:2px;color:var(--dsw-alias-state-success-primary)}",
1184
+ ".pbb-resultIconBad{color:var(--dsw-alias-state-error-primary)}",
1185
+ ".pbb-deviceList{display:flex;flex-direction:column}",
1186
+ ".pbb-deviceRow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:12px;row-gap:8px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1187
+ ".pbb-deviceRow:first-child{border-top:0;padding-top:2px}",
1188
+ ".pbb-deviceRow:last-child{padding-bottom:0}",
1189
+ ".pbb-deviceRow>.pbb-deviceRenameForm,.pbb-deviceRow>.pbb-deviceConfirm{grid-column:1/-1}",
1190
+ ".pbb-deviceMain{min-width:0;display:flex;flex-direction:column;gap:2px}",
1135
1191
  ".pbb-deviceNameRow{display:flex;align-items:center;gap:6px;min-width:0}",
1136
- ".pbb-deviceName{display:block;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1137
- ".pbb-deviceMeta{display:flex;gap:4px;align-items:center;margin-top:3px;color:var(--dsw-alias-label-tertiary);font-size:10px;min-width:0}",
1138
- ".pbb-deviceMeta code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1139
- ".pbb-deviceRenameForm{display:flex;flex-direction:column;gap:5px;min-width:0}",
1140
- ".pbb-deviceRenameControls{display:flex;align-items:center;gap:7px;min-width:0}",
1141
- ".pbb-deviceRenameControls>.pbb-deviceNameInput{flex:1;width:auto;min-width:0}",
1142
- ".pbb-deviceNameInput{width:100%;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:6px 8px}",
1143
- ".pbb-deviceNameInput:focus{outline:2px solid var(--dsw-alias-state-success-primary,#22a06b);outline-offset:1px}",
1192
+ ".pbb-deviceName{display:block;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1193
+ ".pbb-deviceMeta{display:flex;flex-wrap:wrap;column-gap:6px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;min-width:0}",
1194
+ ".pbb-deviceMeta>*+*::before{content:\"\\00B7\";margin-right:6px}",
1195
+ ".pbb-deviceMeta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1196
+ ".pbb-deviceActions{grid-row:1;grid-column:2;display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}",
1197
+ ".pbb-deviceConfirm{display:flex;flex-direction:column;gap:8px}",
1198
+ ".pbb-deviceRenameForm{display:flex;gap:8px;flex-wrap:wrap;min-width:0}",
1199
+ ".pbb-deviceNameInput{flex:1;min-width:0;box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:6px 8px}",
1200
+ ".pbb-deviceNameInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1144
1201
  ".pbb-deviceNameInput:disabled{opacity:.5}",
1145
1202
  ".pbb-deviceRenameActions{display:flex;align-items:center;gap:5px;flex:none}",
1146
- ".pbb-deviceIconAction{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:8px;box-shadow:none}",
1147
- ".pbb-deviceRenameActions .pbb-buttonPrimary{color:#fff;border-color:transparent;background:var(--dsw-alias-state-success-primary,#3b82f6)}",
1148
- ".pbb-deviceRenameActions .pbb-buttonPrimary:hover:not(:disabled){color:#fff;background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#3b82f6) 86%,#000)}",
1149
- ".pbb-deviceRenameActions .pbb-deviceIconAction:not(.pbb-buttonPrimary){background:var(--dsw-alias-bg-module-platform);border-color:transparent;color:var(--dsw-alias-label-secondary)}",
1150
- ".pbb-deviceRenameActions .pbb-deviceIconAction:not(.pbb-buttonPrimary):hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-module-platform));color:var(--dsw-alias-label-primary)}",
1151
- ".pbb-controlIcon{display:block;pointer-events:none}",
1152
- ".pbb-inlineSpinner{width:12px;height:12px;box-sizing:border-box;border:1.5px solid currentColor;border-right-color:transparent;border-radius:50%;animation:pbb-spin .7s linear infinite}",
1153
- ".pbb-lastSeen{color:var(--dsw-alias-label-secondary);font-size:11px;overflow-wrap:anywhere}",
1154
- ".pbb-disclosure{padding:0}",
1155
- ".pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:12px;padding:13px 18px;cursor:pointer;color:var(--dsw-alias-label-primary)}",
1156
- ".pbb-disclosure>summary::-webkit-details-marker{display:none}",
1157
- ".pbb-disclosure>summary::after{content:\"›\";color:var(--dsw-alias-label-tertiary);font-size:17px;line-height:1;transform:rotate(90deg);transition:transform .15s ease}",
1158
- ".pbb-disclosure[open]>summary::after{transform:rotate(-90deg)}",
1159
- ".pbb-disclosureTitle{font-size:12px;font-weight:500;flex:1}",
1160
- ".pbb-disclosureMeta{font-size:11px;color:var(--dsw-alias-label-tertiary);max-width:46%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1161
- ".pbb-disclosureBody{display:flex;flex-direction:column;gap:12px;padding:2px 18px 16px}",
1203
+ ".pbb-helpBody{display:flex;flex-direction:column;gap:14px}",
1204
+ ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
1205
+ ".pbb-helpHeading{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1206
+ ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1207
+ ".pbb-helpList li+li{margin-top:4px}",
1208
+ ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1209
+ ".pbb-helpCode{display:block;margin:2px 0 0;padding:10px;overflow:auto;white-space:pre-wrap;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}",
1210
+ ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
1162
1211
  ".pbb-testGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}",
1163
- ".pbb-testItem{display:flex;align-items:center;gap:12px;padding:11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-module-platform)}",
1164
- ".pbb-testTitle{font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1165
- ".pbb-testDescription{font-size:10px;color:var(--dsw-alias-label-tertiary);line-height:1.45}",
1166
- ".pbb-runtimeList{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;overflow:hidden}",
1212
+ ".pbb-testItem:last-child:nth-child(odd){grid-column:1/-1}",
1213
+ ".pbb-testItem{display:flex;align-items:center;gap:12px;padding:11px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-module-platform)}",
1214
+ ".pbb-testTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1215
+ ".pbb-testDescription{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.5}",
1216
+ ".pbb-runtimeList{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;overflow:hidden}",
1167
1217
  ".pbb-runtimeList>.pbb-runtimeRow,.pbb-runtimeRow{display:flex;align-items:center;gap:10px;padding:9px 10px;border-top:1px solid var(--dsw-alias-border-l2)}",
1168
1218
  ".pbb-runtimeList>.pbb-runtimeRow:first-child{border-top:0}",
1169
1219
  ".pbb-runtimeList>.pbb-runtimeRow>.pbb-row{flex:1;min-width:0}",
1170
- ".pbb-updateRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#22a06b) 35%,var(--dsw-alias-border-l2));border-radius:8px;color:var(--dsw-alias-label-primary);font-size:12px}",
1220
+ ".pbb-updateRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-business-primary,#4176e6) 35%,var(--dsw-alias-border-l3));border-radius:8px;color:var(--dsw-alias-label-primary);font-size:13px;text-decoration:none;cursor:pointer;transition:background-color 160ms " + EASE + "}",
1221
+ ".pbb-disclosure{padding:0}",
1222
+ ".pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;padding:13px 18px;cursor:pointer;color:var(--dsw-alias-label-primary)}",
1223
+ ".pbb-disclosure>summary::-webkit-details-marker{display:none}",
1224
+ ".pbb-disclosure[open]>summary .pbb-chev{transform:rotate(90deg)}",
1225
+ ".pbb-disclosureTitle{font-size:13px;font-weight:500;flex:1}",
1226
+ ".pbb-disclosureMeta{font-size:12px;color:var(--dsw-alias-label-tertiary);max-width:46%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1227
+ ".pbb-disclosureBody{display:flex;flex-direction:column;gap:12px;padding:2px 18px 16px}",
1171
1228
  ".pbb-diagnostics>summary{color:var(--dsw-alias-label-secondary)}",
1172
- "@media (max-width:620px){.pbb-connectionGrid,.pbb-testGrid{grid-template-columns:1fr}.pbb-cardHeader{align-items:flex-start}.pbb-masterRow{align-items:flex-start}.pbb-lastSeen{display:none}.pbb-table th:nth-child(2){width:1px}.pbb-table th:first-child{width:auto}.pbb-tableActionHead,.pbb-tableActionCell{width:132px!important}.pbb-limitRow{align-items:flex-start;flex-direction:column}.pbb-limitControl{align-self:flex-end}}"
1229
+ "@media (hover:hover) and (pointer:fine){.pbb-refresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.pbb-cardHead:hover{background:var(--dsw-alias-bg-module-platform)}.pbb-action:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}.pbb-buttonPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-interactive-bg-hover-solid))}.pbb-actionDangerGhost:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 9%,transparent);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent)}.pbb-expand:hover{color:var(--dsw-alias-label-primary)}.pbb-updateRow:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary,#4176e6) 8%,transparent)}}",
1230
+ "@media (prefers-reduced-motion:reduce){.pbb-section *{animation-duration:.01ms!important;animation-delay:0s!important;transition-duration:.01ms!important;transition-delay:0s!important}}",
1231
+ "@container pbb (max-width:640px){.pbb-statusStrip{grid-template-columns:repeat(2,1fr)}.pbb-statusCell:nth-child(3){border-left:0}.pbb-statusCell:nth-child(n+3){border-top:1px solid var(--dsw-alias-border-l2)}.pbb-cardSummary{display:none}}",
1232
+ "@container pbb (max-width:480px){.pbb-testGrid{grid-template-columns:1fr}.pbb-testItem:last-child:nth-child(odd){grid-column:auto}.pbb-qrPanel{grid-template-columns:1fr}.pbb-limitRow{align-items:flex-start;flex-direction:column}.pbb-limitControl{align-self:flex-end}.pbb-deviceRow{grid-template-columns:minmax(0,1fr)}.pbb-deviceActions{grid-row:auto;grid-column:auto;justify-content:flex-start}}",
1233
+ "@media (pointer:coarse){.pbb-action{min-height:32px;padding:7px 10px;font-size:13px}.pbb-buttonPrimary{font-size:14px}.pbb-cardHead{min-height:56px}.pbb-expand{padding:5px 0}.pbb-refresh{padding:5px 0}}"
1173
1234
  ].join("\n");
1174
1235
  /**
1175
1236
  * Client bundle id, identical to the `window.__ModuleLoader__.load({ id })`
@@ -4032,26 +4093,6 @@ window.__ModuleLoader__.load({
4032
4093
  function deviceLabel(device) {
4033
4094
  return (device.customName ?? device.deviceName).trim();
4034
4095
  }
4035
- /** 渲染一块连接磁贴。纯数据函数:九参进,元素树出。 */
4036
- function renderConnectionTile(key, title, description, checked, ready, dot, statusLabel, onToggle, extra = null) {
4037
- return (0, react.createElement)("div", {
4038
- className: "pbb-connectionTile",
4039
- key
4040
- }, (0, react.createElement)("div", { className: "pbb-connectionTileHeader" }, (0, react.createElement)("span", { className: "pbb-switchTitle pbb-dotRow" }, (0, react.createElement)("span", {
4041
- className: "pbb-dot" + dot,
4042
- role: "img",
4043
- "aria-label": statusLabel,
4044
- title: statusLabel
4045
- }), title), (0, react.createElement)("button", {
4046
- type: "button",
4047
- role: "switch",
4048
- "aria-checked": checked,
4049
- "aria-label": title,
4050
- disabled: !ready,
4051
- className: "pbb-switch" + (checked ? " pbb-switchOn" : ""),
4052
- onClick: onToggle
4053
- })), (0, react.createElement)("span", { className: "pbb-connectionState" }, statusLabel), (0, react.createElement)("p", { className: "pbb-connectionDescription" }, description), extra);
4054
- }
4055
4096
  //#endregion
4056
4097
  //#region src/client/settings-page.ts
4057
4098
  /** phase 语义标签 → i18n key。措辞归页面,view model 只认语义。 */
@@ -4071,6 +4112,16 @@ window.__ModuleLoader__.load({
4071
4112
  error: "local.phaseError",
4072
4113
  stopped: "local.phaseStopped"
4073
4114
  };
4115
+ /** 状态条用的短状态:列标签已经写明是哪条通道,值只说状态,免得在窄格里被截断。 */
4116
+ const CHANNEL_STATE_KEY = {
4117
+ disabled: "channel.state.disabled",
4118
+ starting: "channel.state.starting",
4119
+ login_required: "channel.state.login_required",
4120
+ online: "channel.state.online",
4121
+ error: "channel.state.error",
4122
+ unavailable: "channel.state.unavailable",
4123
+ stopped: "channel.state.stopped"
4124
+ };
4074
4125
  /** 服务状态 → i18n key。 */
4075
4126
  const SERVICE_STATUS_KEY = {
4076
4127
  loading: "status.loading",
@@ -4093,29 +4144,59 @@ window.__ModuleLoader__.load({
4093
4144
  strokeLinejoin: "round",
4094
4145
  "aria-hidden": true
4095
4146
  }, (0, react.createElement)("path", { d: "M2.5 4.5h11" }), (0, react.createElement)("path", { d: "M6.5 2.5h3" }), (0, react.createElement)("path", { d: "M4 4.5l.6 8.2a1 1 0 0 0 1 .9h4.8a1 1 0 0 0 1-.9l.6-8.2" }), (0, react.createElement)("path", { d: "M6.6 7v4.2" }), (0, react.createElement)("path", { d: "M9.4 7v4.2" }));
4147
+ /** Chevron icon (linear, currentColor). CSS rotates it 90° while its owner is
4148
+ * expanded, so collapsed always means "points right, more content behind". */
4149
+ const ChevronIcon = () => (0, react.createElement)("svg", {
4150
+ className: "pbb-chevIcon",
4151
+ viewBox: "0 0 16 16",
4152
+ width: 12,
4153
+ height: 12,
4154
+ fill: "none",
4155
+ stroke: "currentColor",
4156
+ strokeWidth: 1.5,
4157
+ strokeLinecap: "round",
4158
+ strokeLinejoin: "round",
4159
+ "aria-hidden": true
4160
+ }, (0, react.createElement)("path", { d: "M6 3.5 10.5 8 6 12.5" }));
4161
+ /** Check/cross marks for the relay and push result lines. They used to be the
4162
+ * dingbats '✓'/'✗', which the repo's visual rules reject as character icons. */
4096
4163
  const CheckIcon = () => (0, react.createElement)("svg", {
4097
- className: "pbb-controlIcon",
4098
4164
  viewBox: "0 0 16 16",
4099
- width: 14,
4100
- height: 14,
4165
+ width: 12,
4166
+ height: 12,
4101
4167
  fill: "none",
4102
4168
  stroke: "currentColor",
4103
- strokeWidth: 1.8,
4169
+ strokeWidth: 1.6,
4104
4170
  strokeLinecap: "round",
4105
4171
  strokeLinejoin: "round",
4106
4172
  "aria-hidden": true
4107
- }, (0, react.createElement)("path", { d: "m3 8.3 3.1 3.1L13 4.8" }));
4108
- const CloseIcon = () => (0, react.createElement)("svg", {
4109
- className: "pbb-controlIcon",
4173
+ }, (0, react.createElement)("path", { d: "M3.5 8.5 6.5 11.5 12.5 4.5" }));
4174
+ const CrossIcon = () => (0, react.createElement)("svg", {
4110
4175
  viewBox: "0 0 16 16",
4111
- width: 14,
4112
- height: 14,
4176
+ width: 12,
4177
+ height: 12,
4113
4178
  fill: "none",
4114
4179
  stroke: "currentColor",
4115
4180
  strokeWidth: 1.6,
4116
4181
  strokeLinecap: "round",
4182
+ strokeLinejoin: "round",
4117
4183
  "aria-hidden": true
4118
- }, (0, react.createElement)("path", { d: "m4.5 4.5 7 7m0-7-7 7" }));
4184
+ }, (0, react.createElement)("path", { d: "M4.5 4.5 11.5 11.5" }), (0, react.createElement)("path", { d: "M11.5 4.5 4.5 11.5" }));
4185
+ /** One result line: SVG mark + text; the mark carries the pass/fail state. */
4186
+ const resultRow = (ok, text, key) => (0, react.createElement)("p", {
4187
+ className: "pbb-diag pbb-resultRow",
4188
+ key
4189
+ }, (0, react.createElement)("span", { className: "pbb-resultIcon" + (ok ? "" : " pbb-resultIconBad") }, ok ? CheckIcon() : CrossIcon()), (0, react.createElement)("span", null, text));
4190
+ /** 最近在线只细化到分钟:秒和年份对判断设备是否还连着没有意义。 */
4191
+ function formatLastSeen(ts) {
4192
+ if (ts === void 0 || !Number.isFinite(ts) || ts <= 0) return "—";
4193
+ return new Date(ts).toLocaleString(void 0, {
4194
+ month: "numeric",
4195
+ day: "numeric",
4196
+ hour: "2-digit",
4197
+ minute: "2-digit"
4198
+ });
4199
+ }
4119
4200
  async function writeClipboard(t, value) {
4120
4201
  try {
4121
4202
  await navigator.clipboard.writeText(value);
@@ -4164,6 +4245,11 @@ window.__ModuleLoader__.load({
4164
4245
  message: "",
4165
4246
  failed: false
4166
4247
  });
4248
+ const [localOptionsOpen, setLocalOptionsOpen] = (0, react.useState)(false);
4249
+ const [remoteOptionsOpen, setRemoteOptionsOpen] = (0, react.useState)(false);
4250
+ const [systemOpen, setSystemOpen] = (0, react.useState)(false);
4251
+ const [armedRevokeDeviceId, setArmedRevokeDeviceId] = (0, react.useState)(null);
4252
+ const [nowMs, setNowMs] = (0, react.useState)(() => Date.now());
4167
4253
  (0, react.useEffect)(() => {
4168
4254
  if (!can("refresh")) return;
4169
4255
  const refresh = props.refresh;
@@ -4423,37 +4509,22 @@ window.__ModuleLoader__.load({
4423
4509
  });
4424
4510
  });
4425
4511
  };
4426
- const primaryRows = [];
4427
4512
  const advancedRows = [];
4428
- if (report !== null) {
4429
- primaryRows.push((0, react.createElement)("div", {
4430
- className: "pbb-stat",
4431
- key: "conn"
4432
- }, (0, react.createElement)("span", { className: "pbb-statLabel" }, translateWith(props.t, "panel.activeConnections")), (0, react.createElement)("span", { className: "pbb-statValue" }, String(report.activeConnections))), (0, react.createElement)("div", {
4433
- className: "pbb-stat",
4434
- key: "identity"
4435
- }, (0, react.createElement)("span", { className: "pbb-statLabel" }, translateWith(props.t, "panel.identity")), (0, react.createElement)("code", { className: "pbb-token " + (report.pairingReady ? "pbb-ok" : "pbb-bad") }, report.pairingReady ? translateWith(props.t, "panel.identityReady") : translateWith(props.t, "panel.identityNotReady"))));
4436
- if (report.local.phase === "online" && report.local.tlsFingerprint) advancedRows.push((0, react.createElement)("div", {
4437
- className: "pbb-runtimeRow",
4438
- key: "tls"
4439
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "local.tlsFingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, report.local.tlsFingerprint))));
4440
- advancedRows.push((0, react.createElement)("div", {
4441
- className: "pbb-runtimeRow",
4442
- key: "proto"
4443
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.protocolVersion")), (0, react.createElement)("span", { className: "pbb-value" }, "v" + String(report.protocolVersion)))), (0, react.createElement)("div", {
4444
- className: "pbb-runtimeRow",
4445
- key: "server"
4446
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.serverVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.serverVersion))), (0, react.createElement)("div", {
4447
- className: "pbb-runtimeRow",
4448
- key: "path"
4449
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.identityPath")), (0, react.createElement)("span", { className: "pbb-value" }, report.identityPath))), (0, react.createElement)("div", {
4450
- className: "pbb-runtimeRow",
4451
- key: "buffer"
4452
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.bufferMax")), (0, react.createElement)("span", { className: "pbb-value" }, String(report.historyBufferMax) + translateWith(props.t, "advanced.frames")))));
4453
- }
4454
- const revokeDevice = (deviceId, name) => {
4513
+ if (report !== null) advancedRows.push((0, react.createElement)("div", {
4514
+ className: "pbb-runtimeRow",
4515
+ key: "proto"
4516
+ }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.protocolVersion")), (0, react.createElement)("span", { className: "pbb-value" }, "v" + String(report.protocolVersion)))), (0, react.createElement)("div", {
4517
+ className: "pbb-runtimeRow",
4518
+ key: "server"
4519
+ }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.serverVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.serverVersion))), (0, react.createElement)("div", {
4520
+ className: "pbb-runtimeRow",
4521
+ key: "path"
4522
+ }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.identityPath")), (0, react.createElement)("span", { className: "pbb-value" }, report.identityPath))), (0, react.createElement)("div", {
4523
+ className: "pbb-runtimeRow",
4524
+ key: "buffer"
4525
+ }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.bufferMax")), (0, react.createElement)("span", { className: "pbb-value" }, String(report.historyBufferMax) + translateWith(props.t, "advanced.frames")))));
4526
+ const revokeDevice = (deviceId, _name) => {
4455
4527
  if (!can("revokeDevice")) return;
4456
- if (typeof window !== "undefined" && !window.confirm(translateWith(props.t, "devices.revokeConfirm", { name }))) return;
4457
4528
  setDeviceBusy(deviceId);
4458
4529
  setDeviceBusyAction("revoke");
4459
4530
  setDeviceMessage("");
@@ -4468,6 +4539,7 @@ window.__ModuleLoader__.load({
4468
4539
  };
4469
4540
  const deviceLabel$1 = (device) => deviceLabel(device) || translateWith(props.t, "devices.unnamed");
4470
4541
  const beginDeviceRename = (device) => {
4542
+ setArmedRevokeDeviceId(null);
4471
4543
  setEditingDeviceId(device.deviceId);
4472
4544
  setDeviceNameDraft(device.customName ?? device.deviceName);
4473
4545
  setDeviceMessage("");
@@ -4499,20 +4571,28 @@ window.__ModuleLoader__.load({
4499
4571
  });
4500
4572
  };
4501
4573
  const visibleDevices$1 = visibleDevices(report);
4502
- const deviceTable = visibleDevices$1.length > 0 ? (0, react.createElement)("div", { className: "pbb-tableWrap" }, (0, react.createElement)("table", { className: "pbb-table" }, (0, react.createElement)("thead", null, (0, react.createElement)("tr", null, (0, react.createElement)("th", null, translateWith(props.t, "devices.col.name")), (0, react.createElement)("th", null, translateWith(props.t, "devices.col.lastSeen")), (0, react.createElement)("th", { className: "pbb-tableActionHead" }, translateWith(props.t, "devices.col.actions")))), (0, react.createElement)("tbody", null, visibleDevices$1.map((d) => {
4574
+ const deviceRows = visibleDevices$1.length > 0 ? (0, react.createElement)("div", { className: "pbb-deviceList" }, visibleDevices$1.map((d) => {
4503
4575
  const editing = editingDeviceId === d.deviceId;
4504
4576
  const busy = deviceBusy === d.deviceId;
4505
4577
  const label = deviceLabel$1(d);
4506
- return (0, react.createElement)("tr", {
4578
+ const fingerprint = d.fingerprint ?? "";
4579
+ const armed = armedRevokeDeviceId === d.deviceId;
4580
+ return (0, react.createElement)("div", {
4581
+ className: "pbb-deviceRow",
4507
4582
  key: d.deviceId,
4508
- "aria-busy": busy
4509
- }, (0, react.createElement)("td", null, editing ? (0, react.createElement)("form", {
4583
+ "data-armed": armed || void 0,
4584
+ "aria-busy": busy,
4585
+ "aria-label": translateWith(props.t, "devices.col.name") + ": " + label
4586
+ }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", {
4587
+ className: "pbb-deviceName",
4588
+ title: label
4589
+ }, label), d.customName ? (0, react.createElement)("span", { className: "pbb-badge" }, translateWith(props.t, "devices.customBadge")) : null), (0, react.createElement)("div", { className: "pbb-deviceMeta" }, (0, react.createElement)("span", null, d.appVersion), (0, react.createElement)("code", { title: fingerprint }, fingerprint.slice(0, 12)), (0, react.createElement)("span", null, translateWith(props.t, "devices.col.lastSeen") + ": " + formatLastSeen(d.lastSeenTs)))), editing ? (0, react.createElement)("form", {
4510
4590
  className: "pbb-deviceRenameForm",
4511
4591
  onSubmit: (event) => {
4512
4592
  event.preventDefault();
4513
4593
  saveDeviceName(d.deviceId);
4514
4594
  }
4515
- }, (0, react.createElement)("div", { className: "pbb-deviceRenameControls" }, (0, react.createElement)("input", {
4595
+ }, (0, react.createElement)("input", {
4516
4596
  className: "pbb-deviceNameInput",
4517
4597
  type: "text",
4518
4598
  value: deviceNameDraft,
@@ -4533,21 +4613,21 @@ window.__ModuleLoader__.load({
4533
4613
  }
4534
4614
  }), (0, react.createElement)("div", { className: "pbb-deviceRenameActions" }, (0, react.createElement)("button", {
4535
4615
  type: "submit",
4536
- className: "pbb-action pbb-deviceIconAction pbb-buttonPrimary",
4616
+ className: "pbb-action pbb-buttonPrimary",
4537
4617
  disabled: busy || deviceNameDraft.length > 64,
4538
4618
  title: busy ? translateWith(props.t, "devices.nameSaving") : translateWith(props.t, "devices.nameSave"),
4539
4619
  "aria-label": busy ? translateWith(props.t, "devices.nameSaving") : translateWith(props.t, "devices.nameSave")
4540
- }, busy ? (0, react.createElement)("span", {
4541
- className: "pbb-inlineSpinner",
4542
- "aria-hidden": true
4543
- }) : CheckIcon()), (0, react.createElement)("button", {
4620
+ }, busy ? translateWith(props.t, "devices.nameSaving") : translateWith(props.t, "devices.nameSave")), (0, react.createElement)("button", {
4544
4621
  type: "button",
4545
- className: "pbb-action pbb-deviceIconAction",
4622
+ className: "pbb-action",
4546
4623
  disabled: busy,
4547
4624
  title: translateWith(props.t, "devices.nameCancel"),
4548
4625
  "aria-label": translateWith(props.t, "devices.nameCancel"),
4549
4626
  onClick: cancelDeviceRename
4550
- }, CloseIcon()))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", { className: "pbb-deviceName" }, label), d.customName ? (0, react.createElement)("span", { className: "pbb-badge" }, translateWith(props.t, "devices.customBadge")) : null), (0, react.createElement)("span", { className: "pbb-deviceMeta" }, d.appVersion, " · ", (0, react.createElement)("code", null, (d.fingerprint ?? "").slice(0, 12))))), (0, react.createElement)("td", { className: "pbb-lastSeen" }, new Date(d.lastSeenTs ?? 0).toLocaleString()), (0, react.createElement)("td", { className: "pbb-tableActionCell" }, !editing ? (0, react.createElement)("button", {
4627
+ }, translateWith(props.t, "devices.nameCancel"))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : null, armed ? null : (0, react.createElement)("div", {
4628
+ className: "pbb-deviceActions",
4629
+ "aria-label": translateWith(props.t, "devices.col.actions")
4630
+ }, !editing ? (0, react.createElement)("button", {
4551
4631
  type: "button",
4552
4632
  className: "pbb-action",
4553
4633
  disabled: busy || editingDeviceId !== null || !can("setDeviceName"),
@@ -4558,9 +4638,27 @@ window.__ModuleLoader__.load({
4558
4638
  className: "pbb-action pbb-actionDangerGhost",
4559
4639
  disabled: busy,
4560
4640
  "aria-label": busy && deviceBusyAction === "revoke" ? translateWith(props.t, "devices.deleting", { name: label }) : translateWith(props.t, "devices.revokeAria", { name: label }),
4561
- onClick: () => revokeDevice(d.deviceId, label)
4562
- }, 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"))));
4563
- })))) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
4641
+ onClick: () => {
4642
+ cancelDeviceRename();
4643
+ setArmedRevokeDeviceId(d.deviceId);
4644
+ }
4645
+ }, busy && deviceBusyAction === "revoke" ? null : TrashIcon(), busy && deviceBusyAction === "revoke" ? (0, react.createElement)("span", { className: "pbb-actionBusy" }, translateWith(props.t, "devices.deleting")) : translateWith(props.t, "devices.revoke"))), armed && !busy ? (0, react.createElement)("div", { className: "pbb-deviceConfirm" }, (0, react.createElement)("p", {
4646
+ className: "pbb-diag pbb-diagBad",
4647
+ role: "alert"
4648
+ }, translateWith(props.t, "devices.revokeConfirm", { name: label })), (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
4649
+ type: "button",
4650
+ className: "pbb-action pbb-actionDangerGhost",
4651
+ "aria-label": translateWith(props.t, "devices.revokeAria", { name: label }),
4652
+ onClick: () => {
4653
+ setArmedRevokeDeviceId(null);
4654
+ revokeDevice(d.deviceId, label);
4655
+ }
4656
+ }, translateWith(props.t, "devices.revoke")), (0, react.createElement)("button", {
4657
+ type: "button",
4658
+ className: "pbb-action",
4659
+ onClick: () => setArmedRevokeDeviceId(null)
4660
+ }, translateWith(props.t, "devices.nameCancel")))) : null);
4661
+ })) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
4564
4662
  const switchTitle = translateWith(props.t, "master.title");
4565
4663
  const switchDesc = switchReady ? enabled ? translateWith(props.t, "master.on") : translateWith(props.t, "master.off") : translateWith(props.t, "master.loading");
4566
4664
  const localMeta = report === null ? null : LOCAL_PHASE_META[report.local.phase];
@@ -4573,6 +4671,10 @@ window.__ModuleLoader__.load({
4573
4671
  });
4574
4672
  const serviceStatusText = translateWith(props.t, SERVICE_STATUS_KEY[service.kind]);
4575
4673
  const serviceDot = service.dot;
4674
+ const localPhaseText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, LOCAL_LABEL_KEY[localMeta.label]);
4675
+ const remotePhaseText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, REMOTE_LABEL_KEY[remoteMeta.label]);
4676
+ const localStateText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[localMeta.label]);
4677
+ const remoteStateText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[remoteMeta.label]);
4576
4678
  /** 一条失败事件的单行文本:时间 · 类型 · 分因,附带可选排查字段。 */
4577
4679
  const diagnosticLine = (failure) => {
4578
4680
  const base = translateWith(props.t, "diagnostics.item", {
@@ -4625,17 +4727,98 @@ window.__ModuleLoader__.load({
4625
4727
  className: "pbb-action",
4626
4728
  onClick: copyDiagnostics,
4627
4729
  "data-testid": "diagnosticsCopy"
4628
- }, translateWith(props.t, "diagnostics.copy"))), diagnosticsCopy.message ? (0, react.createElement)("p", { className: "pbb-diag" + (diagnosticsCopy.failed ? " pbb-diagBad" : "") }, diagnosticsCopy.message) : null, report.diagnostics === void 0 ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "diagnostics.staleHost")) : report.diagnostics.recentFailures.length === 0 ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "diagnostics.empty")) : (0, react.createElement)("div", null, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "diagnostics.recent")), report.diagnostics.recentFailures.slice(-5).reverse().map((failure, index) => (0, react.createElement)("p", {
4730
+ }, translateWith(props.t, "diagnostics.copy"))), diagnosticsCopy.message ? (0, react.createElement)("p", {
4731
+ className: "pbb-diag" + (diagnosticsCopy.failed ? " pbb-diagBad" : ""),
4732
+ role: "status"
4733
+ }, diagnosticsCopy.message) : null, report.diagnostics === void 0 ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "diagnostics.staleHost")) : report.diagnostics.recentFailures.length === 0 ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "diagnostics.empty")) : (0, react.createElement)("div", null, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "diagnostics.recent")), report.diagnostics.recentFailures.slice(-5).reverse().map((failure, index) => (0, react.createElement)("p", {
4629
4734
  className: "pbb-diag",
4630
4735
  key: String(index)
4631
4736
  }, diagnosticLine(failure)))));
4737
+ /** 状态总览的一格:标签 / 点位 + 值 / 次行;整格 title 兜住被裁掉的全文。
4738
+ * dot 为 null 表示这一格是计数而非状态,不画点位(灰点会被读成「未启用」)。 */
4739
+ const statusCell = (label, dot, value, sub) => (0, react.createElement)("div", {
4740
+ className: "pbb-statusCell",
4741
+ title: label + " · " + value + (sub === "" ? "" : " · " + sub)
4742
+ }, (0, react.createElement)("div", { className: "pbb-statusLabel" }, label), (0, react.createElement)("div", { className: "pbb-statusValue" }, dot === null ? null : (0, react.createElement)("span", { className: "pbb-dot" + dot }), value), (0, react.createElement)("div", { className: "pbb-statusSub" }, sub));
4743
+ /** 折叠区:grid-rows 0fr→1fr 平滑开合(mockup 的做法),不用 <details> 的瞬时跳变。 */
4744
+ const fold = (open, body) => (0, react.createElement)("div", { className: "pbb-fold" + (open ? "" : " pbb-foldClosed") }, (0, react.createElement)("div", null, body));
4745
+ /** 配对倒计时:仅在 QR 面板打开且 grant 在场时每秒刷新一次。 */
4746
+ (0, react.useEffect)(() => {
4747
+ if (!panel.open || pairingPanel.grant === null) return;
4748
+ const timer = globalThis.setInterval(() => setNowMs(Date.now()), 1e3);
4749
+ return () => globalThis.clearInterval(timer);
4750
+ }, [panel.open, pairingPanel.grant]);
4751
+ const grantRemainingMs = pairingPanel.grant === null ? 0 : Math.max(0, pairingPanel.grant.expiresAt - nowMs);
4752
+ const grantRemainingText = String(Math.floor(grantRemainingMs / 6e4)) + ":" + String(Math.floor(grantRemainingMs / 1e3) % 60).padStart(2, "0");
4753
+ const grantRemainingPct = Math.min(100, grantRemainingMs / 3e5 * 100);
4754
+ const localDesc = localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading");
4755
+ const remoteDesc = remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading");
4756
+ /** 端点 → 状态条副行只暴露 host[:port],去掉协议前缀让一行能放下。 */
4757
+ const endpointHost = (value) => {
4758
+ if (value === void 0 || value === "") return void 0;
4759
+ try {
4760
+ return new URL(value).host;
4761
+ } catch {
4762
+ return value;
4763
+ }
4764
+ };
4765
+ const localStatusSub = !localSwitchReady ? translateWith(props.t, "master.loading") : report !== null && report.local.phase === "online" ? endpointHost(report.local.endpoints[0]) ?? localDesc : report !== null && report.local.phase === "error" ? report.local.message !== void 0 && report.local.message !== "" ? report.local.message : localDesc : report !== null && (report.local.phase === "disabled" || report.local.phase === "stopped") ? translateWith(props.t, "local.off") : localDesc;
4766
+ const remoteStatusSub = !remoteSwitchReady ? translateWith(props.t, "master.loading") : report !== null && report.remote.phase === "online" ? endpointHost(report.remote.publicURL) ?? remoteDesc : report !== null && report.remote.phase === "error" ? report.remote.message !== void 0 && report.remote.message !== "" ? report.remote.message : remoteDesc : report !== null && report.remote.phase === "disabled" ? translateWith(props.t, "remote.off") : remoteDesc;
4767
+ const isOffPhase = (phase) => phase === "disabled" || phase === "stopped";
4768
+ const localSettled = report === null || (localEnabled ? report.local.phase === "online" : isOffPhase(report.local.phase));
4769
+ const remoteSettled = report === null || (remoteEnabled ? report.remote.phase === "online" : isOffPhase(report.remote.phase));
4770
+ const localChannelDesc = localSettled ? localDesc : localPhaseText + " · " + localDesc;
4771
+ const remoteChannelDesc = remoteSettled ? remoteDesc : remotePhaseText + " · " + remoteDesc;
4772
+ const recentFailures = report?.diagnostics?.recentFailures ?? [];
4773
+ const troubleGood = relayTestResult !== null ? relayTestResult.overall === "ok" : pushTestResult !== null ? pushTestResult.overall === "sent" : false;
4774
+ const troubleBad = relayTestResult !== null ? relayTestResult.overall !== "ok" : pushTestResult !== null ? pushTestResult.overall !== "sent" : recentFailures.length > 0;
4775
+ const troubleConclusion = relayTestResult !== null ? relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad") : pushTestResult !== null ? pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : translateWith(props.t, "push.pushFailed") : recentFailures.length > 0 ? translateWith(props.t, "diagnostics.recent") + " " + String(recentFailures.length) : translateWith(props.t, "troubleshooting.title");
4632
4776
  return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
4633
4777
  type: "button",
4634
4778
  className: "pbb-refresh",
4635
4779
  onClick: () => {
4636
4780
  if (can("refresh")) props.refresh();
4637
4781
  }
4638
- }, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("div", { className: "pbb-card pbb-primaryCard" }, (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)("span", { className: "pbb-statusBadge" }, (0, react.createElement)("span", { className: "pbb-dot" + serviceDot }), serviceStatusText)), (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", {
4782
+ }, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("section", { className: "pbb-statusStrip" }, statusCell(translateWith(props.t, "master.title"), serviceDot, serviceStatusText, switchDesc), statusCell(translateWith(props.t, "local.title"), localMeta?.dot ?? "", localStateText, localStatusSub), statusCell(translateWith(props.t, "remote.title"), remoteMeta?.dot ?? "", remoteStateText, remoteStatusSub), statusCell(translateWith(props.t, "panel.activeConnections"), null, report === null ? translateWith(props.t, "status.loading") : String(report.activeConnections), translateWith(props.t, "panel.identity") + " · " + (report !== null && report.pairingReady ? translateWith(props.t, "panel.identityReady") : translateWith(props.t, "panel.identityNotReady")))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "pair.qrPanelTitle")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "pair.description"))), pairingTarget === null ? (0, react.createElement)("span", { className: "pbb-statusBadge" + (enabled ? " pbb-bad" : "") }, enabled ? translateWith(props.t, "pair.noAddress") : translateWith(props.t, "status.off")) : (0, react.createElement)("button", {
4783
+ type: "button",
4784
+ className: "pbb-action pbb-buttonPrimary",
4785
+ disabled: panel.busy || report !== null && !report.pairingReady,
4786
+ onClick: () => {
4787
+ if (pairingTarget === null) return;
4788
+ if (panel.open) hidePairingQR();
4789
+ else showPairingQR(pairingTarget);
4790
+ }
4791
+ }, translateWith(props.t, panel.action))), (0, react.createElement)("div", { className: "pbb-pairingBody" }, pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag" }, enabled ? translateWith(props.t, "pair.noAddressHelp") : translateWith(props.t, "pair.disabledHelp")) : null, pairingTargets.length < 2 ? null : (0, react.createElement)("div", { className: "pbb-targetList" }, pairingTargets.map((target) => (0, react.createElement)("button", {
4792
+ type: "button",
4793
+ key: target.host,
4794
+ className: "pbb-targetButton" + (target.host === pairingTarget?.host ? " pbb-targetSelected" : ""),
4795
+ disabled: panel.busy,
4796
+ onClick: () => {
4797
+ setSelectedPairingHost(target.host);
4798
+ if (panel.open) showPairingQR(target);
4799
+ else hidePairingQR();
4800
+ }
4801
+ }, (target.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan")) + " · " + target.host))), panel.message ? (0, react.createElement)("p", {
4802
+ className: "pbb-diag pbb-diagBad",
4803
+ role: "alert"
4804
+ }, panel.message) : null, panel.notice ? (0, react.createElement)("p", {
4805
+ className: "pbb-diag",
4806
+ role: "status"
4807
+ }, panel.notice) : null, panel.failed && pairingTarget !== null ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
4808
+ type: "button",
4809
+ className: "pbb-action",
4810
+ disabled: panel.busy || report !== null && !report.pairingReady,
4811
+ onClick: () => showPairingQR(pairingTarget)
4812
+ }, translateWith(props.t, "pair.qrRetry"))) : null, panel.open && !panel.failed ? (0, react.createElement)("div", { className: "pbb-qrPanel" }, (0, react.createElement)("div", null, panel.qrDataURL === null ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "pair.qrGenerating")) : (0, react.createElement)("img", {
4813
+ className: "pbb-qrImage",
4814
+ src: panel.qrDataURL,
4815
+ alt: translateWith(props.t, "pair.qrAlt")
4816
+ }), pairingPanel.grant === null ? null : (0, react.createElement)("div", { className: "pbb-countdown" }, (0, react.createElement)("div", { className: "pbb-countdownBar" }, (0, react.createElement)("i", { style: { transform: "scaleX(" + String(grantRemainingPct / 100) + ")" } })), (0, react.createElement)("span", { className: "pbb-countdownText" }, grantRemainingText))), (0, react.createElement)("div", { className: "pbb-pairInfo" }, panel.link === null ? null : (0, react.createElement)("div", { className: "pbb-pairCodeBlock" }, (0, react.createElement)("span", { className: "pbb-pairCodeLabel" }, translateWith(props.t, "pair.infoLabel")), (0, react.createElement)("div", { className: "pbb-pairCodeRow" }, (0, react.createElement)("code", { className: "pbb-pairCode" }, panel.link), (0, react.createElement)("button", {
4817
+ type: "button",
4818
+ className: "pbb-action pbb-buttonPrimary",
4819
+ onClick: copyPairingInfo,
4820
+ "data-testid": "pairingInfoCopy"
4821
+ }, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") })))) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "connection.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "connection.description")))), (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-masterRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, switchTitle), (0, react.createElement)("span", { className: "pbb-switchDesc" }, switchDesc)), (0, react.createElement)("button", {
4639
4822
  type: "button",
4640
4823
  role: "switch",
4641
4824
  "aria-checked": enabled,
@@ -4645,16 +4828,22 @@ window.__ModuleLoader__.load({
4645
4828
  onClick: () => {
4646
4829
  if (can("setEnabled")) props.setDeepPilotEnabled(!enabled);
4647
4830
  }
4648
- })), (0, react.createElement)("div", { className: "pbb-connectionGrid" }, renderConnectionTile("local", translateWith(props.t, "local.title"), localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading"), localEnabled, localSwitchReady, localMeta?.dot ?? "", localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, LOCAL_LABEL_KEY[localMeta.label]), () => {
4649
- if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
4650
- }, (0, react.createElement)(react.Fragment, null, 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)), renderConnectionTile("remote", translateWith(props.t, "remote.title"), remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading"), remoteEnabled, remoteSwitchReady, remoteMeta?.dot ?? "", remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, REMOTE_LABEL_KEY[remoteMeta.label]), () => {
4651
- if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
4652
- }, (0, react.createElement)(react.Fragment, null, 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", {
4653
- className: "pbb-action pbb-buttonPrimary",
4654
- href: report.remote.authURL,
4655
- target: "_blank",
4656
- rel: "noreferrer"
4657
- }, 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))), report === null ? null : (0, react.createElement)("div", { className: "pbb-statGrid" }, primaryRows), (0, react.createElement)("details", { className: "pbb-inlineDetails" }, (0, react.createElement)("summary", null, translateWith(props.t, "connection.optionsSummary")), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-limitRow pbb-limitNested" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4831
+ })), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, (0, react.createElement)("span", { className: "pbb-dot" + (localMeta?.dot ?? "") }), " ", translateWith(props.t, "local.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, localChannelDesc)), (0, react.createElement)("button", {
4832
+ type: "button",
4833
+ role: "switch",
4834
+ "aria-checked": localEnabled,
4835
+ "aria-label": translateWith(props.t, "local.title"),
4836
+ disabled: !localSwitchReady,
4837
+ className: "pbb-switch" + (localEnabled ? " pbb-switchOn" : ""),
4838
+ onClick: () => {
4839
+ if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
4840
+ }
4841
+ })), report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.local.message) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null, (0, react.createElement)("button", {
4842
+ type: "button",
4843
+ className: "pbb-expand",
4844
+ "aria-expanded": localOptionsOpen,
4845
+ onClick: () => setLocalOptionsOpen(!localOptionsOpen)
4846
+ }, translateWith(props.t, "connection.optionsSummary"), " ", (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(localOptionsOpen, (0, react.createElement)("div", { className: "pbb-channelBody" }, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4658
4847
  className: "pbb-switchTitle",
4659
4848
  htmlFor: "deeppilot-local-port"
4660
4849
  }, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "local.portDescription")), !localPortView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.portInvalid")) : localPortDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (localPortDraft.failed ? " pbb-diagBad" : "") }, localPortDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
@@ -4683,7 +4872,27 @@ window.__ModuleLoader__.load({
4683
4872
  className: "pbb-action",
4684
4873
  disabled: !localPortReady || !localPortView.valid || localPortView.unchanged,
4685
4874
  onClick: applyLocalPort
4686
- }, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("div", { className: "pbb-limitRow pbb-limitNested" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4875
+ }, translateWith(props.t, "remote.limitApply")))), report !== null && report.local.phase === "online" && report.local.tlsFingerprint ? (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, translateWith(props.t, "local.tlsFingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, report.local.tlsFingerprint))) : null))), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, (0, react.createElement)("span", { className: "pbb-dot" + (remoteMeta?.dot ?? "") }), " ", translateWith(props.t, "remote.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, remoteChannelDesc)), (0, react.createElement)("button", {
4876
+ type: "button",
4877
+ role: "switch",
4878
+ "aria-checked": remoteEnabled,
4879
+ "aria-label": translateWith(props.t, "remote.title"),
4880
+ disabled: !remoteSwitchReady,
4881
+ className: "pbb-switch" + (remoteEnabled ? " pbb-switchOn" : ""),
4882
+ onClick: () => {
4883
+ if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
4884
+ }
4885
+ })), report !== null && report.remote.phase === "login_required" && typeof report.remote.authURL === "string" && report.remote.authURL.startsWith("https://") ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("a", {
4886
+ className: "pbb-action pbb-buttonPrimary",
4887
+ href: report.remote.authURL,
4888
+ target: "_blank",
4889
+ rel: "noreferrer"
4890
+ }, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.remote.message) : null, (0, react.createElement)("button", {
4891
+ type: "button",
4892
+ className: "pbb-expand",
4893
+ "aria-expanded": remoteOptionsOpen,
4894
+ onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4895
+ }, translateWith(props.t, "connection.optionsSummary"), " ", (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(remoteOptionsOpen, (0, react.createElement)("div", { className: "pbb-channelBody" }, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4687
4896
  className: "pbb-switchTitle",
4688
4897
  htmlFor: "deeppilot-funnel-source-limit"
4689
4898
  }, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "remote.limitDescription")), !remoteLimitView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "remote.limitInvalid")) : remoteLimitDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (remoteLimitDraft.failed ? " pbb-diagBad" : "") }, remoteLimitDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
@@ -4712,48 +4921,20 @@ window.__ModuleLoader__.load({
4712
4921
  className: "pbb-action",
4713
4922
  disabled: !remoteConnectionLimitReady || !remoteLimitView.valid || remoteLimitView.unchanged,
4714
4923
  onClick: applyRemoteLimit
4715
- }, translateWith(props.t, "remote.limitApply")))))), (0, react.createElement)("details", { className: "pbb-help" }, (0, react.createElement)("summary", null, 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", {
4924
+ }, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.remoteTitle")), (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.recommended")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.step1")), (0, react.createElement)("li", null, translateWith(props.t, "help.step2")), (0, react.createElement)("li", null, translateWith(props.t, "help.step3")), (0, react.createElement)("li", null, translateWith(props.t, "help.step4"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.funnelHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.httpsTitle")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep1")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep2")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep3"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.httpsHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.allowTitle")), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowBody")), (0, react.createElement)("code", { className: "pbb-helpCode" }, "\"nodeAttrs\": [\n {\n \"target\": [\"autogroup:member\"],\n \"attr\": [\"funnel\"],\n },\n],"), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.faqTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.faq1")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq2")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq3")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq4")))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.securityTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.security1")), (0, react.createElement)("li", null, translateWith(props.t, "help.security2")), (0, react.createElement)("li", null, translateWith(props.t, "help.security3")), (0, react.createElement)("li", null, translateWith(props.t, "help.security4")), (0, react.createElement)("li", null, translateWith(props.t, "help.security5"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.docsPrefix"), (0, react.createElement)("a", {
4716
4925
  className: "pbb-helpLink",
4717
4926
  href: "https://tailscale.com/docs/features/tailscale-funnel",
4718
4927
  target: "_blank",
4719
4928
  rel: "noreferrer"
4720
- }, translateWith(props.t, "help.funnelDocs"))))))), 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 pbb-bad" }, translateWith(props.t, "pair.noAddress")) : (0, react.createElement)("button", {
4929
+ }, translateWith(props.t, "help.funnelDocs")))))))))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "devices.description"))), (0, react.createElement)("span", { className: "pbb-countBadge" }, String(visibleDevices$1.length))), (0, react.createElement)("div", { className: "pbb-cardBody" }, deviceMessage ? (0, react.createElement)("p", {
4930
+ className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4931
+ role: "status"
4932
+ }, deviceMessage) : null, deviceRows)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
4721
4933
  type: "button",
4722
- className: "pbb-action pbb-buttonPrimary",
4723
- disabled: panel.busy || !report.pairingReady,
4724
- onClick: () => {
4725
- if (panel.open) hidePairingQR();
4726
- else showPairingQR(pairingTarget);
4727
- }
4728
- }, translateWith(props.t, panel.action))), (0, react.createElement)("div", { className: "pbb-pairingBody" }, pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "pair.noAddressHelp")) : null, pairingTargets.length < 2 ? null : (0, react.createElement)("div", { className: "pbb-targetList" }, pairingTargets.map((target) => (0, react.createElement)("button", {
4729
- type: "button",
4730
- key: target.host,
4731
- className: "pbb-targetButton" + (target.host === pairingTarget?.host ? " pbb-targetSelected" : ""),
4732
- disabled: panel.busy,
4733
- onClick: () => {
4734
- setSelectedPairingHost(target.host);
4735
- if (panel.open) showPairingQR(target);
4736
- else hidePairingQR();
4737
- }
4738
- }, (target.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan")) + " · " + target.host))), panel.message ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, panel.message) : null, panel.notice ? (0, react.createElement)("p", { className: "pbb-diag" }, panel.notice) : null, panel.failed && pairingTarget !== null ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
4739
- type: "button",
4740
- className: "pbb-action",
4741
- disabled: panel.busy || !report.pairingReady,
4742
- onClick: () => showPairingQR(pairingTarget)
4743
- }, translateWith(props.t, "pair.qrRetry"))) : null, panel.open ? (0, react.createElement)("div", { className: "pbb-qrPanel" }, panel.qrDataURL === null ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "pair.qrGenerating")) : (0, react.createElement)("img", {
4744
- className: "pbb-qrImage",
4745
- src: panel.qrDataURL,
4746
- alt: translateWith(props.t, "pair.qrAlt")
4747
- }), panel.link === null ? null : (0, react.createElement)("div", { className: "pbb-pairCodeBlock" }, (0, react.createElement)("span", { className: "pbb-pairCodeLabel" }, translateWith(props.t, "pair.infoLabel")), (0, react.createElement)("div", { className: "pbb-pairCodeRow" }, (0, react.createElement)("code", { className: "pbb-pairCode" }, panel.link), (0, react.createElement)("button", {
4748
- type: "button",
4749
- className: "pbb-action pbb-buttonPrimary",
4750
- onClick: copyPairingInfo,
4751
- "data-testid": "pairingInfoCopy"
4752
- }, 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, "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", { className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : "") }, deviceMessage) : null, deviceTable)), (0, react.createElement)("details", {
4753
- className: "pbb-card pbb-disclosure",
4754
- open: troubleshootingOpen,
4755
- onToggle: (event) => setTroubleshootingOpen(event.currentTarget.open)
4756
- }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-disclosureTitle" }, translateWith(props.t, "troubleshooting.summary")), (0, react.createElement)("span", { className: "pbb-disclosureMeta" }, 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") : translateWith(props.t, "troubleshooting.title"))), (0, react.createElement)("div", { className: "pbb-disclosureBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "troubleshooting.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "troubleshooting.description"))), (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", {
4934
+ className: "pbb-cardHead",
4935
+ "aria-expanded": troubleshootingOpen,
4936
+ onClick: () => setTroubleshootingOpen(!troubleshootingOpen)
4937
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "troubleshooting.summary")), (0, react.createElement)("span", { className: "pbb-cardSummary" }, translateWith(props.t, "troubleshooting.description")), (0, react.createElement)("span", { className: "pbb-badge" + (troubleGood ? " pbb-badgeOk" : troubleBad ? " pbb-badgeWarn" : "") }, troubleConclusion)), fold(troubleshootingOpen, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-testGrid" }, (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.relayTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.relayDefault"))), (0, react.createElement)("button", {
4757
4938
  type: "button",
4758
4939
  className: "pbb-action",
4759
4940
  disabled: relayTestBusy,
@@ -4773,21 +4954,26 @@ window.__ModuleLoader__.load({
4773
4954
  onClick: () => {
4774
4955
  if (can("setDebug")) props.setDeepPilotDebug(!debugEnabled);
4775
4956
  }
4776
- }))), relayTestError ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, relayTestError) : null, relayTestResult === null ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "push.relayDefault")) : (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) => (0, react.createElement)("p", {
4777
- className: "pbb-diag",
4778
- key: String(index)
4779
- }, (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))), pushTestError ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, pushTestError) : null, pushTestResult === null ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "push.pushDefault")) : (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) => (0, react.createElement)("p", {
4780
- className: "pbb-diag",
4781
- key: String(index)
4782
- }, (r.outcome === "sent" ? "✓ " : "✗ ") + r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : "")))), diagnosticsSection)), report === null ? null : (0, react.createElement)("details", { className: "pbb-card pbb-disclosure" }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-disclosureTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-disclosureMeta" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-disclosureBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "system.description"))), (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)), advancedRows, report.updateAvailable === true ? (0, react.createElement)("div", { className: "pbb-updateRow" }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("a", {
4783
- className: "pbb-action pbb-buttonPrimary",
4957
+ }))), relayTestError ? (0, react.createElement)("p", {
4958
+ className: "pbb-diag pbb-diagBad",
4959
+ role: "alert"
4960
+ }, relayTestError) : null, relayTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || translateWith(props.t, "push.relayUrlEmpty")), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad"))), relayTestResult.steps.map((step, index) => resultRow(step.ok, (step.id === "health" ? translateWith(props.t, "push.relayStep.health") : translateWith(props.t, "push.relayStep.enroll")) + (step.latencyMs !== void 0 ? ` (${String(step.latencyMs)}ms)` : "") + " — " + step.message, String(index)))), pushTestError ? (0, react.createElement)("p", {
4961
+ className: "pbb-diag pbb-diagBad",
4962
+ role: "alert"
4963
+ }, pushTestError) : null, pushTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token " + (pushTestResult.overall === "sent" ? "pbb-ok" : pushTestResult.overall === "failed" ? "pbb-bad" : "") }, pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : pushTestResult.overall === "failed" ? translateWith(props.t, "push.pushFailed") : pushTestResult.overall === "no-targets" ? translateWith(props.t, "push.pushNoTargets") : translateWith(props.t, "push.pushNotEnabled"))), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, pushTestResult.message) : null, pushTestResult.results.map((r, index) => resultRow(r.outcome === "sent", r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : ""), String(index)))), diagnosticsSection))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
4964
+ type: "button",
4965
+ className: "pbb-cardHead",
4966
+ "aria-expanded": systemOpen,
4967
+ onClick: () => setSystemOpen(!systemOpen)
4968
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-cardSummary" }, translateWith(props.t, "system.description")), (0, react.createElement)("span", { className: "pbb-badge" + (report.updateAvailable === true ? " pbb-badgeOk" : "") }, "v" + report.pluginVersion + (report.updateAvailable === true ? " · " + translateWith(props.t, "update.badge") : ""))), fold(systemOpen, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.tailscaleVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.remote.tailscaleVersion ?? translateWith(props.t, "advanced.tailscaleVersionUnknown"))), advancedRows, report.updateAvailable === true ? (0, react.createElement)("a", {
4969
+ className: "pbb-updateRow",
4784
4970
  href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
4785
4971
  target: "_blank",
4786
4972
  rel: "noreferrer"
4787
- }, translateWith(props.t, "update.badge"))) : null))), diag.length > 0 ? (0, react.createElement)("details", {
4973
+ }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())) : null)))), diag.length > 0 ? (0, react.createElement)("details", {
4788
4974
  className: "pbb-card pbb-disclosure pbb-diagnostics",
4789
4975
  open: failed
4790
- }, (0, react.createElement)("summary", null, 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);
4976
+ }, (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);
4791
4977
  }
4792
4978
  //#endregion
4793
4979
  //#region src/client/config-form.ts