dsh-deeppilot 0.9.11 → 0.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4,14 +4,14 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- //#region \0rolldown/runtime.js
7
+ // \0rolldown/runtime.js
8
8
  var __create = Object.create;
9
9
  var __defProp = Object.defineProperty;
10
10
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
11
  var __getOwnPropNames = Object.getOwnPropertyNames;
12
12
  var __getProtoOf = Object.getPrototypeOf;
13
13
  var __hasOwnProp = Object.prototype.hasOwnProperty;
14
- var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
14
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
15
15
  var __copyProps = (to, from, except, desc) => {
16
16
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
17
17
  key = keys[i];
@@ -26,9 +26,8 @@ window.__ModuleLoader__.load({
26
26
  value: mod,
27
27
  enumerable: true
28
28
  }) : target, mod));
29
- //#endregion
30
29
  let react = require("react");
31
- //#region src/client/snapshot-store.ts
30
+ // src/client/snapshot-store.ts
32
31
  function createSnapshotStore(initial) {
33
32
  let snapshot = initial;
34
33
  const listeners = /* @__PURE__ */ new Set();
@@ -45,8 +44,30 @@ window.__ModuleLoader__.load({
45
44
  }
46
45
  };
47
46
  }
48
- //#endregion
49
- //#region src/device-auth.ts
47
+ // src/tailscale-version.ts
48
+ /**
49
+ * 内嵌 helper 上报的 tailscale.com 模块版本。
50
+ *
51
+ * 版本的唯一来源是 helper 启动事件(`helper/main.go` 读 Go 构建信息里的
52
+ * `tailscale.com` 依赖版本),不是 go.mod、也不是插件常量:用户可能通过
53
+ * `remote.helperPath` 换成自己编译的 helper,界面必须显示实际在跑的那一个。
54
+ *
55
+ * 两条边界都要守卫:
56
+ * - 宿主侧 `parseHelperEvent` 把 helper 的 stdout 当成不可信输入,格式不对
57
+ * 就丢弃,避免把任意文本渲染进设置页;
58
+ * - 设置页侧 `reportSchema` 同样校验 Host RPC 返回值,旧 Host 不带该字段
59
+ * 时保持 undefined(页面显示「未获取」),不当作错误。
60
+ *
61
+ * 接受 Go 模块版本形态:`v1.104.2`、`v1.104.2-pre.1`、
62
+ * `v0.0.0-20260101000000-abcdef123456`。长度上限防止异常输入撑爆界面。
63
+ */
64
+ const TAILSCALE_VERSION_PATTERN = /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]{1,64})?$/;
65
+ const MAX_TAILSCALE_VERSION_LENGTH = 80;
66
+ /** 判断外部输入是否是合法的 Tailscale 模块版本字符串。 */
67
+ function isTailscaleVersion(value) {
68
+ return typeof value === "string" && value.length <= MAX_TAILSCALE_VERSION_LENGTH && TAILSCALE_VERSION_PATTERN.test(value);
69
+ }
70
+ // src/device-auth.ts
50
71
  const DEVICE_SCOPES = [
51
72
  "sessions.read",
52
73
  "prompt.send",
@@ -61,8 +82,7 @@ window.__ModuleLoader__.load({
61
82
  const allowed = new Set(DEVICE_SCOPES);
62
83
  return [...new Set(value.filter((scope) => typeof scope === "string" && allowed.has(scope)))];
63
84
  }
64
- //#endregion
65
- //#region src/report-wire.ts
85
+ // src/report-wire.ts
66
86
  /** The npm package identity both contribution registrations claim. */
67
87
  const REPORT_REMOTE_PACKAGE = "dsh-deeppilot";
68
88
  /** Canonical `<namespace>/<method>` endpoint of the report Remote. */
@@ -144,15 +164,18 @@ window.__ModuleLoader__.load({
144
164
  const publicURL = s.publicURL;
145
165
  const authURL = s.authURL;
146
166
  const message = s.message;
167
+ const tailscaleVersion = s.tailscaleVersion;
147
168
  if (publicURL !== void 0 && typeof publicURL !== "string") reject("remote.publicURL");
148
169
  if (authURL !== void 0 && typeof authURL !== "string") reject("remote.authURL");
149
170
  if (message !== void 0 && typeof message !== "string") reject("remote.message");
171
+ if (tailscaleVersion !== void 0 && !isTailscaleVersion(tailscaleVersion)) reject("remote.tailscaleVersion");
150
172
  return {
151
173
  provider,
152
174
  phase,
153
175
  ...typeof publicURL === "string" ? { publicURL } : {},
154
176
  ...typeof authURL === "string" ? { authURL } : {},
155
177
  ...typeof message === "string" ? { message } : {},
178
+ ...typeof tailscaleVersion === "string" ? { tailscaleVersion } : {},
156
179
  updatedAt: int(s, "updatedAt", "remote.updatedAt")
157
180
  };
158
181
  }
@@ -437,8 +460,7 @@ window.__ModuleLoader__.load({
437
460
  }
438
461
  ]
439
462
  };
440
- //#endregion
441
- //#region src/client/report-mount.ts
463
+ // src/client/report-mount.ts
442
464
  /** Mount the contribution and resolve the namespace service it installs. */
443
465
  async function mountReportRemote(remote, resolveNamespace) {
444
466
  const dispose = await remote.$mount(REPORT_REMOTE_CONTRIBUTION);
@@ -452,8 +474,7 @@ window.__ModuleLoader__.load({
452
474
  dispose
453
475
  };
454
476
  }
455
- //#endregion
456
- //#region src/client/i18n.ts
477
+ // src/client/i18n.ts
457
478
  /** Single namespace shared with the host's locale registry, so other
458
479
  * host-side modules (if any) can translate the same keys. */
459
480
  const DEEPPILOT_LOCALE_NS = "settings.deeppilot";
@@ -555,6 +576,13 @@ window.__ModuleLoader__.load({
555
576
  "phase.unavailable": "远程 helper 不可用",
556
577
  "phase.stopped": "已停止",
557
578
  "phase.unknown": "状态未知",
579
+ "channel.state.disabled": "未启用",
580
+ "channel.state.starting": "正在启动",
581
+ "channel.state.login_required": "待授权",
582
+ "channel.state.online": "已就绪",
583
+ "channel.state.error": "失败",
584
+ "channel.state.unavailable": "不可用",
585
+ "channel.state.stopped": "已停止",
558
586
  "panel.activeConnections": "当前连接",
559
587
  "panel.identity": "设备身份认证",
560
588
  "panel.identityReady": "v2 已就绪",
@@ -582,6 +610,7 @@ window.__ModuleLoader__.load({
582
610
  "pair.kind.lan": "内网",
583
611
  "pair.noAddress": "没有可用地址",
584
612
  "pair.noAddressHelp": "没有可用的局域网或远程入口。请确认局域网连接已启动,或启用远程连接。",
613
+ "pair.disabledHelp": "DeepPilot 连接已关闭。先在上方开启连接,再添加手机。",
585
614
  "pair.qrAlt": "DeepPilot 配对二维码",
586
615
  "pair.qrShow": "显示二维码",
587
616
  "pair.qrHide": "隐藏二维码",
@@ -596,6 +625,8 @@ window.__ModuleLoader__.load({
596
625
  "advanced.summary": "高级信息",
597
626
  "advanced.protocolVersion": "协议版本",
598
627
  "advanced.serverVersion": "服务器版本",
628
+ "advanced.tailscaleVersion": "Tailscale 版本",
629
+ "advanced.tailscaleVersionUnknown": "未获取",
599
630
  "advanced.tokenPath": "Token 路径",
600
631
  "advanced.identityPath": "设备注册表路径",
601
632
  "advanced.bufferMax": "重放缓冲上限",
@@ -798,6 +829,13 @@ window.__ModuleLoader__.load({
798
829
  "phase.unavailable": "Remote helper unavailable",
799
830
  "phase.stopped": "Stopped",
800
831
  "phase.unknown": "Status unknown",
832
+ "channel.state.disabled": "Off",
833
+ "channel.state.starting": "Starting",
834
+ "channel.state.login_required": "Needs sign-in",
835
+ "channel.state.online": "Ready",
836
+ "channel.state.error": "Failed",
837
+ "channel.state.unavailable": "Unavailable",
838
+ "channel.state.stopped": "Stopped",
801
839
  "panel.activeConnections": "Active connections",
802
840
  "panel.identity": "Device authentication",
803
841
  "panel.identityReady": "v2 ready",
@@ -825,6 +863,7 @@ window.__ModuleLoader__.load({
825
863
  "pair.kind.lan": "LAN",
826
864
  "pair.noAddress": "No address available",
827
865
  "pair.noAddressHelp": "No LAN or remote endpoint is available. Make sure LAN is running or enable remote access.",
866
+ "pair.disabledHelp": "DeepPilot is turned off. Turn the connection on above before adding a phone.",
828
867
  "pair.qrAlt": "DeepPilot pairing QR code",
829
868
  "pair.qrShow": "Show QR code",
830
869
  "pair.qrHide": "Hide QR code",
@@ -839,6 +878,8 @@ window.__ModuleLoader__.load({
839
878
  "advanced.summary": "Advanced info",
840
879
  "advanced.protocolVersion": "Protocol version",
841
880
  "advanced.serverVersion": "Server version",
881
+ "advanced.tailscaleVersion": "Tailscale version",
882
+ "advanced.tailscaleVersionUnknown": "Not reported",
842
883
  "advanced.tokenPath": "Token path",
843
884
  "advanced.identityPath": "Device registry path",
844
885
  "advanced.bufferMax": "Replay buffer cap",
@@ -1019,157 +1060,170 @@ window.__ModuleLoader__.load({
1019
1060
  en: TABLES.en
1020
1061
  });
1021
1062
  }
1022
- //#endregion
1023
- //#region src/client/styles.ts
1063
+ // src/client/styles.ts
1064
+ const EASE = "cubic-bezier(0.22,1,0.36,1)";
1024
1065
  const CSS = [
1025
- ".pbb-section{max-width:760px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:10px}",
1066
+ ".pbb-section{container:pbb/inline-size;max-width:760px;color:var(--dsw-alias-label-primary);font-size:14px;line-height:1.5;display:flex;flex-direction:column;gap:16px}",
1067
+ ".pbb-section>*{animation:pbb-cardIn 160ms " + EASE + " backwards}",
1068
+ ".pbb-section>*:nth-child(2){animation-delay:24ms}",
1069
+ ".pbb-section>*:nth-child(3){animation-delay:48ms}",
1070
+ ".pbb-section>*:nth-child(4){animation-delay:72ms}",
1071
+ ".pbb-section>*:nth-child(5){animation-delay:96ms}",
1072
+ ".pbb-section>*:nth-child(6){animation-delay:120ms}",
1073
+ ".pbb-section>*:nth-child(7){animation-delay:144ms}",
1074
+ ".pbb-section>*:nth-child(n+8){animation-delay:144ms}",
1075
+ "@keyframes pbb-cardIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}",
1026
1076
  ".pbb-pageHeader{display:flex;align-items:flex-start;gap:16px;padding:2px 2px 4px}",
1027
1077
  ".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)}",
1078
+ ".pbb-intro{color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1.5;margin:4px 0 0;max-width:600px}",
1079
+ ".pbb-refresh{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:none;padding:3px 0;margin-left:auto;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + "}",
1080
+ ".pbb-refresh:active{transform:scale(0.97)}",
1031
1081
  ".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}",
1082
+ ".pbb-statusStrip{display:grid;grid-template-columns:repeat(4,1fr);background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2));border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4));border-radius:var(--dsw-radius-xl,20px);overflow:hidden}",
1083
+ ".pbb-statusCell{padding:14px 16px;min-width:0}",
1084
+ ".pbb-statusCell+.pbb-statusCell{border-left:1px solid var(--dsw-alias-border-l2)}",
1085
+ ".pbb-statusLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
1086
+ ".pbb-statusValue{margin-top:2px;font-size:16px;font-weight:600;line-height:22px;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
1087
+ ".pbb-statusValue .pbb-dot{margin-right:6px}",
1088
+ ".pbb-statusSub{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}",
1089
+ ".pbb-card{border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4));background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2));border-radius:var(--dsw-radius-xl,20px);overflow:hidden}",
1033
1090
  ".pbb-cardHeader{display:flex;align-items:flex-start;gap:16px;padding:15px 18px 13px}",
1034
1091
  ".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}",
1092
+ ".pbb-cardTitle{margin:0;font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1093
+ ".pbb-cardDescription{margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
1094
+ ".pbb-cardHead{display:flex;align-items:center;gap:10px;width:100%;min-height:52px;padding:0 18px;border:0;background:none;color:inherit;font:inherit;text-align:left;cursor:pointer;transition:background-color 160ms " + EASE + "}",
1095
+ ".pbb-cardHead:active{background:var(--dsw-alias-bg-module-platform)}",
1096
+ ".pbb-cardSummary{flex:1;min-width:0;text-align:right;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
1097
+ ".pbb-chev{flex:none;display:inline-flex;align-items:center;color:var(--dsw-alias-label-tertiary);transition:transform 160ms " + EASE + "}",
1098
+ ".pbb-chevIcon{display:block}",
1099
+ ".pbb-cardHead[aria-expanded=\"true\"] .pbb-chev,.pbb-expand[aria-expanded=\"true\"] .pbb-chev{transform:rotate(90deg)}",
1100
+ ".pbb-cardBody{display:flex;flex-direction:column;gap:12px;padding:4px 18px 16px}",
1101
+ ".pbb-pairingBody{display:flex;flex-direction:column;gap:10px;padding:0 18px 15px}",
1038
1102
  ".pbb-sectionIntro{padding-bottom:2px}",
1103
+ ".pbb-fold{display:grid;grid-template-rows:1fr;transition:grid-template-rows 240ms " + EASE + ",opacity 200ms " + EASE + ";opacity:1}",
1104
+ ".pbb-foldClosed{grid-template-rows:0fr;opacity:0}",
1105
+ ".pbb-fold>div{overflow:hidden;min-height:0}",
1039
1106
  ".pbb-row{display:flex;align-items:center;gap:8px}",
1040
1107
  ".pbb-label{flex:1;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary);min-width:0}",
1041
1108
  ".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}",
1109
+ ".pbb-badge,.pbb-countBadge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:2px 8px;font-size:12px;font-weight:500;white-space:nowrap}",
1110
+ ".pbb-badgeOk{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22a06b) 12%,transparent);color:var(--dsw-alias-state-success-primary,#22a06b)}",
1111
+ ".pbb-badgeWarn{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 14%,transparent);color:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1043
1112
  ".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}",
1113
+ ".pbb-statusBadge{display:inline-flex;align-items:center;gap:6px;flex:none;padding:3px 8px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;white-space:nowrap}",
1045
1114
  ".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)}",
1115
+ ".pbb-bad{color:var(--dsw-alias-state-error-primary);font-weight:500}",
1116
+ ".pbb-diag{font-size:12px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5;overflow-wrap:anywhere}",
1117
+ ".pbb-diagBad{color:var(--dsw-alias-state-error-primary)}",
1118
+ ".pbb-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.55;margin:0;padding:12px;border:1px dashed var(--dsw-alias-border-l3);border-radius:8px}",
1119
+ ".pbb-token{max-width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--dsw-alias-label-secondary);word-break:break-all}",
1120
+ ".pbb-action{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:1px solid var(--dsw-alias-border-l3);border-radius:7px;padding:5px 9px;line-height:1.2;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + ",background-color 160ms " + EASE + ",border-color 160ms " + EASE + "}",
1121
+ ".pbb-action:active{transform:scale(0.97)}",
1055
1122
  ".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}",
1123
+ ".pbb-buttonPrimary{font-size:13px;padding:6px 12px;color:var(--dsw-alias-label-primary-foreground);border-color:transparent;background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary))}",
1124
+ ".pbb-actionDangerGhost{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 38%,transparent);background:transparent;line-height:1.35;padding:4px 9px}",
1125
+ ".pbb-actionDangerGhost:active{transform:scale(0.97)}",
1063
1126
  ".pbb-actionDangerGhost:disabled{cursor:default;opacity:.62}",
1064
1127
  ".pbb-actionIcon{flex:none;display:inline-block;vertical-align:-2px;margin-right:5px;pointer-events:none}",
1065
1128
  ".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}",
1129
+ ".pbb-actionBusy::before{content:\"\";width:9px;height:9px;border-radius:50%;border:1.5px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 35%,transparent);border-top-color:currentColor;animation:pbb-spin .7s linear infinite}",
1067
1130
  "@keyframes pbb-spin{to{transform:rotate(360deg)}}",
1068
- ".pbb-masterRow{display:flex;align-items:center;gap:14px;padding:0 18px 14px}",
1131
+ ".pbb-masterRow{display:flex;align-items:center;gap:14px}",
1069
1132
  ".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}",
1133
+ ".pbb-switchTitle{font-size:14px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1134
+ ".pbb-switchDesc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.45}",
1135
+ ".pbb-switch{appearance:none;-webkit-appearance:none;width:40px;height:24px;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent);border:1px solid transparent;position:relative;cursor:pointer;padding:0;flex:none;transition:background-color 160ms " + EASE + ",border-color 160ms " + EASE + ",transform 160ms " + EASE + "}",
1136
+ ".pbb-switch::after{content:\"\";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.24);transition:transform 160ms " + EASE + ",background-color 160ms " + EASE + "}",
1137
+ ".pbb-switchOn{background:var(--dsw-alias-brand-primary)}",
1138
+ ".pbb-switchOn::after{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}",
1139
+ ".pbb-switch:active{transform:scale(0.97)}",
1076
1140
  ".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}",
1141
+ ".pbb-switch:focus-visible,.pbb-action:focus-visible,.pbb-targetButton:focus-visible,.pbb-cardHead:focus-visible,.pbb-expand:focus-visible,.pbb-refresh:focus-visible,.pbb-numberInput:focus-visible,.pbb-deviceNameInput:focus-visible,.pbb-disclosure>summary:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}",
1142
+ ".pbb-dot{display:inline-block;vertical-align:1px;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
1083
1143
  ".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}",
1144
+ ".pbb-dotWarn{background:var(--dsw-alias-state-warn-primary,#f59e0b)}",
1145
+ ".pbb-dotBad{background:var(--dsw-alias-state-error-primary)}",
1146
+ ".pbb-switchTitle .pbb-dot{margin-right:6px;vertical-align:1px}",
1147
+ ".pbb-channel{border-top:1px solid var(--dsw-alias-border-l2);padding-top:12px;display:flex;flex-direction:column;gap:10px}",
1148
+ ".pbb-expand{border:0;background:none;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;padding:0;display:flex;align-items:center;gap:6px;align-self:flex-start;transition:color 160ms " + EASE + ",transform 160ms " + EASE + "}",
1149
+ ".pbb-expand:active{transform:scale(0.97)}",
1150
+ ".pbb-channelBody{display:flex;flex-direction:column;gap:12px;padding:2px 0 0 14px}",
1107
1151
  ".pbb-limitRow{display:flex;align-items:center;gap:12px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1108
1152
  ".pbb-limitRow:first-child{border-top:0;padding-top:2px}",
1109
1153
  ".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}",
1154
+ ".pbb-numberInput{width:72px;box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:6px 8px}",
1155
+ ".pbb-numberInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1112
1156
  ".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)}",
1157
+ ".pbb-targetList{display:inline-flex;flex-wrap:wrap;gap:2px;background:var(--dsw-alias-bg-module-platform);border-radius:999px;padding:2px;align-self:flex-start}",
1158
+ ".pbb-targetButton{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:0;border-radius:999px;padding:6px 9px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:transform 160ms " + EASE + ",color 160ms " + EASE + ",background-color 160ms " + EASE + ",box-shadow 160ms " + EASE + "}",
1159
+ ".pbb-targetButton:active{transform:scale(0.97)}",
1116
1160
  ".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}",
1161
+ ".pbb-targetSelected{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);box-shadow:0 1px 3px color-mix(in srgb,var(--dsw-alias-label-primary) 14%,transparent);font-weight:500}",
1162
+ ".pbb-qrPanel{display:grid;grid-template-columns:224px 1fr;gap:20px;align-items:start;padding:14px 0 4px;border-top:1px solid var(--dsw-alias-border-l2)}",
1163
+ ".pbb-qrImage{width:224px;height:224px;max-width:100%;background:#fff;border-radius:12px;padding:8px;box-sizing:border-box}",
1164
+ ".pbb-countdown{margin-top:10px;display:flex;flex-direction:column;gap:6px}",
1165
+ ".pbb-countdownBar{height:4px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);overflow:hidden}",
1166
+ ".pbb-countdownBar i{display:block;width:100%;height:100%;transform-origin:left center;background:var(--dsw-alias-state-business-primary,var(--dsw-alias-state-success-primary,#22a06b));border-radius:999px;transition:transform 1s linear}",
1167
+ ".pbb-countdownText{font-size:12px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}",
1168
+ ".pbb-pairInfo{display:flex;flex-direction:column;gap:10px;min-width:0}",
1169
+ ".pbb-pairCodeBlock{width:100%;display:flex;flex-direction:column;gap:6px}",
1170
+ ".pbb-pairCodeLabel{font-size:12px;color:var(--dsw-alias-label-tertiary)}",
1171
+ ".pbb-pairCodeRow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
1172
+ ".pbb-pairCode{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;line-height:1.4;letter-spacing:.03em;color:var(--dsw-alias-label-primary);word-break:break-all;user-select:all}",
1173
+ ".pbb-qrHint{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
1174
+ ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
1175
+ ".pbb-resultRow{display:flex;align-items:flex-start;gap:6px}",
1176
+ ".pbb-resultIcon{flex:none;display:inline-flex;margin-top:2px;color:var(--dsw-alias-state-success-primary)}",
1177
+ ".pbb-resultIconBad{color:var(--dsw-alias-state-error-primary)}",
1178
+ ".pbb-deviceList{display:flex;flex-direction:column}",
1179
+ ".pbb-deviceRow{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:12px;row-gap:8px;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2)}",
1180
+ ".pbb-deviceRow:first-child{border-top:0;padding-top:2px}",
1181
+ ".pbb-deviceRow:last-child{padding-bottom:0}",
1182
+ ".pbb-deviceRow>.pbb-deviceRenameForm,.pbb-deviceRow>.pbb-deviceConfirm{grid-column:1/-1}",
1183
+ ".pbb-deviceMain{min-width:0;display:flex;flex-direction:column;gap:2px}",
1135
1184
  ".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}",
1185
+ ".pbb-deviceName{display:block;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1186
+ ".pbb-deviceMeta{display:flex;flex-wrap:wrap;column-gap:6px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;min-width:0}",
1187
+ ".pbb-deviceMeta>*+*::before{content:\"\\00B7\";margin-right:6px}",
1188
+ ".pbb-deviceMeta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1189
+ ".pbb-deviceActions{grid-row:1;grid-column:2;display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}",
1190
+ ".pbb-deviceConfirm{display:flex;flex-direction:column;gap:8px}",
1191
+ ".pbb-deviceRenameForm{display:flex;gap:8px;flex-wrap:wrap;min-width:0}",
1192
+ ".pbb-deviceNameInput{flex:1;min-width:0;box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:7px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:6px 8px}",
1193
+ ".pbb-deviceNameInput:focus{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}",
1144
1194
  ".pbb-deviceNameInput:disabled{opacity:.5}",
1145
1195
  ".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}",
1196
+ ".pbb-helpBody{display:flex;flex-direction:column;gap:14px}",
1197
+ ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
1198
+ ".pbb-helpHeading{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}",
1199
+ ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1200
+ ".pbb-helpList li+li{margin-top:4px}",
1201
+ ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
1202
+ ".pbb-helpCode{display:block;margin:2px 0 0;padding:10px;overflow:auto;white-space:pre-wrap;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}",
1203
+ ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
1162
1204
  ".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}",
1205
+ ".pbb-testItem:last-child:nth-child(odd){grid-column:1/-1}",
1206
+ ".pbb-testItem{display:flex;align-items:center;gap:12px;padding:11px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-module-platform)}",
1207
+ ".pbb-testTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
1208
+ ".pbb-testDescription{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.5}",
1209
+ ".pbb-runtimeList{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;overflow:hidden}",
1167
1210
  ".pbb-runtimeList>.pbb-runtimeRow,.pbb-runtimeRow{display:flex;align-items:center;gap:10px;padding:9px 10px;border-top:1px solid var(--dsw-alias-border-l2)}",
1168
1211
  ".pbb-runtimeList>.pbb-runtimeRow:first-child{border-top:0}",
1169
1212
  ".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}",
1213
+ ".pbb-updateRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-business-primary,#4176e6) 35%,var(--dsw-alias-border-l3));border-radius:8px;color:var(--dsw-alias-label-primary);font-size:13px;text-decoration:none;cursor:pointer;transition:background-color 160ms " + EASE + "}",
1214
+ ".pbb-disclosure{padding:0}",
1215
+ ".pbb-disclosure>summary{list-style:none;display:flex;align-items:center;gap:10px;padding:13px 18px;cursor:pointer;color:var(--dsw-alias-label-primary)}",
1216
+ ".pbb-disclosure>summary::-webkit-details-marker{display:none}",
1217
+ ".pbb-disclosure[open]>summary .pbb-chev{transform:rotate(90deg)}",
1218
+ ".pbb-disclosureTitle{font-size:13px;font-weight:500;flex:1}",
1219
+ ".pbb-disclosureMeta{font-size:12px;color:var(--dsw-alias-label-tertiary);max-width:46%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
1220
+ ".pbb-disclosureBody{display:flex;flex-direction:column;gap:12px;padding:2px 18px 16px}",
1171
1221
  ".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}}"
1222
+ "@media (hover:hover) and (pointer:fine){.pbb-refresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.pbb-cardHead:hover{background:var(--dsw-alias-bg-module-platform)}.pbb-action:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}.pbb-buttonPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-interactive-bg-hover-solid))}.pbb-actionDangerGhost:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 9%,transparent);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent)}.pbb-expand:hover{color:var(--dsw-alias-label-primary)}.pbb-updateRow:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary,#4176e6) 8%,transparent)}}",
1223
+ "@media (prefers-reduced-motion:reduce){.pbb-section *{animation-duration:.01ms!important;animation-delay:0s!important;transition-duration:.01ms!important;transition-delay:0s!important}}",
1224
+ "@container pbb (max-width:640px){.pbb-statusStrip{grid-template-columns:repeat(2,1fr)}.pbb-statusCell:nth-child(3){border-left:0}.pbb-statusCell:nth-child(n+3){border-top:1px solid var(--dsw-alias-border-l2)}.pbb-cardSummary{display:none}}",
1225
+ "@container pbb (max-width:480px){.pbb-testGrid{grid-template-columns:1fr}.pbb-testItem:last-child:nth-child(odd){grid-column:auto}.pbb-qrPanel{grid-template-columns:1fr}.pbb-limitRow{align-items:flex-start;flex-direction:column}.pbb-limitControl{align-self:flex-end}.pbb-deviceRow{grid-template-columns:minmax(0,1fr)}.pbb-deviceActions{grid-row:auto;grid-column:auto;justify-content:flex-start}}",
1226
+ "@media (pointer:coarse){.pbb-action{min-height:32px;padding:7px 10px;font-size:13px}.pbb-buttonPrimary{font-size:14px}.pbb-cardHead{min-height:56px}.pbb-expand{padding:5px 0}.pbb-refresh{padding:5px 0}}"
1173
1227
  ].join("\n");
1174
1228
  /**
1175
1229
  * Client bundle id, identical to the `window.__ModuleLoader__.load({ id })`
@@ -1198,15 +1252,13 @@ window.__ModuleLoader__.load({
1198
1252
  tag.textContent = CSS;
1199
1253
  document.head.appendChild(tag);
1200
1254
  }
1201
- //#endregion
1202
- //#region node_modules/qrcode/lib/can-promise.js
1255
+ // node_modules/qrcode/lib/can-promise.js
1203
1256
  var require_can_promise = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1204
1257
  module.exports = function() {
1205
1258
  return typeof Promise === "function" && Promise.prototype && Promise.prototype.then;
1206
1259
  };
1207
1260
  }));
1208
- //#endregion
1209
- //#region node_modules/qrcode/lib/core/utils.js
1261
+ // node_modules/qrcode/lib/core/utils.js
1210
1262
  var require_utils$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
1211
1263
  let toSJISFunction;
1212
1264
  const CODEWORDS_COUNT = [
@@ -1297,8 +1349,7 @@ window.__ModuleLoader__.load({
1297
1349
  return toSJISFunction(kanji);
1298
1350
  };
1299
1351
  }));
1300
- //#endregion
1301
- //#region node_modules/qrcode/lib/core/error-correction-level.js
1352
+ // node_modules/qrcode/lib/core/error-correction-level.js
1302
1353
  var require_error_correction_level = /* @__PURE__ */ __commonJSMin(((exports) => {
1303
1354
  exports.L = { bit: 1 };
1304
1355
  exports.M = { bit: 0 };
@@ -1330,8 +1381,7 @@ window.__ModuleLoader__.load({
1330
1381
  }
1331
1382
  };
1332
1383
  }));
1333
- //#endregion
1334
- //#region node_modules/qrcode/lib/core/bit-buffer.js
1384
+ // node_modules/qrcode/lib/core/bit-buffer.js
1335
1385
  var require_bit_buffer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1336
1386
  function BitBuffer() {
1337
1387
  this.buffer = [];
@@ -1357,8 +1407,7 @@ window.__ModuleLoader__.load({
1357
1407
  };
1358
1408
  module.exports = BitBuffer;
1359
1409
  }));
1360
- //#endregion
1361
- //#region node_modules/qrcode/lib/core/bit-matrix.js
1410
+ // node_modules/qrcode/lib/core/bit-matrix.js
1362
1411
  var require_bit_matrix = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1363
1412
  /**
1364
1413
  * Helper class to handle QR Code symbol modules
@@ -1418,8 +1467,7 @@ window.__ModuleLoader__.load({
1418
1467
  };
1419
1468
  module.exports = BitMatrix;
1420
1469
  }));
1421
- //#endregion
1422
- //#region node_modules/qrcode/lib/core/alignment-pattern.js
1470
+ // node_modules/qrcode/lib/core/alignment-pattern.js
1423
1471
  var require_alignment_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1424
1472
  /**
1425
1473
  * Alignment pattern are fixed reference pattern in defined positions
@@ -1486,8 +1534,7 @@ window.__ModuleLoader__.load({
1486
1534
  return coords;
1487
1535
  };
1488
1536
  }));
1489
- //#endregion
1490
- //#region node_modules/qrcode/lib/core/finder-pattern.js
1537
+ // node_modules/qrcode/lib/core/finder-pattern.js
1491
1538
  var require_finder_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1492
1539
  const getSymbolSize = require_utils$1().getSymbolSize;
1493
1540
  const FINDER_PATTERN_SIZE = 7;
@@ -1507,8 +1554,7 @@ window.__ModuleLoader__.load({
1507
1554
  ];
1508
1555
  };
1509
1556
  }));
1510
- //#endregion
1511
- //#region node_modules/qrcode/lib/core/mask-pattern.js
1557
+ // node_modules/qrcode/lib/core/mask-pattern.js
1512
1558
  var require_mask_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1513
1559
  /**
1514
1560
  * Data mask pattern reference
@@ -1571,18 +1617,18 @@ window.__ModuleLoader__.load({
1571
1617
  sameCountCol = sameCountRow = 0;
1572
1618
  lastCol = lastRow = null;
1573
1619
  for (let col = 0; col < size; col++) {
1574
- let module$1 = data.get(row, col);
1575
- if (module$1 === lastCol) sameCountCol++;
1620
+ let module = data.get(row, col);
1621
+ if (module === lastCol) sameCountCol++;
1576
1622
  else {
1577
1623
  if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5);
1578
- lastCol = module$1;
1624
+ lastCol = module;
1579
1625
  sameCountCol = 1;
1580
1626
  }
1581
- module$1 = data.get(col, row);
1582
- if (module$1 === lastRow) sameCountRow++;
1627
+ module = data.get(col, row);
1628
+ if (module === lastRow) sameCountRow++;
1583
1629
  else {
1584
1630
  if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5);
1585
- lastRow = module$1;
1631
+ lastRow = module;
1586
1632
  sameCountRow = 1;
1587
1633
  }
1588
1634
  }
@@ -1698,8 +1744,7 @@ window.__ModuleLoader__.load({
1698
1744
  return bestPattern;
1699
1745
  };
1700
1746
  }));
1701
- //#endregion
1702
- //#region node_modules/qrcode/lib/core/error-correction-code.js
1747
+ // node_modules/qrcode/lib/core/error-correction-code.js
1703
1748
  var require_error_correction_code = /* @__PURE__ */ __commonJSMin(((exports) => {
1704
1749
  const ECLevel = require_error_correction_level();
1705
1750
  const EC_BLOCKS_TABLE = [
@@ -2061,8 +2106,7 @@ window.__ModuleLoader__.load({
2061
2106
  }
2062
2107
  };
2063
2108
  }));
2064
- //#endregion
2065
- //#region node_modules/qrcode/lib/core/galois-field.js
2109
+ // node_modules/qrcode/lib/core/galois-field.js
2066
2110
  var require_galois_field = /* @__PURE__ */ __commonJSMin(((exports) => {
2067
2111
  const EXP_TABLE = /* @__PURE__ */ new Uint8Array(512);
2068
2112
  const LOG_TABLE = /* @__PURE__ */ new Uint8Array(256);
@@ -2107,8 +2151,7 @@ window.__ModuleLoader__.load({
2107
2151
  return EXP_TABLE[LOG_TABLE[x] + LOG_TABLE[y]];
2108
2152
  };
2109
2153
  }));
2110
- //#endregion
2111
- //#region node_modules/qrcode/lib/core/polynomial.js
2154
+ // node_modules/qrcode/lib/core/polynomial.js
2112
2155
  var require_polynomial = /* @__PURE__ */ __commonJSMin(((exports) => {
2113
2156
  const GF = require_galois_field();
2114
2157
  /**
@@ -2154,8 +2197,7 @@ window.__ModuleLoader__.load({
2154
2197
  return poly;
2155
2198
  };
2156
2199
  }));
2157
- //#endregion
2158
- //#region node_modules/qrcode/lib/core/reed-solomon-encoder.js
2200
+ // node_modules/qrcode/lib/core/reed-solomon-encoder.js
2159
2201
  var require_reed_solomon_encoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2160
2202
  const Polynomial = require_polynomial();
2161
2203
  function ReedSolomonEncoder(degree) {
@@ -2194,8 +2236,7 @@ window.__ModuleLoader__.load({
2194
2236
  };
2195
2237
  module.exports = ReedSolomonEncoder;
2196
2238
  }));
2197
- //#endregion
2198
- //#region node_modules/qrcode/lib/core/version-check.js
2239
+ // node_modules/qrcode/lib/core/version-check.js
2199
2240
  var require_version_check = /* @__PURE__ */ __commonJSMin(((exports) => {
2200
2241
  /**
2201
2242
  * Check if QR Code version is valid
@@ -2207,8 +2248,7 @@ window.__ModuleLoader__.load({
2207
2248
  return !isNaN(version) && version >= 1 && version <= 40;
2208
2249
  };
2209
2250
  }));
2210
- //#endregion
2211
- //#region node_modules/qrcode/lib/core/regex.js
2251
+ // node_modules/qrcode/lib/core/regex.js
2212
2252
  var require_regex = /* @__PURE__ */ __commonJSMin(((exports) => {
2213
2253
  const numeric = "[0-9]+";
2214
2254
  const alphanumeric = "[A-Z $%*+\\-./:]+";
@@ -2233,8 +2273,7 @@ window.__ModuleLoader__.load({
2233
2273
  return TEST_ALPHANUMERIC.test(str);
2234
2274
  };
2235
2275
  }));
2236
- //#endregion
2237
- //#region node_modules/qrcode/lib/core/mode.js
2276
+ // node_modules/qrcode/lib/core/mode.js
2238
2277
  var require_mode = /* @__PURE__ */ __commonJSMin(((exports) => {
2239
2278
  const VersionCheck = require_version_check();
2240
2279
  const Regex = require_regex();
@@ -2390,8 +2429,7 @@ window.__ModuleLoader__.load({
2390
2429
  }
2391
2430
  };
2392
2431
  }));
2393
- //#endregion
2394
- //#region node_modules/qrcode/lib/core/version.js
2432
+ // node_modules/qrcode/lib/core/version.js
2395
2433
  var require_version = /* @__PURE__ */ __commonJSMin(((exports) => {
2396
2434
  const Utils = require_utils$1();
2397
2435
  const ECCode = require_error_correction_code();
@@ -2487,8 +2525,7 @@ window.__ModuleLoader__.load({
2487
2525
  return version << 12 | d;
2488
2526
  };
2489
2527
  }));
2490
- //#endregion
2491
- //#region node_modules/qrcode/lib/core/format-info.js
2528
+ // node_modules/qrcode/lib/core/format-info.js
2492
2529
  var require_format_info = /* @__PURE__ */ __commonJSMin(((exports) => {
2493
2530
  const Utils = require_utils$1();
2494
2531
  const G15 = 1335;
@@ -2511,8 +2548,7 @@ window.__ModuleLoader__.load({
2511
2548
  return (data << 10 | d) ^ G15_MASK;
2512
2549
  };
2513
2550
  }));
2514
- //#endregion
2515
- //#region node_modules/qrcode/lib/core/numeric-data.js
2551
+ // node_modules/qrcode/lib/core/numeric-data.js
2516
2552
  var require_numeric_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2517
2553
  const Mode = require_mode();
2518
2554
  function NumericData(data) {
@@ -2544,8 +2580,7 @@ window.__ModuleLoader__.load({
2544
2580
  };
2545
2581
  module.exports = NumericData;
2546
2582
  }));
2547
- //#endregion
2548
- //#region node_modules/qrcode/lib/core/alphanumeric-data.js
2583
+ // node_modules/qrcode/lib/core/alphanumeric-data.js
2549
2584
  var require_alphanumeric_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2550
2585
  const Mode = require_mode();
2551
2586
  /**
@@ -2628,8 +2663,7 @@ window.__ModuleLoader__.load({
2628
2663
  };
2629
2664
  module.exports = AlphanumericData;
2630
2665
  }));
2631
- //#endregion
2632
- //#region node_modules/qrcode/lib/core/byte-data.js
2666
+ // node_modules/qrcode/lib/core/byte-data.js
2633
2667
  var require_byte_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2634
2668
  const Mode = require_mode();
2635
2669
  function ByteData(data) {
@@ -2651,8 +2685,7 @@ window.__ModuleLoader__.load({
2651
2685
  };
2652
2686
  module.exports = ByteData;
2653
2687
  }));
2654
- //#endregion
2655
- //#region node_modules/qrcode/lib/core/kanji-data.js
2688
+ // node_modules/qrcode/lib/core/kanji-data.js
2656
2689
  var require_kanji_data = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2657
2690
  const Mode = require_mode();
2658
2691
  const Utils = require_utils$1();
@@ -2682,8 +2715,7 @@ window.__ModuleLoader__.load({
2682
2715
  };
2683
2716
  module.exports = KanjiData;
2684
2717
  }));
2685
- //#endregion
2686
- //#region node_modules/dijkstrajs/dijkstra.js
2718
+ // node_modules/dijkstrajs/dijkstra.js
2687
2719
  var require_dijkstra = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2688
2720
  /******************************************************************************
2689
2721
  * Created 2008-08-19.
@@ -2798,8 +2830,7 @@ window.__ModuleLoader__.load({
2798
2830
  };
2799
2831
  if (typeof module !== "undefined") module.exports = dijkstra;
2800
2832
  }));
2801
- //#endregion
2802
- //#region node_modules/qrcode/lib/core/segments.js
2833
+ // node_modules/qrcode/lib/core/segments.js
2803
2834
  var require_segments = /* @__PURE__ */ __commonJSMin(((exports) => {
2804
2835
  const Mode = require_mode();
2805
2836
  const NumericData = require_numeric_data();
@@ -3077,8 +3108,7 @@ window.__ModuleLoader__.load({
3077
3108
  return exports.fromArray(getSegmentsFromString(data, Utils.isKanjiModeEnabled()));
3078
3109
  };
3079
3110
  }));
3080
- //#endregion
3081
- //#region node_modules/qrcode/lib/core/qrcode.js
3111
+ // node_modules/qrcode/lib/core/qrcode.js
3082
3112
  var require_qrcode = /* @__PURE__ */ __commonJSMin(((exports) => {
3083
3113
  const Utils = require_utils$1();
3084
3114
  const ECLevel = require_error_correction_level();
@@ -3373,8 +3403,7 @@ window.__ModuleLoader__.load({
3373
3403
  return createSymbol(data, version, errorCorrectionLevel, mask);
3374
3404
  };
3375
3405
  }));
3376
- //#endregion
3377
- //#region node_modules/qrcode/lib/renderer/utils.js
3406
+ // node_modules/qrcode/lib/renderer/utils.js
3378
3407
  var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
3379
3408
  function hex2rgba(hex) {
3380
3409
  if (typeof hex === "number") hex = hex.toString();
@@ -3441,8 +3470,7 @@ window.__ModuleLoader__.load({
3441
3470
  }
3442
3471
  };
3443
3472
  }));
3444
- //#endregion
3445
- //#region node_modules/qrcode/lib/renderer/canvas.js
3473
+ // node_modules/qrcode/lib/renderer/canvas.js
3446
3474
  var require_canvas = /* @__PURE__ */ __commonJSMin(((exports) => {
3447
3475
  const Utils = require_utils();
3448
3476
  function clearCanvas(ctx, canvas, size) {
@@ -3490,8 +3518,7 @@ window.__ModuleLoader__.load({
3490
3518
  return canvasEl.toDataURL(type, rendererOpts.quality);
3491
3519
  };
3492
3520
  }));
3493
- //#endregion
3494
- //#region node_modules/qrcode/lib/renderer/svg-tag.js
3521
+ // node_modules/qrcode/lib/renderer/svg-tag.js
3495
3522
  var require_svg_tag = /* @__PURE__ */ __commonJSMin(((exports) => {
3496
3523
  const Utils = require_utils();
3497
3524
  function getColorAttrib(color, attrib) {
@@ -3541,8 +3568,7 @@ window.__ModuleLoader__.load({
3541
3568
  return svgTag;
3542
3569
  };
3543
3570
  }));
3544
- //#endregion
3545
- //#region src/pairing-qr.ts
3571
+ // src/pairing-qr.ts
3546
3572
  var import_browser = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
3547
3573
  const canPromise = require_can_promise();
3548
3574
  const QRCode = require_qrcode();
@@ -3668,8 +3694,7 @@ window.__ModuleLoader__.load({
3668
3694
  if (!grant.audience.startsWith("deeppilot:")) throw new TypeError("pairing audience is invalid");
3669
3695
  if (tlsFingerprint !== void 0 && !isLanTlsFingerprint(tlsFingerprint)) throw new TypeError("pairing TLS fingerprint is invalid");
3670
3696
  }
3671
- //#endregion
3672
- //#region src/client/pairing-panel.ts
3697
+ // src/client/pairing-panel.ts
3673
3698
  const initialPairingPanelState = {
3674
3699
  target: null,
3675
3700
  busy: false,
@@ -3743,16 +3768,14 @@ window.__ModuleLoader__.load({
3743
3768
  function normalizeFunnelConnectionLimit(value) {
3744
3769
  return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 16 ? value : 8;
3745
3770
  }
3746
- //#endregion
3747
- //#region src/local-policy.ts
3771
+ // src/local-policy.ts
3748
3772
  const DEFAULT_LOCAL_PORT = 3098;
3749
3773
  const MIN_LOCAL_PORT = 1024;
3750
3774
  const MAX_LOCAL_PORT = 65535;
3751
3775
  function normalizeLocalPort(value) {
3752
3776
  return typeof value === "number" && Number.isInteger(value) && value >= 1024 && value <= 65535 ? value : DEFAULT_LOCAL_PORT;
3753
3777
  }
3754
- //#endregion
3755
- //#region src/client/settings-props.ts
3778
+ // src/client/settings-props.ts
3756
3779
  const MISSING_REPORT_HOOK = () => ({
3757
3780
  status: "loading",
3758
3781
  report: null,
@@ -3887,8 +3910,7 @@ window.__ModuleLoader__.load({
3887
3910
  reportHookMissing: !isFn(source.useDeepPilotReport)
3888
3911
  };
3889
3912
  }
3890
- //#endregion
3891
- //#region src/client/settings-view-models.ts
3913
+ // src/client/settings-view-models.ts
3892
3914
  /**
3893
3915
  * 设置页的 view model:把原先住在渲染函数里的三块逻辑变成可单测的纯模块。
3894
3916
  *
@@ -4032,28 +4054,7 @@ window.__ModuleLoader__.load({
4032
4054
  function deviceLabel(device) {
4033
4055
  return (device.customName ?? device.deviceName).trim();
4034
4056
  }
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
- //#endregion
4056
- //#region src/client/settings-page.ts
4057
+ // src/client/settings-page.ts
4057
4058
  /** phase 语义标签 → i18n key。措辞归页面,view model 只认语义。 */
4058
4059
  const REMOTE_LABEL_KEY = {
4059
4060
  disabled: "phase.disabled",
@@ -4071,6 +4072,16 @@ window.__ModuleLoader__.load({
4071
4072
  error: "local.phaseError",
4072
4073
  stopped: "local.phaseStopped"
4073
4074
  };
4075
+ /** 状态条用的短状态:列标签已经写明是哪条通道,值只说状态,免得在窄格里被截断。 */
4076
+ const CHANNEL_STATE_KEY = {
4077
+ disabled: "channel.state.disabled",
4078
+ starting: "channel.state.starting",
4079
+ login_required: "channel.state.login_required",
4080
+ online: "channel.state.online",
4081
+ error: "channel.state.error",
4082
+ unavailable: "channel.state.unavailable",
4083
+ stopped: "channel.state.stopped"
4084
+ };
4074
4085
  /** 服务状态 → i18n key。 */
4075
4086
  const SERVICE_STATUS_KEY = {
4076
4087
  loading: "status.loading",
@@ -4093,29 +4104,59 @@ window.__ModuleLoader__.load({
4093
4104
  strokeLinejoin: "round",
4094
4105
  "aria-hidden": true
4095
4106
  }, (0, react.createElement)("path", { d: "M2.5 4.5h11" }), (0, react.createElement)("path", { d: "M6.5 2.5h3" }), (0, react.createElement)("path", { d: "M4 4.5l.6 8.2a1 1 0 0 0 1 .9h4.8a1 1 0 0 0 1-.9l.6-8.2" }), (0, react.createElement)("path", { d: "M6.6 7v4.2" }), (0, react.createElement)("path", { d: "M9.4 7v4.2" }));
4107
+ /** Chevron icon (linear, currentColor). CSS rotates it 90° while its owner is
4108
+ * expanded, so collapsed always means "points right, more content behind". */
4109
+ const ChevronIcon = () => (0, react.createElement)("svg", {
4110
+ className: "pbb-chevIcon",
4111
+ viewBox: "0 0 16 16",
4112
+ width: 12,
4113
+ height: 12,
4114
+ fill: "none",
4115
+ stroke: "currentColor",
4116
+ strokeWidth: 1.5,
4117
+ strokeLinecap: "round",
4118
+ strokeLinejoin: "round",
4119
+ "aria-hidden": true
4120
+ }, (0, react.createElement)("path", { d: "M6 3.5 10.5 8 6 12.5" }));
4121
+ /** Check/cross marks for the relay and push result lines. They used to be the
4122
+ * dingbats '✓'/'✗', which the repo's visual rules reject as character icons. */
4096
4123
  const CheckIcon = () => (0, react.createElement)("svg", {
4097
- className: "pbb-controlIcon",
4098
4124
  viewBox: "0 0 16 16",
4099
- width: 14,
4100
- height: 14,
4125
+ width: 12,
4126
+ height: 12,
4101
4127
  fill: "none",
4102
4128
  stroke: "currentColor",
4103
- strokeWidth: 1.8,
4129
+ strokeWidth: 1.6,
4104
4130
  strokeLinecap: "round",
4105
4131
  strokeLinejoin: "round",
4106
4132
  "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",
4133
+ }, (0, react.createElement)("path", { d: "M3.5 8.5 6.5 11.5 12.5 4.5" }));
4134
+ const CrossIcon = () => (0, react.createElement)("svg", {
4110
4135
  viewBox: "0 0 16 16",
4111
- width: 14,
4112
- height: 14,
4136
+ width: 12,
4137
+ height: 12,
4113
4138
  fill: "none",
4114
4139
  stroke: "currentColor",
4115
4140
  strokeWidth: 1.6,
4116
4141
  strokeLinecap: "round",
4142
+ strokeLinejoin: "round",
4117
4143
  "aria-hidden": true
4118
- }, (0, react.createElement)("path", { d: "m4.5 4.5 7 7m0-7-7 7" }));
4144
+ }, (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" }));
4145
+ /** One result line: SVG mark + text; the mark carries the pass/fail state. */
4146
+ const resultRow = (ok, text, key) => (0, react.createElement)("p", {
4147
+ className: "pbb-diag pbb-resultRow",
4148
+ key
4149
+ }, (0, react.createElement)("span", { className: "pbb-resultIcon" + (ok ? "" : " pbb-resultIconBad") }, ok ? CheckIcon() : CrossIcon()), (0, react.createElement)("span", null, text));
4150
+ /** 最近在线只细化到分钟:秒和年份对判断设备是否还连着没有意义。 */
4151
+ function formatLastSeen(ts) {
4152
+ if (ts === void 0 || !Number.isFinite(ts) || ts <= 0) return "—";
4153
+ return new Date(ts).toLocaleString(void 0, {
4154
+ month: "numeric",
4155
+ day: "numeric",
4156
+ hour: "2-digit",
4157
+ minute: "2-digit"
4158
+ });
4159
+ }
4119
4160
  async function writeClipboard(t, value) {
4120
4161
  try {
4121
4162
  await navigator.clipboard.writeText(value);
@@ -4164,6 +4205,11 @@ window.__ModuleLoader__.load({
4164
4205
  message: "",
4165
4206
  failed: false
4166
4207
  });
4208
+ const [localOptionsOpen, setLocalOptionsOpen] = (0, react.useState)(false);
4209
+ const [remoteOptionsOpen, setRemoteOptionsOpen] = (0, react.useState)(false);
4210
+ const [systemOpen, setSystemOpen] = (0, react.useState)(false);
4211
+ const [armedRevokeDeviceId, setArmedRevokeDeviceId] = (0, react.useState)(null);
4212
+ const [nowMs, setNowMs] = (0, react.useState)(() => Date.now());
4167
4213
  (0, react.useEffect)(() => {
4168
4214
  if (!can("refresh")) return;
4169
4215
  const refresh = props.refresh;
@@ -4423,37 +4469,22 @@ window.__ModuleLoader__.load({
4423
4469
  });
4424
4470
  });
4425
4471
  };
4426
- const primaryRows = [];
4427
4472
  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) => {
4473
+ if (report !== null) advancedRows.push((0, react.createElement)("div", {
4474
+ className: "pbb-runtimeRow",
4475
+ key: "proto"
4476
+ }, (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", {
4477
+ className: "pbb-runtimeRow",
4478
+ key: "server"
4479
+ }, (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", {
4480
+ className: "pbb-runtimeRow",
4481
+ key: "path"
4482
+ }, (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", {
4483
+ className: "pbb-runtimeRow",
4484
+ key: "buffer"
4485
+ }, (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")))));
4486
+ const revokeDevice = (deviceId, _name) => {
4455
4487
  if (!can("revokeDevice")) return;
4456
- if (typeof window !== "undefined" && !window.confirm(translateWith(props.t, "devices.revokeConfirm", { name }))) return;
4457
4488
  setDeviceBusy(deviceId);
4458
4489
  setDeviceBusyAction("revoke");
4459
4490
  setDeviceMessage("");
@@ -4468,6 +4499,7 @@ window.__ModuleLoader__.load({
4468
4499
  };
4469
4500
  const deviceLabel$1 = (device) => deviceLabel(device) || translateWith(props.t, "devices.unnamed");
4470
4501
  const beginDeviceRename = (device) => {
4502
+ setArmedRevokeDeviceId(null);
4471
4503
  setEditingDeviceId(device.deviceId);
4472
4504
  setDeviceNameDraft(device.customName ?? device.deviceName);
4473
4505
  setDeviceMessage("");
@@ -4499,20 +4531,28 @@ window.__ModuleLoader__.load({
4499
4531
  });
4500
4532
  };
4501
4533
  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) => {
4534
+ const deviceRows = visibleDevices$1.length > 0 ? (0, react.createElement)("div", { className: "pbb-deviceList" }, visibleDevices$1.map((d) => {
4503
4535
  const editing = editingDeviceId === d.deviceId;
4504
4536
  const busy = deviceBusy === d.deviceId;
4505
4537
  const label = deviceLabel$1(d);
4506
- return (0, react.createElement)("tr", {
4538
+ const fingerprint = d.fingerprint ?? "";
4539
+ const armed = armedRevokeDeviceId === d.deviceId;
4540
+ return (0, react.createElement)("div", {
4541
+ className: "pbb-deviceRow",
4507
4542
  key: d.deviceId,
4508
- "aria-busy": busy
4509
- }, (0, react.createElement)("td", null, editing ? (0, react.createElement)("form", {
4543
+ "data-armed": armed || void 0,
4544
+ "aria-busy": busy,
4545
+ "aria-label": translateWith(props.t, "devices.col.name") + ": " + label
4546
+ }, (0, react.createElement)("div", { className: "pbb-deviceMain" }, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", {
4547
+ className: "pbb-deviceName",
4548
+ title: label
4549
+ }, label), d.customName ? (0, react.createElement)("span", { className: "pbb-badge" }, translateWith(props.t, "devices.customBadge")) : null), (0, react.createElement)("div", { className: "pbb-deviceMeta" }, (0, react.createElement)("span", null, d.appVersion), (0, react.createElement)("code", { title: fingerprint }, fingerprint.slice(0, 12)), (0, react.createElement)("span", null, translateWith(props.t, "devices.col.lastSeen") + ": " + formatLastSeen(d.lastSeenTs)))), editing ? (0, react.createElement)("form", {
4510
4550
  className: "pbb-deviceRenameForm",
4511
4551
  onSubmit: (event) => {
4512
4552
  event.preventDefault();
4513
4553
  saveDeviceName(d.deviceId);
4514
4554
  }
4515
- }, (0, react.createElement)("div", { className: "pbb-deviceRenameControls" }, (0, react.createElement)("input", {
4555
+ }, (0, react.createElement)("input", {
4516
4556
  className: "pbb-deviceNameInput",
4517
4557
  type: "text",
4518
4558
  value: deviceNameDraft,
@@ -4533,21 +4573,21 @@ window.__ModuleLoader__.load({
4533
4573
  }
4534
4574
  }), (0, react.createElement)("div", { className: "pbb-deviceRenameActions" }, (0, react.createElement)("button", {
4535
4575
  type: "submit",
4536
- className: "pbb-action pbb-deviceIconAction pbb-buttonPrimary",
4576
+ className: "pbb-action pbb-buttonPrimary",
4537
4577
  disabled: busy || deviceNameDraft.length > 64,
4538
4578
  title: busy ? translateWith(props.t, "devices.nameSaving") : translateWith(props.t, "devices.nameSave"),
4539
4579
  "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", {
4580
+ }, busy ? translateWith(props.t, "devices.nameSaving") : translateWith(props.t, "devices.nameSave")), (0, react.createElement)("button", {
4544
4581
  type: "button",
4545
- className: "pbb-action pbb-deviceIconAction",
4582
+ className: "pbb-action",
4546
4583
  disabled: busy,
4547
4584
  title: translateWith(props.t, "devices.nameCancel"),
4548
4585
  "aria-label": translateWith(props.t, "devices.nameCancel"),
4549
4586
  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", {
4587
+ }, translateWith(props.t, "devices.nameCancel"))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : null, armed ? null : (0, react.createElement)("div", {
4588
+ className: "pbb-deviceActions",
4589
+ "aria-label": translateWith(props.t, "devices.col.actions")
4590
+ }, !editing ? (0, react.createElement)("button", {
4551
4591
  type: "button",
4552
4592
  className: "pbb-action",
4553
4593
  disabled: busy || editingDeviceId !== null || !can("setDeviceName"),
@@ -4558,9 +4598,27 @@ window.__ModuleLoader__.load({
4558
4598
  className: "pbb-action pbb-actionDangerGhost",
4559
4599
  disabled: busy,
4560
4600
  "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"));
4601
+ onClick: () => {
4602
+ cancelDeviceRename();
4603
+ setArmedRevokeDeviceId(d.deviceId);
4604
+ }
4605
+ }, busy && deviceBusyAction === "revoke" ? null : TrashIcon(), busy && deviceBusyAction === "revoke" ? (0, react.createElement)("span", { className: "pbb-actionBusy" }, translateWith(props.t, "devices.deleting")) : translateWith(props.t, "devices.revoke"))), armed && !busy ? (0, react.createElement)("div", { className: "pbb-deviceConfirm" }, (0, react.createElement)("p", {
4606
+ className: "pbb-diag pbb-diagBad",
4607
+ role: "alert"
4608
+ }, translateWith(props.t, "devices.revokeConfirm", { name: label })), (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
4609
+ type: "button",
4610
+ className: "pbb-action pbb-actionDangerGhost",
4611
+ "aria-label": translateWith(props.t, "devices.revokeAria", { name: label }),
4612
+ onClick: () => {
4613
+ setArmedRevokeDeviceId(null);
4614
+ revokeDevice(d.deviceId, label);
4615
+ }
4616
+ }, translateWith(props.t, "devices.revoke")), (0, react.createElement)("button", {
4617
+ type: "button",
4618
+ className: "pbb-action",
4619
+ onClick: () => setArmedRevokeDeviceId(null)
4620
+ }, translateWith(props.t, "devices.nameCancel")))) : null);
4621
+ })) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
4564
4622
  const switchTitle = translateWith(props.t, "master.title");
4565
4623
  const switchDesc = switchReady ? enabled ? translateWith(props.t, "master.on") : translateWith(props.t, "master.off") : translateWith(props.t, "master.loading");
4566
4624
  const localMeta = report === null ? null : LOCAL_PHASE_META[report.local.phase];
@@ -4573,6 +4631,10 @@ window.__ModuleLoader__.load({
4573
4631
  });
4574
4632
  const serviceStatusText = translateWith(props.t, SERVICE_STATUS_KEY[service.kind]);
4575
4633
  const serviceDot = service.dot;
4634
+ const localPhaseText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, LOCAL_LABEL_KEY[localMeta.label]);
4635
+ const remotePhaseText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, REMOTE_LABEL_KEY[remoteMeta.label]);
4636
+ const localStateText = localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[localMeta.label]);
4637
+ const remoteStateText = remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, CHANNEL_STATE_KEY[remoteMeta.label]);
4576
4638
  /** 一条失败事件的单行文本:时间 · 类型 · 分因,附带可选排查字段。 */
4577
4639
  const diagnosticLine = (failure) => {
4578
4640
  const base = translateWith(props.t, "diagnostics.item", {
@@ -4625,17 +4687,98 @@ window.__ModuleLoader__.load({
4625
4687
  className: "pbb-action",
4626
4688
  onClick: copyDiagnostics,
4627
4689
  "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", {
4690
+ }, translateWith(props.t, "diagnostics.copy"))), diagnosticsCopy.message ? (0, react.createElement)("p", {
4691
+ className: "pbb-diag" + (diagnosticsCopy.failed ? " pbb-diagBad" : ""),
4692
+ role: "status"
4693
+ }, 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
4694
  className: "pbb-diag",
4630
4695
  key: String(index)
4631
4696
  }, diagnosticLine(failure)))));
4697
+ /** 状态总览的一格:标签 / 点位 + 值 / 次行;整格 title 兜住被裁掉的全文。
4698
+ * dot 为 null 表示这一格是计数而非状态,不画点位(灰点会被读成「未启用」)。 */
4699
+ const statusCell = (label, dot, value, sub) => (0, react.createElement)("div", {
4700
+ className: "pbb-statusCell",
4701
+ title: label + " · " + value + (sub === "" ? "" : " · " + sub)
4702
+ }, (0, react.createElement)("div", { className: "pbb-statusLabel" }, label), (0, react.createElement)("div", { className: "pbb-statusValue" }, dot === null ? null : (0, react.createElement)("span", { className: "pbb-dot" + dot }), value), (0, react.createElement)("div", { className: "pbb-statusSub" }, sub));
4703
+ /** 折叠区:grid-rows 0fr→1fr 平滑开合(mockup 的做法),不用 <details> 的瞬时跳变。 */
4704
+ const fold = (open, body) => (0, react.createElement)("div", { className: "pbb-fold" + (open ? "" : " pbb-foldClosed") }, (0, react.createElement)("div", null, body));
4705
+ /** 配对倒计时:仅在 QR 面板打开且 grant 在场时每秒刷新一次。 */
4706
+ (0, react.useEffect)(() => {
4707
+ if (!panel.open || pairingPanel.grant === null) return;
4708
+ const timer = globalThis.setInterval(() => setNowMs(Date.now()), 1e3);
4709
+ return () => globalThis.clearInterval(timer);
4710
+ }, [panel.open, pairingPanel.grant]);
4711
+ const grantRemainingMs = pairingPanel.grant === null ? 0 : Math.max(0, pairingPanel.grant.expiresAt - nowMs);
4712
+ const grantRemainingText = String(Math.floor(grantRemainingMs / 6e4)) + ":" + String(Math.floor(grantRemainingMs / 1e3) % 60).padStart(2, "0");
4713
+ const grantRemainingPct = Math.min(100, grantRemainingMs / 3e5 * 100);
4714
+ const localDesc = localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading");
4715
+ const remoteDesc = remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading");
4716
+ /** 端点 → 状态条副行只暴露 host[:port],去掉协议前缀让一行能放下。 */
4717
+ const endpointHost = (value) => {
4718
+ if (value === void 0 || value === "") return void 0;
4719
+ try {
4720
+ return new URL(value).host;
4721
+ } catch {
4722
+ return value;
4723
+ }
4724
+ };
4725
+ const localStatusSub = !localSwitchReady ? translateWith(props.t, "master.loading") : report !== null && report.local.phase === "online" ? endpointHost(report.local.endpoints[0]) ?? localDesc : report !== null && report.local.phase === "error" ? report.local.message !== void 0 && report.local.message !== "" ? report.local.message : localDesc : report !== null && (report.local.phase === "disabled" || report.local.phase === "stopped") ? translateWith(props.t, "local.off") : localDesc;
4726
+ const remoteStatusSub = !remoteSwitchReady ? translateWith(props.t, "master.loading") : report !== null && report.remote.phase === "online" ? endpointHost(report.remote.publicURL) ?? remoteDesc : report !== null && report.remote.phase === "error" ? report.remote.message !== void 0 && report.remote.message !== "" ? report.remote.message : remoteDesc : report !== null && report.remote.phase === "disabled" ? translateWith(props.t, "remote.off") : remoteDesc;
4727
+ const isOffPhase = (phase) => phase === "disabled" || phase === "stopped";
4728
+ const localSettled = report === null || (localEnabled ? report.local.phase === "online" : isOffPhase(report.local.phase));
4729
+ const remoteSettled = report === null || (remoteEnabled ? report.remote.phase === "online" : isOffPhase(report.remote.phase));
4730
+ const localChannelDesc = localSettled ? localDesc : localPhaseText + " · " + localDesc;
4731
+ const remoteChannelDesc = remoteSettled ? remoteDesc : remotePhaseText + " · " + remoteDesc;
4732
+ const recentFailures = report?.diagnostics?.recentFailures ?? [];
4733
+ const troubleGood = relayTestResult !== null ? relayTestResult.overall === "ok" : pushTestResult !== null ? pushTestResult.overall === "sent" : false;
4734
+ const troubleBad = relayTestResult !== null ? relayTestResult.overall !== "ok" : pushTestResult !== null ? pushTestResult.overall !== "sent" : recentFailures.length > 0;
4735
+ const troubleConclusion = relayTestResult !== null ? relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad") : pushTestResult !== null ? pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : translateWith(props.t, "push.pushFailed") : recentFailures.length > 0 ? translateWith(props.t, "diagnostics.recent") + " " + String(recentFailures.length) : translateWith(props.t, "troubleshooting.title");
4632
4736
  return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
4633
4737
  type: "button",
4634
4738
  className: "pbb-refresh",
4635
4739
  onClick: () => {
4636
4740
  if (can("refresh")) props.refresh();
4637
4741
  }
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", {
4742
+ }, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("section", { className: "pbb-statusStrip" }, statusCell(translateWith(props.t, "master.title"), serviceDot, serviceStatusText, switchDesc), statusCell(translateWith(props.t, "local.title"), localMeta?.dot ?? "", localStateText, localStatusSub), statusCell(translateWith(props.t, "remote.title"), remoteMeta?.dot ?? "", remoteStateText, remoteStatusSub), statusCell(translateWith(props.t, "panel.activeConnections"), null, report === null ? translateWith(props.t, "status.loading") : String(report.activeConnections), translateWith(props.t, "panel.identity") + " · " + (report !== null && report.pairingReady ? translateWith(props.t, "panel.identityReady") : translateWith(props.t, "panel.identityNotReady")))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "pair.qrPanelTitle")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "pair.description"))), pairingTarget === null ? (0, react.createElement)("span", { className: "pbb-statusBadge" + (enabled ? " pbb-bad" : "") }, enabled ? translateWith(props.t, "pair.noAddress") : translateWith(props.t, "status.off")) : (0, react.createElement)("button", {
4743
+ type: "button",
4744
+ className: "pbb-action pbb-buttonPrimary",
4745
+ disabled: panel.busy || report !== null && !report.pairingReady,
4746
+ onClick: () => {
4747
+ if (pairingTarget === null) return;
4748
+ if (panel.open) hidePairingQR();
4749
+ else showPairingQR(pairingTarget);
4750
+ }
4751
+ }, translateWith(props.t, panel.action))), (0, react.createElement)("div", { className: "pbb-pairingBody" }, pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag" }, enabled ? translateWith(props.t, "pair.noAddressHelp") : translateWith(props.t, "pair.disabledHelp")) : null, pairingTargets.length < 2 ? null : (0, react.createElement)("div", { className: "pbb-targetList" }, pairingTargets.map((target) => (0, react.createElement)("button", {
4752
+ type: "button",
4753
+ key: target.host,
4754
+ className: "pbb-targetButton" + (target.host === pairingTarget?.host ? " pbb-targetSelected" : ""),
4755
+ disabled: panel.busy,
4756
+ onClick: () => {
4757
+ setSelectedPairingHost(target.host);
4758
+ if (panel.open) showPairingQR(target);
4759
+ else hidePairingQR();
4760
+ }
4761
+ }, (target.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan")) + " · " + target.host))), panel.message ? (0, react.createElement)("p", {
4762
+ className: "pbb-diag pbb-diagBad",
4763
+ role: "alert"
4764
+ }, panel.message) : null, panel.notice ? (0, react.createElement)("p", {
4765
+ className: "pbb-diag",
4766
+ role: "status"
4767
+ }, panel.notice) : null, panel.failed && pairingTarget !== null ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("button", {
4768
+ type: "button",
4769
+ className: "pbb-action",
4770
+ disabled: panel.busy || report !== null && !report.pairingReady,
4771
+ onClick: () => showPairingQR(pairingTarget)
4772
+ }, 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", {
4773
+ className: "pbb-qrImage",
4774
+ src: panel.qrDataURL,
4775
+ alt: translateWith(props.t, "pair.qrAlt")
4776
+ }), 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", {
4777
+ type: "button",
4778
+ className: "pbb-action pbb-buttonPrimary",
4779
+ onClick: copyPairingInfo,
4780
+ "data-testid": "pairingInfoCopy"
4781
+ }, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") })))) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "connection.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "connection.description")))), (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-masterRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, switchTitle), (0, react.createElement)("span", { className: "pbb-switchDesc" }, switchDesc)), (0, react.createElement)("button", {
4639
4782
  type: "button",
4640
4783
  role: "switch",
4641
4784
  "aria-checked": enabled,
@@ -4645,16 +4788,22 @@ window.__ModuleLoader__.load({
4645
4788
  onClick: () => {
4646
4789
  if (can("setEnabled")) props.setDeepPilotEnabled(!enabled);
4647
4790
  }
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", {
4791
+ })), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, (0, react.createElement)("span", { className: "pbb-dot" + (localMeta?.dot ?? "") }), " ", translateWith(props.t, "local.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, localChannelDesc)), (0, react.createElement)("button", {
4792
+ type: "button",
4793
+ role: "switch",
4794
+ "aria-checked": localEnabled,
4795
+ "aria-label": translateWith(props.t, "local.title"),
4796
+ disabled: !localSwitchReady,
4797
+ className: "pbb-switch" + (localEnabled ? " pbb-switchOn" : ""),
4798
+ onClick: () => {
4799
+ if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
4800
+ }
4801
+ })), report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.local.message) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null, (0, react.createElement)("button", {
4802
+ type: "button",
4803
+ className: "pbb-expand",
4804
+ "aria-expanded": localOptionsOpen,
4805
+ onClick: () => setLocalOptionsOpen(!localOptionsOpen)
4806
+ }, translateWith(props.t, "connection.optionsSummary"), " ", (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(localOptionsOpen, (0, react.createElement)("div", { className: "pbb-channelBody" }, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4658
4807
  className: "pbb-switchTitle",
4659
4808
  htmlFor: "deeppilot-local-port"
4660
4809
  }, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "local.portDescription")), !localPortView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.portInvalid")) : localPortDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (localPortDraft.failed ? " pbb-diagBad" : "") }, localPortDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
@@ -4683,7 +4832,27 @@ window.__ModuleLoader__.load({
4683
4832
  className: "pbb-action",
4684
4833
  disabled: !localPortReady || !localPortView.valid || localPortView.unchanged,
4685
4834
  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", {
4835
+ }, translateWith(props.t, "remote.limitApply")))), report !== null && report.local.phase === "online" && report.local.tlsFingerprint ? (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, translateWith(props.t, "local.tlsFingerprint")), (0, react.createElement)("code", { className: "pbb-token" }, report.local.tlsFingerprint))) : null))), (0, react.createElement)("div", { className: "pbb-channel" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, (0, react.createElement)("span", { className: "pbb-dot" + (remoteMeta?.dot ?? "") }), " ", translateWith(props.t, "remote.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, remoteChannelDesc)), (0, react.createElement)("button", {
4836
+ type: "button",
4837
+ role: "switch",
4838
+ "aria-checked": remoteEnabled,
4839
+ "aria-label": translateWith(props.t, "remote.title"),
4840
+ disabled: !remoteSwitchReady,
4841
+ className: "pbb-switch" + (remoteEnabled ? " pbb-switchOn" : ""),
4842
+ onClick: () => {
4843
+ if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
4844
+ }
4845
+ })), report !== null && report.remote.phase === "login_required" && typeof report.remote.authURL === "string" && report.remote.authURL.startsWith("https://") ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("a", {
4846
+ className: "pbb-action pbb-buttonPrimary",
4847
+ href: report.remote.authURL,
4848
+ target: "_blank",
4849
+ rel: "noreferrer"
4850
+ }, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.remote.message) : null, (0, react.createElement)("button", {
4851
+ type: "button",
4852
+ className: "pbb-expand",
4853
+ "aria-expanded": remoteOptionsOpen,
4854
+ onClick: () => setRemoteOptionsOpen(!remoteOptionsOpen)
4855
+ }, translateWith(props.t, "connection.optionsSummary"), " ", (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())), fold(remoteOptionsOpen, (0, react.createElement)("div", { className: "pbb-channelBody" }, (0, react.createElement)("div", { className: "pbb-limitRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
4687
4856
  className: "pbb-switchTitle",
4688
4857
  htmlFor: "deeppilot-funnel-source-limit"
4689
4858
  }, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "remote.limitDescription")), !remoteLimitView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "remote.limitInvalid")) : remoteLimitDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (remoteLimitDraft.failed ? " pbb-diagBad" : "") }, remoteLimitDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
@@ -4712,48 +4881,20 @@ window.__ModuleLoader__.load({
4712
4881
  className: "pbb-action",
4713
4882
  disabled: !remoteConnectionLimitReady || !remoteLimitView.valid || remoteLimitView.unchanged,
4714
4883
  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", {
4884
+ }, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.remoteTitle")), (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.recommended")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.step1")), (0, react.createElement)("li", null, translateWith(props.t, "help.step2")), (0, react.createElement)("li", null, translateWith(props.t, "help.step3")), (0, react.createElement)("li", null, translateWith(props.t, "help.step4"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.funnelHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.httpsTitle")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep1")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep2")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep3"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.httpsHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.allowTitle")), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowBody")), (0, react.createElement)("code", { className: "pbb-helpCode" }, "\"nodeAttrs\": [\n {\n \"target\": [\"autogroup:member\"],\n \"attr\": [\"funnel\"],\n },\n],"), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.faqTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.faq1")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq2")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq3")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq4")))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.securityTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.security1")), (0, react.createElement)("li", null, translateWith(props.t, "help.security2")), (0, react.createElement)("li", null, translateWith(props.t, "help.security3")), (0, react.createElement)("li", null, translateWith(props.t, "help.security4")), (0, react.createElement)("li", null, translateWith(props.t, "help.security5"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.docsPrefix"), (0, react.createElement)("a", {
4716
4885
  className: "pbb-helpLink",
4717
4886
  href: "https://tailscale.com/docs/features/tailscale-funnel",
4718
4887
  target: "_blank",
4719
4888
  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", {
4889
+ }, translateWith(props.t, "help.funnelDocs")))))))))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "devices.description"))), (0, react.createElement)("span", { className: "pbb-countBadge" }, String(visibleDevices$1.length))), (0, react.createElement)("div", { className: "pbb-cardBody" }, deviceMessage ? (0, react.createElement)("p", {
4890
+ className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : ""),
4891
+ role: "status"
4892
+ }, deviceMessage) : null, deviceRows)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
4721
4893
  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", {
4894
+ className: "pbb-cardHead",
4895
+ "aria-expanded": troubleshootingOpen,
4896
+ onClick: () => setTroubleshootingOpen(!troubleshootingOpen)
4897
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "troubleshooting.summary")), (0, react.createElement)("span", { className: "pbb-cardSummary" }, translateWith(props.t, "troubleshooting.description")), (0, react.createElement)("span", { className: "pbb-badge" + (troubleGood ? " pbb-badgeOk" : troubleBad ? " pbb-badgeWarn" : "") }, troubleConclusion)), fold(troubleshootingOpen, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-testGrid" }, (0, react.createElement)("div", { className: "pbb-testItem" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "push.relayTitle")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "push.relayDefault"))), (0, react.createElement)("button", {
4757
4898
  type: "button",
4758
4899
  className: "pbb-action",
4759
4900
  disabled: relayTestBusy,
@@ -4773,24 +4914,65 @@ window.__ModuleLoader__.load({
4773
4914
  onClick: () => {
4774
4915
  if (can("setDebug")) props.setDeepPilotDebug(!debugEnabled);
4775
4916
  }
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",
4917
+ }))), relayTestError ? (0, react.createElement)("p", {
4918
+ className: "pbb-diag pbb-diagBad",
4919
+ role: "alert"
4920
+ }, relayTestError) : null, relayTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || translateWith(props.t, "push.relayUrlEmpty")), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad"))), relayTestResult.steps.map((step, index) => resultRow(step.ok, (step.id === "health" ? translateWith(props.t, "push.relayStep.health") : translateWith(props.t, "push.relayStep.enroll")) + (step.latencyMs !== void 0 ? ` (${String(step.latencyMs)}ms)` : "") + " — " + step.message, String(index)))), pushTestError ? (0, react.createElement)("p", {
4921
+ className: "pbb-diag pbb-diagBad",
4922
+ role: "alert"
4923
+ }, pushTestError) : null, pushTestResult === null ? null : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token " + (pushTestResult.overall === "sent" ? "pbb-ok" : pushTestResult.overall === "failed" ? "pbb-bad" : "") }, pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : pushTestResult.overall === "failed" ? translateWith(props.t, "push.pushFailed") : pushTestResult.overall === "no-targets" ? translateWith(props.t, "push.pushNoTargets") : translateWith(props.t, "push.pushNotEnabled"))), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, pushTestResult.message) : null, pushTestResult.results.map((r, index) => resultRow(r.outcome === "sent", r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : ""), String(index)))), diagnosticsSection))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("button", {
4924
+ type: "button",
4925
+ className: "pbb-cardHead",
4926
+ "aria-expanded": systemOpen,
4927
+ onClick: () => setSystemOpen(!systemOpen)
4928
+ }, (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon()), (0, react.createElement)("span", { className: "pbb-cardTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-cardSummary" }, translateWith(props.t, "system.description")), (0, react.createElement)("span", { className: "pbb-badge" + (report.updateAvailable === true ? " pbb-badgeOk" : "") }, "v" + report.pluginVersion + (report.updateAvailable === true ? " · " + translateWith(props.t, "update.badge") : ""))), fold(systemOpen, (0, react.createElement)("div", { className: "pbb-cardBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.tailscaleVersion")), (0, react.createElement)("span", { className: "pbb-value" }, report.remote.tailscaleVersion ?? translateWith(props.t, "advanced.tailscaleVersionUnknown"))), advancedRows, report.updateAvailable === true ? (0, react.createElement)("a", {
4929
+ className: "pbb-updateRow",
4784
4930
  href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
4785
4931
  target: "_blank",
4786
4932
  rel: "noreferrer"
4787
- }, translateWith(props.t, "update.badge"))) : null))), diag.length > 0 ? (0, react.createElement)("details", {
4933
+ }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("span", { className: "pbb-chev" }, ChevronIcon())) : null)))), diag.length > 0 ? (0, react.createElement)("details", {
4788
4934
  className: "pbb-card pbb-disclosure pbb-diagnostics",
4789
4935
  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);
4936
+ }, (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
4937
  }
4792
- //#endregion
4793
- //#region src/client/config-form.ts
4938
+ // src/client/nav-icon.ts
4939
+ /**
4940
+ * 设置页左侧导航里 DeepPilot 一行的图标。
4941
+ *
4942
+ * DSH 0.2.2 的设置壳为每个 `settings.section` 渲染
4943
+ * `renderSlot('settings.section.icon', { size, active }, { only: id, fallback })`:
4944
+ * 有同 id 的图标贡献就用它,否则画默认齿轮。壳负责按钮、文字和装饰性外框
4945
+ * (外框已 `aria-hidden`),图标只需按 `size` 画一个随文字颜色变化的线性 SVG。
4946
+ *
4947
+ * 不用 `icon.svg`:那是插件管理页的彩色品牌图(渐变填充、白底),放进导航行
4948
+ * 会和内置单色线性图标格格不入,也违反「图标统一用线性单色 SVG」的要求。
4949
+ * 这里画一部手机(DeepPilot 的职责是把手机接到这台电脑的 DSH),描边沿用
4950
+ * 设置页其他图标的 16×16 视图框和 1.5 线宽。
4951
+ */
4952
+ /** 导航行默认边长;壳当前固定传 16。 */
4953
+ const DEFAULT_NAV_ICON_SIZE = 16;
4954
+ const DeepPilotNavIcon = ({ size }) => {
4955
+ const edge = typeof size === "number" && size > 0 ? size : DEFAULT_NAV_ICON_SIZE;
4956
+ return (0, react.createElement)("svg", {
4957
+ viewBox: "0 0 16 16",
4958
+ width: edge,
4959
+ height: edge,
4960
+ fill: "none",
4961
+ stroke: "currentColor",
4962
+ strokeWidth: 1.5,
4963
+ strokeLinecap: "round",
4964
+ strokeLinejoin: "round",
4965
+ "aria-hidden": true,
4966
+ focusable: "false"
4967
+ }, (0, react.createElement)("rect", {
4968
+ x: 4.25,
4969
+ y: 1.75,
4970
+ width: 7.5,
4971
+ height: 12.5,
4972
+ rx: 1.75
4973
+ }), (0, react.createElement)("path", { d: "M7 11.75h2" }));
4974
+ };
4975
+ // src/client/config-form.ts
4794
4976
  /**
4795
4977
  * Deep-unwrap live-update references in a snapshot section.
4796
4978
  *
@@ -4843,8 +5025,7 @@ window.__ModuleLoader__.load({
4843
5025
  function bindConfigForm(ctx) {
4844
5026
  return adaptConfigForm(ctx.get("configForms"), "deeppilot");
4845
5027
  }
4846
- //#endregion
4847
- //#region src/client/index.ts
5028
+ // src/client/index.ts
4848
5029
  /** Polls the report remote and owns the page state transitions. */
4849
5030
  var ReportController = class {
4850
5031
  fetchReport;
@@ -5296,8 +5477,11 @@ window.__ModuleLoader__.load({
5296
5477
  t: (key, vars) => t(ctx, key, vars)
5297
5478
  })
5298
5479
  }, DeepPilotSettingsPage));
5480
+ anyCtx.slots?.inject("settings.section.icon", () => anyCtx.slots.register({
5481
+ name: "settings.section.icon",
5482
+ id: "deeppilot"
5483
+ }, DeepPilotNavIcon));
5299
5484
  }
5300
- //#endregion
5301
5485
  exports.DeepPilotSettingsPage = DeepPilotSettingsPage;
5302
5486
  exports.apply = apply;
5303
5487
  exports.inject = inject;