dsh-deeppilot 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -27,510 +27,1217 @@ window.__ModuleLoader__.load({
27
27
  enumerable: true
28
28
  }) : target, mod));
29
29
  //#endregion
30
- let react = require("react");
31
30
  let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
32
- //#region node_modules/qrcode/lib/can-promise.js
33
- var require_can_promise = /* @__PURE__ */ __commonJSMin(((exports, module) => {
34
- module.exports = function() {
35
- return typeof Promise === "function" && Promise.prototype && Promise.prototype.then;
36
- };
37
- }));
38
- //#endregion
39
- //#region node_modules/qrcode/lib/core/utils.js
40
- var require_utils$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
41
- let toSJISFunction;
42
- const CODEWORDS_COUNT = [
43
- 0,
44
- 26,
45
- 44,
46
- 70,
47
- 100,
48
- 134,
49
- 172,
50
- 196,
51
- 242,
52
- 292,
53
- 346,
54
- 404,
55
- 466,
56
- 532,
57
- 581,
58
- 655,
59
- 733,
60
- 815,
61
- 901,
62
- 991,
63
- 1085,
64
- 1156,
65
- 1258,
66
- 1364,
67
- 1474,
68
- 1588,
69
- 1706,
70
- 1828,
71
- 1921,
72
- 2051,
73
- 2185,
74
- 2323,
75
- 2465,
76
- 2611,
77
- 2761,
78
- 2876,
79
- 3034,
80
- 3196,
81
- 3362,
82
- 3532,
83
- 3706
84
- ];
85
- /**
86
- * Returns the QR Code size for the specified version
87
- *
88
- * @param {Number} version QR Code version
89
- * @return {Number} size of QR code
90
- */
91
- exports.getSymbolSize = function getSymbolSize(version) {
92
- if (!version) throw new Error("\"version\" cannot be null or undefined");
93
- if (version < 1 || version > 40) throw new Error("\"version\" should be in range from 1 to 40");
94
- return version * 4 + 17;
31
+ let react = require("react");
32
+ //#region src/report-wire.ts
33
+ /** The npm package identity both contribution registrations claim. */
34
+ const REPORT_REMOTE_PACKAGE = "dsh-deeppilot";
35
+ /** Canonical `<namespace>/<method>` endpoint of the report Remote. */
36
+ const REPORT_ENDPOINT = "deeppilot/report";
37
+ /** Explicit, user-triggered endpoint for revealing the pairing secret. */
38
+ const REVEAL_TOKEN_ENDPOINT = "deeppilot/revealToken";
39
+ /**
40
+ * Explicit, user-triggered endpoint that replaces the pairing secret. The old
41
+ * token stops working immediately; the fresh one is returned so the page can
42
+ * show/QR it right away.
43
+ */
44
+ const ROTATE_TOKEN_ENDPOINT = "deeppilot/rotateToken";
45
+ function reject(field) {
46
+ throw new TypeError(`deeppilot/report result: invalid ${field}`);
47
+ }
48
+ function str(source, key, field) {
49
+ const value = source[key];
50
+ if (typeof value !== "string") reject(field);
51
+ return value;
52
+ }
53
+ /**
54
+ * Non-negative integer: counters and timestamps (activeConnections,
55
+ * historyBufferMax, updatedAt, lastSeenTs, protocolVersion, etc.). A bare
56
+ * `typeof number` check accepts 1.5, -1, and 1e20 — all of which then
57
+ * surface verbatim on the settings page and break any sort or arithmetic
58
+ * the UI does.
59
+ */
60
+ function int(source, key, field) {
61
+ const value = source[key];
62
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < 0) reject(field);
63
+ return value;
64
+ }
65
+ function bool(source, key, field) {
66
+ const value = source[key];
67
+ if (typeof value !== "boolean") reject(field);
68
+ return value;
69
+ }
70
+ function rec(value, field) {
71
+ if (typeof value !== "object" || value === null || Array.isArray(value)) reject(field);
72
+ return value;
73
+ }
74
+ function parseDevice(value) {
75
+ const s = rec(value, "device");
76
+ let apns;
77
+ if (s.apns !== void 0) {
78
+ const a = rec(s.apns, "device.apns");
79
+ const environment = str(a, "environment", "device.apns.environment");
80
+ if (environment !== "development" && environment !== "production") reject("device.apns.environment");
81
+ apns = {
82
+ environment,
83
+ updatedAt: int(a, "updatedAt", "device.apns.updatedAt")
84
+ };
85
+ }
86
+ return {
87
+ deviceId: str(s, "deviceId", "device.deviceId"),
88
+ deviceName: str(s, "deviceName", "device.deviceName"),
89
+ appVersion: str(s, "appVersion", "device.appVersion"),
90
+ firstSeenTs: int(s, "firstSeenTs", "device.firstSeenTs"),
91
+ lastSeenTs: int(s, "lastSeenTs", "device.lastSeenTs"),
92
+ ...apns ? { apns } : {}
95
93
  };
96
- /**
97
- * Returns the total number of codewords used to store data and EC information.
98
- *
99
- * @param {Number} version QR Code version
100
- * @return {Number} Data length in bits
101
- */
102
- exports.getSymbolTotalCodewords = function getSymbolTotalCodewords(version) {
103
- return CODEWORDS_COUNT[version];
94
+ }
95
+ function parseRemote(value) {
96
+ const s = rec(value, "remote");
97
+ const provider = str(s, "provider", "remote.provider");
98
+ const phase = str(s, "phase", "remote.phase");
99
+ if (provider !== "tailscale-funnel") reject("remote.provider");
100
+ if (![
101
+ "disabled",
102
+ "starting",
103
+ "login_required",
104
+ "online",
105
+ "error",
106
+ "unavailable",
107
+ "stopped"
108
+ ].includes(phase)) reject("remote.phase");
109
+ const publicURL = s.publicURL;
110
+ const authURL = s.authURL;
111
+ const message = s.message;
112
+ if (publicURL !== void 0 && typeof publicURL !== "string") reject("remote.publicURL");
113
+ if (authURL !== void 0 && typeof authURL !== "string") reject("remote.authURL");
114
+ if (message !== void 0 && typeof message !== "string") reject("remote.message");
115
+ return {
116
+ provider,
117
+ phase,
118
+ ...typeof publicURL === "string" ? { publicURL } : {},
119
+ ...typeof authURL === "string" ? { authURL } : {},
120
+ ...typeof message === "string" ? { message } : {},
121
+ updatedAt: int(s, "updatedAt", "remote.updatedAt")
104
122
  };
105
- /**
106
- * Encode data with Bose-Chaudhuri-Hocquenghem
107
- *
108
- * @param {Number} data Value to encode
109
- * @return {Number} Encoded value
110
- */
111
- exports.getBCHDigit = function(data) {
112
- let digit = 0;
113
- while (data !== 0) {
114
- digit++;
115
- data >>>= 1;
116
- }
117
- return digit;
123
+ }
124
+ function parseRelayTestStep(value) {
125
+ const st = rec(value, "step");
126
+ const id = str(st, "id", "step.id");
127
+ if (id !== "health" && id !== "enroll") reject("step.id");
128
+ const latencyMs = st.latencyMs;
129
+ if (latencyMs !== void 0) {
130
+ if (typeof latencyMs !== "number" || !Number.isFinite(latencyMs) || !Number.isInteger(latencyMs) || latencyMs < 0) reject("step.latencyMs");
131
+ }
132
+ return {
133
+ id,
134
+ ok: bool(st, "ok", "step.ok"),
135
+ message: str(st, "message", "step.message"),
136
+ ...typeof latencyMs === "number" ? { latencyMs } : {}
118
137
  };
119
- exports.setToSJISFunction = function setToSJISFunction(f) {
120
- if (typeof f !== "function") throw new Error("\"toSJISFunc\" is not a valid function.");
121
- toSJISFunction = f;
138
+ }
139
+ function parseRelayTestResult(value) {
140
+ const s = rec(value, "result");
141
+ const overall = str(s, "overall", "overall");
142
+ if (overall !== "ok" && overall !== "failed") reject("overall");
143
+ const stepsRaw = s.steps;
144
+ if (!Array.isArray(stepsRaw)) reject("steps");
145
+ return {
146
+ url: str(s, "url", "url"),
147
+ overall,
148
+ tokenIssued: bool(s, "tokenIssued", "tokenIssued"),
149
+ steps: stepsRaw.map(parseRelayTestStep)
122
150
  };
123
- exports.isKanjiModeEnabled = function() {
124
- return typeof toSJISFunction !== "undefined";
151
+ }
152
+ function parsePushTestResult(value) {
153
+ const s = rec(value, "result");
154
+ const transport = str(s, "transport", "transport");
155
+ if (transport !== "apns" && transport !== "relay" && transport !== "none") reject("transport");
156
+ const overall = str(s, "overall", "overall");
157
+ if (![
158
+ "sent",
159
+ "failed",
160
+ "no-targets",
161
+ "not-configured"
162
+ ].includes(overall)) reject("overall");
163
+ const resultsRaw = s.results;
164
+ if (!Array.isArray(resultsRaw)) reject("results");
165
+ const results = resultsRaw.map((value) => {
166
+ const r = rec(value, "device result");
167
+ const reason = r.reason;
168
+ const tokenFingerprint = r.tokenFingerprint;
169
+ return {
170
+ name: str(r, "name", "result.name"),
171
+ environment: str(r, "environment", "result.environment"),
172
+ outcome: str(r, "outcome", "result.outcome"),
173
+ ...typeof reason === "string" && reason.length > 0 ? { reason } : {},
174
+ ...typeof tokenFingerprint === "string" && /^[0-9a-f]{10}$/.test(tokenFingerprint) ? { tokenFingerprint } : {}
175
+ };
176
+ });
177
+ const message = s.message;
178
+ return {
179
+ transport,
180
+ overall,
181
+ ...typeof message === "string" && message.length > 0 ? { message } : {},
182
+ results
125
183
  };
126
- exports.toSJIS = function toSJIS(kanji) {
127
- return toSJISFunction(kanji);
184
+ }
185
+ function parseReport(value) {
186
+ const s = rec(value, "report");
187
+ const devices = s.devices;
188
+ const lanAddresses = s.lanAddresses;
189
+ if (!Array.isArray(devices)) reject("devices");
190
+ if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== "string")) reject("lanAddresses");
191
+ const releaseUrl = s.releaseUrl;
192
+ return {
193
+ protocolVersion: int(s, "protocolVersion", "protocolVersion"),
194
+ serverVersion: str(s, "serverVersion", "serverVersion"),
195
+ pluginVersion: str(s, "pluginVersion", "pluginVersion"),
196
+ ...s.updateAvailable === true ? { updateAvailable: true } : {},
197
+ ...typeof releaseUrl === "string" && releaseUrl.length > 0 ? { releaseUrl } : {},
198
+ enabled: bool(s, "enabled", "enabled"),
199
+ tokenPath: str(s, "tokenPath", "tokenPath"),
200
+ tokenReady: bool(s, "tokenReady", "tokenReady"),
201
+ activeConnections: int(s, "activeConnections", "activeConnections"),
202
+ historyBufferMax: int(s, "historyBufferMax", "historyBufferMax"),
203
+ debug: bool(s, "debug", "debug"),
204
+ lanAddresses,
205
+ remote: parseRemote(s.remote),
206
+ devices: devices.map(parseDevice)
128
207
  };
129
- }));
130
- //#endregion
131
- //#region node_modules/qrcode/lib/core/error-correction-level.js
132
- var require_error_correction_level = /* @__PURE__ */ __commonJSMin(((exports) => {
133
- exports.L = { bit: 1 };
134
- exports.M = { bit: 0 };
135
- exports.Q = { bit: 3 };
136
- exports.H = { bit: 2 };
137
- function fromString(string) {
138
- if (typeof string !== "string") throw new Error("Param is not a string");
139
- switch (string.toLowerCase()) {
140
- case "l":
141
- case "low": return exports.L;
142
- case "m":
143
- case "medium": return exports.M;
144
- case "q":
145
- case "quartile": return exports.Q;
146
- case "h":
147
- case "high": return exports.H;
148
- default: throw new Error("Unknown EC Level: " + string);
149
- }
150
- }
151
- exports.isValid = function isValid(level) {
152
- return level && typeof level.bit !== "undefined" && level.bit >= 0 && level.bit < 4;
153
- };
154
- exports.from = function from(value, defaultValue) {
155
- if (exports.isValid(value)) return value;
156
- try {
157
- return fromString(value);
158
- } catch (e) {
159
- return defaultValue;
160
- }
161
- };
162
- }));
163
- //#endregion
164
- //#region node_modules/qrcode/lib/core/bit-buffer.js
165
- var require_bit_buffer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
166
- function BitBuffer() {
167
- this.buffer = [];
168
- this.length = 0;
169
- }
170
- BitBuffer.prototype = {
171
- get: function(index) {
172
- const bufIndex = Math.floor(index / 8);
173
- return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) === 1;
208
+ }
209
+ const reportSchema = { parse: parseReport };
210
+ const relayTestSchema = { parse: parseRelayTestResult };
211
+ const pushTestSchema = { parse: parsePushTestResult };
212
+ const pairingTokenSchema = { parse(value) {
213
+ if (typeof value !== "string" || value.length < 32) throw new TypeError("deeppilot/revealToken result: invalid token");
214
+ return value;
215
+ } };
216
+ const REPORT_REMOTE_CONTRIBUTION = {
217
+ package: REPORT_REMOTE_PACKAGE,
218
+ descriptors: [
219
+ {
220
+ id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,
221
+ service: "deeppilotReport",
222
+ namespace: "deeppilot",
223
+ method: "report",
224
+ invocation: { kind: "direct" },
225
+ parameters: [],
226
+ result: {
227
+ mode: "strict",
228
+ typeSymbol: `${REPORT_REMOTE_PACKAGE}#DeepPilotReport`,
229
+ schema: reportSchema
230
+ }
174
231
  },
175
- put: function(num, length) {
176
- for (let i = 0; i < length; i++) this.putBit((num >>> length - i - 1 & 1) === 1);
232
+ {
233
+ id: `${REPORT_REMOTE_PACKAGE}#${REVEAL_TOKEN_ENDPOINT}`,
234
+ service: "deeppilotReport",
235
+ namespace: "deeppilot",
236
+ method: "revealToken",
237
+ invocation: { kind: "direct" },
238
+ parameters: [],
239
+ result: {
240
+ mode: "strict",
241
+ typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,
242
+ schema: pairingTokenSchema
243
+ }
177
244
  },
178
- getLengthInBits: function() {
179
- return this.length;
245
+ {
246
+ id: `${REPORT_REMOTE_PACKAGE}#${ROTATE_TOKEN_ENDPOINT}`,
247
+ service: "deeppilotReport",
248
+ namespace: "deeppilot",
249
+ method: "rotateToken",
250
+ invocation: { kind: "direct" },
251
+ parameters: [],
252
+ result: {
253
+ mode: "strict",
254
+ typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,
255
+ schema: pairingTokenSchema
256
+ }
180
257
  },
181
- putBit: function(bit) {
182
- const bufIndex = Math.floor(this.length / 8);
183
- if (this.buffer.length <= bufIndex) this.buffer.push(0);
184
- if (bit) this.buffer[bufIndex] |= 128 >>> this.length % 8;
185
- this.length++;
258
+ {
259
+ id: `${REPORT_REMOTE_PACKAGE}#deeppilot/testRelay`,
260
+ service: "deeppilotReport",
261
+ namespace: "deeppilot",
262
+ method: "testRelay",
263
+ invocation: { kind: "direct" },
264
+ parameters: [],
265
+ result: {
266
+ mode: "strict",
267
+ typeSymbol: `${REPORT_REMOTE_PACKAGE}#RelayTestResult`,
268
+ schema: relayTestSchema
269
+ }
270
+ },
271
+ {
272
+ id: `${REPORT_REMOTE_PACKAGE}#deeppilot/testPush`,
273
+ service: "deeppilotReport",
274
+ namespace: "deeppilot",
275
+ method: "testPush",
276
+ invocation: { kind: "direct" },
277
+ parameters: [],
278
+ result: {
279
+ mode: "strict",
280
+ typeSymbol: `${REPORT_REMOTE_PACKAGE}#PushTestResult`,
281
+ schema: pushTestSchema
282
+ }
186
283
  }
187
- };
188
- module.exports = BitBuffer;
189
- }));
284
+ ]
285
+ };
190
286
  //#endregion
191
- //#region node_modules/qrcode/lib/core/bit-matrix.js
192
- var require_bit_matrix = /* @__PURE__ */ __commonJSMin(((exports, module) => {
193
- /**
194
- * Helper class to handle QR Code symbol modules
195
- *
196
- * @param {Number} size Symbol size
197
- */
198
- function BitMatrix(size) {
199
- if (!size || size < 1) throw new Error("BitMatrix size must be defined and greater than 0");
200
- this.size = size;
201
- this.data = new Uint8Array(size * size);
202
- this.reservedBit = new Uint8Array(size * size);
287
+ //#region src/client/report-mount.ts
288
+ /** Mount the contribution and resolve the namespace service it installs. */
289
+ async function mountReportRemote(remote, resolveNamespace) {
290
+ const dispose = await remote.$mount(REPORT_REMOTE_CONTRIBUTION);
291
+ const namespace = resolveNamespace();
292
+ if (namespace === void 0) {
293
+ await dispose();
294
+ throw new Error("remote.deeppilot 未注册");
203
295
  }
204
- /**
205
- * Set bit value at specified location
206
- * If reserved flag is set, this bit will be ignored during masking process
207
- *
208
- * @param {Number} row
209
- * @param {Number} col
210
- * @param {Boolean} value
211
- * @param {Boolean} reserved
212
- */
213
- BitMatrix.prototype.set = function(row, col, value, reserved) {
214
- const index = row * this.size + col;
215
- this.data[index] = value;
216
- if (reserved) this.reservedBit[index] = true;
217
- };
218
- /**
219
- * Returns bit value at specified location
220
- *
221
- * @param {Number} row
222
- * @param {Number} col
223
- * @return {Boolean}
224
- */
225
- BitMatrix.prototype.get = function(row, col) {
226
- return this.data[row * this.size + col];
227
- };
228
- /**
229
- * Applies xor operator at specified location
230
- * (used during masking process)
231
- *
232
- * @param {Number} row
233
- * @param {Number} col
234
- * @param {Boolean} value
235
- */
236
- BitMatrix.prototype.xor = function(row, col, value) {
237
- this.data[row * this.size + col] ^= value;
238
- };
239
- /**
240
- * Check if bit at specified location is reserved
241
- *
242
- * @param {Number} row
243
- * @param {Number} col
244
- * @return {Boolean}
245
- */
246
- BitMatrix.prototype.isReserved = function(row, col) {
247
- return this.reservedBit[row * this.size + col];
296
+ return {
297
+ namespace,
298
+ dispose
248
299
  };
249
- module.exports = BitMatrix;
250
- }));
300
+ }
251
301
  //#endregion
252
- //#region node_modules/qrcode/lib/core/alignment-pattern.js
253
- var require_alignment_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
254
- /**
255
- * Alignment pattern are fixed reference pattern in defined positions
256
- * in a matrix symbology, which enables the decode software to re-synchronise
257
- * the coordinate mapping of the image modules in the event of moderate amounts
258
- * of distortion of the image.
259
- *
260
- * Alignment patterns are present only in QR Code symbols of version 2 or larger
261
- * and their number depends on the symbol version.
262
- */
263
- const getSymbolSize = require_utils$1().getSymbolSize;
264
- /**
265
- * Calculate the row/column coordinates of the center module of each alignment pattern
266
- * for the specified QR Code version.
267
- *
268
- * The alignment patterns are positioned symmetrically on either side of the diagonal
269
- * running from the top left corner of the symbol to the bottom right corner.
270
- *
271
- * Since positions are simmetrical only half of the coordinates are returned.
272
- * Each item of the array will represent in turn the x and y coordinate.
273
- * @see {@link getPositions}
274
- *
275
- * @param {Number} version QR Code version
276
- * @return {Array} Array of coordinate
277
- */
278
- exports.getRowColCoords = function getRowColCoords(version) {
279
- if (version === 1) return [];
280
- const posCount = Math.floor(version / 7) + 2;
281
- const size = getSymbolSize(version);
282
- const intervals = size === 145 ? 26 : Math.ceil((size - 13) / (2 * posCount - 2)) * 2;
283
- const positions = [size - 7];
284
- for (let i = 1; i < posCount - 1; i++) positions[i] = positions[i - 1] - intervals;
285
- positions.push(6);
286
- return positions.reverse();
287
- };
288
- /**
289
- * Returns an array containing the positions of each alignment pattern.
290
- * Each array's element represent the center point of the pattern as (x, y) coordinates
291
- *
292
- * Coordinates are calculated expanding the row/column coordinates returned by {@link getRowColCoords}
293
- * and filtering out the items that overlaps with finder pattern
294
- *
295
- * @example
296
- * For a Version 7 symbol {@link getRowColCoords} returns values 6, 22 and 38.
297
- * The alignment patterns, therefore, are to be centered on (row, column)
298
- * positions (6,22), (22,6), (22,22), (22,38), (38,22), (38,38).
299
- * Note that the coordinates (6,6), (6,38), (38,6) are occupied by finder patterns
300
- * and are not therefore used for alignment patterns.
301
- *
302
- * let pos = getPositions(7)
303
- * // [[6,22], [22,6], [22,22], [22,38], [38,22], [38,38]]
304
- *
305
- * @param {Number} version QR Code version
306
- * @return {Array} Array of coordinates
307
- */
308
- exports.getPositions = function getPositions(version) {
309
- const coords = [];
310
- const pos = exports.getRowColCoords(version);
311
- const posLength = pos.length;
312
- for (let i = 0; i < posLength; i++) for (let j = 0; j < posLength; j++) {
313
- if (i === 0 && j === 0 || i === 0 && j === posLength - 1 || i === posLength - 1 && j === 0) continue;
314
- coords.push([pos[i], pos[j]]);
315
- }
316
- return coords;
317
- };
318
- }));
302
+ //#region src/client/i18n.ts
303
+ /** Single namespace shared with the host's locale registry, so other
304
+ * host-side modules (if any) can translate the same keys. */
305
+ const DEEPPILOT_LOCALE_NS = "settings.deeppilot";
306
+ /**
307
+ * Single source of truth. Every key here MUST have a translation in every
308
+ * supported locale; the i18n.test.ts "table parity" check enforces that.
309
+ *
310
+ * Shape: `{ locale: { key: value } }` the same shape `dsh-client-locale`'s
311
+ * `register(namespace, table)` accepts (and the same shape we already used
312
+ * at the bottom of client/index.ts before this module existed).
313
+ *
314
+ * Naming convention: `<section>.<field>` (two dotted segments, lowercase,
315
+ * ASCII-only). Names longer than two segments are reserved for future
316
+ * sub-section splits and intentionally absent today.
317
+ */
318
+ const TABLES = {
319
+ zh: {
320
+ "nav": "DeepPilot",
321
+ "meta.title": "DeepPilot",
322
+ "meta.intro": "把 iPhone 与这台电脑上的 DeepSeek Harness(DSH)连接起来(协议 v1)。",
323
+ "meta.refresh": "刷新",
324
+ "master.title": "DeepPilot 连接",
325
+ "master.loading": "正在读取配置…",
326
+ "master.on": "已开启:接受手机连接。",
327
+ "master.off": "已关闭:不接受手机连接。",
328
+ "remote.title": "远程连接(Tailscale Funnel)",
329
+ "remote.on": "已配置:内嵌 Funnel 会自动启动并同步状态。",
330
+ "remote.off": "关闭:仅保留局域网连接。",
331
+ "remote.openAuth": "打开授权页面",
332
+ "phase.disabled": "未启用",
333
+ "phase.starting": "正在启动",
334
+ "phase.login_required": "等待 Tailscale 授权",
335
+ "phase.online": "远程连接已就绪",
336
+ "phase.error": "远程连接失败",
337
+ "phase.unavailable": "远程 helper 不可用",
338
+ "phase.stopped": "已停止",
339
+ "phase.unknown": "状态未知",
340
+ "panel.activeConnections": "当前连接",
341
+ "panel.token": "配对 Token",
342
+ "panel.tokenReady": "已就绪",
343
+ "panel.tokenNotReady": "未生成",
344
+ "panel.tokenMasked": "••••••••••••",
345
+ "panel.tokenAction.show": "显示",
346
+ "panel.tokenAction.hide": "隐藏",
347
+ "panel.tokenAction.copy": "复制",
348
+ "panel.tokenAction.rotate": "更换",
349
+ "panel.tokenAction.rotateConfirm": "确认更换?",
350
+ "panel.tokenAction.showing": "读取中…",
351
+ "panel.tokenAction.rotating": "更换中…",
352
+ "panel.tokenAutoHidden": "Token 已自动隐藏",
353
+ "panel.tokenCopied": "已复制到剪贴板",
354
+ "panel.tokenCopyFailed": "复制失败:",
355
+ "panel.tokenRevealFailed": "Token 读取失败:",
356
+ "panel.tokenRotateWarning": "更换会使当前 Token 立即失效、断开所有手机连接;5 秒内再次点击确认。",
357
+ "panel.tokenRotated": "新 Token 已生效,旧 Token 已失效;请重新配对所有设备。",
358
+ "panel.tokenRotateFailed": "更换失败:",
359
+ "pair.qrPanelTitle": "扫码添加当前手机",
360
+ "pair.kind.public": "公网",
361
+ "pair.kind.lan": "内网",
362
+ "pair.noAddress": "没有可用地址",
363
+ "pair.noAddressHelp": "未发现可供手机访问的局域网地址,请确认这台电脑已连接局域网。",
364
+ "pair.qrAlt": "DeepPilot 配对二维码",
365
+ "pair.qrShow": "显示二维码",
366
+ "pair.qrHide": "隐藏二维码",
367
+ "pair.qrGenerating": "生成中…",
368
+ "pair.qrAutoHidden": "配对二维码已自动隐藏",
369
+ "pair.qrFailed": "二维码生成失败:",
370
+ "pair.publicCopyDone": "公网地址已复制",
371
+ "pair.publicCopyFailed": "复制失败:",
372
+ "pair.qrHint": "二维码包含{kind}地址和配对 Token,将在 60 秒后自动隐藏。",
373
+ "advanced.summary": "高级信息",
374
+ "advanced.protocolVersion": "协议版本",
375
+ "advanced.serverVersion": "服务器版本",
376
+ "advanced.tokenPath": "Token 路径",
377
+ "advanced.bufferMax": "重放缓冲上限",
378
+ "advanced.frames": " 帧",
379
+ "push.relayTitle": "离线推送中继",
380
+ "push.testRelay": "测试访问与注册",
381
+ "push.testPush": "发送测试通知",
382
+ "push.relayTesting": "测试中…",
383
+ "push.pushSending": "发送中…",
384
+ "push.relayDefault": "验证 Mac 到推送中继的连通性与自动注册是否正常。",
385
+ "push.pushDefault": "向所有已注册设备强制发送一条真实推送(不受在线状态与分类开关影响)。",
386
+ "push.relayOk": "通过",
387
+ "push.relayBad": "存在问题",
388
+ "push.relayUrlEmpty": "(未启用)",
389
+ "push.pushSent": "已送达",
390
+ "push.pushFailed": "发送失败",
391
+ "push.pushNoTargets": "无已注册设备",
392
+ "push.pushNotEnabled": "推送未启用",
393
+ "push.relayStep.health": "服务可达",
394
+ "push.relayStep.enroll": "自动注册",
395
+ "push.prefix.ok": "✓ ",
396
+ "push.prefix.fail": "✗ ",
397
+ "devices.title": "已配对设备",
398
+ "devices.col.name": "设备",
399
+ "devices.col.appVersion": "App 版本",
400
+ "devices.col.push": "离线推送",
401
+ "devices.col.lastSeen": "最近在线",
402
+ "devices.pushRegistered": "已注册(",
403
+ "devices.pushNotRegistered": "未注册",
404
+ "devices.pushEnvProduction": "生产",
405
+ "devices.pushEnvDevelopment": "开发",
406
+ "devices.empty": "还没有设备配对过。在 iPhone 上的 DeepPilot App 中扫码或填入本机地址与 Token 即可配对。",
407
+ "help.remoteTitle": "远程连接帮助",
408
+ "help.recommended": "推荐设置流程",
409
+ "help.step1": "先打开\"DeepPilot 连接\",再打开\"远程连接(Tailscale Funnel)\"。",
410
+ "help.step2": "状态变为\"等待 Tailscale 授权\"后,点击\"打开授权页面\"。",
411
+ "help.step3": "使用有权管理此 Tailnet 的账号登录,并按授权页提示启用 Funnel。通常需要 Owner、Admin 或 Network admin 权限。",
412
+ "help.step4": "返回此页面并点击\"刷新\"。远程连接标题旁出现绿点后,即可扫描下方二维码添加手机。",
413
+ "help.funnelHint": "插件已内嵌 Tailscale 网络组件,这台电脑和手机都不需要另外安装 Tailscale App;但首次启用仍需由 Tailnet 管理员授权。",
414
+ "help.httpsTitle": "授权页未自动完成时:开启 HTTPS",
415
+ "help.httpsStep1": "打开 Tailscale 管理后台的 Network → DNS。",
416
+ "help.httpsStep2": "确认 MagicDNS 已开启。",
417
+ "help.httpsStep3": "在 HTTPS Certificates 中点击 Enable HTTPS。",
418
+ "help.httpsHint": "启用 HTTPS 后,设备的完整域名会写入公开的证书透明度日志。如果设备名包含敏感信息,请先在 Tailscale 中重命名设备。",
419
+ "help.allowTitle": "授权页未自动完成时:允许 Funnel",
420
+ "help.allowBody": "打开 Access controls → Definitions,选择 Node attributes。在现有 nodeAttrs 数组中追加下面这一项;不要覆盖已有访问规则,也不要创建第二个 nodeAttrs 顶层字段。",
421
+ "help.allowHint": "保存策略后回到本页刷新。公共 DNS 和权限变更可能需要几分钟生效。",
422
+ "help.faqTitle": "常见问题",
423
+ "help.faq1": "提示\"HTTPS must be enabled\":完成上面的 HTTPS Certificates 设置。",
424
+ "help.faq2": "\"Funnel not available\":确认 nodeAttrs 已保存,并且当前节点属于规则的 target。",
425
+ "help.faq3": "已有公网地址但手机超时:等待几分钟后重试,同时确认这台电脑未休眠、DSH 正在运行,再重新扫描最新二维码。",
426
+ "help.faq4": "没有公网地址:检查 Tailscale 授权是否完成,然后点击\"刷新\"或重启 DSH。",
427
+ "help.securityTitle": "安全说明",
428
+ "help.security1": "Funnel 公网地址可从互联网访问,但手机接口仍需要配对 Token。不要分享二维码、Token 或包含它们的截图。",
429
+ "help.security2": "怀疑 Token 泄露时,点击上方\"更换\"生成新 Token:旧 Token 立即失效,所有已连接设备会被断开并需要重新配对。",
430
+ "help.security3": "插件只通过 Funnel 转发 /phone 和 /phone/health,不会新增 3098 端口。",
431
+ "help.security4": "局域网连接沿用 DSH 的 3080 端口;仅在可信网络中使用,不建议直接把 3080 暴露到公网。",
432
+ "help.security5": "关闭远程连接开关会停止 Funnel,但不会影响可用的局域网连接。",
433
+ "help.funnelDocs": "Tailscale Funnel 官方文档",
434
+ "help.docsPrefix": "更多信息:",
435
+ "diag.remoteUnavailable": "remote 服务不可用",
436
+ "diag.remoteUnmounted": "report remote 未挂载",
437
+ "diag.mountFailed": "报告远程未挂载(remote mount 失败)— 请确认宿主包含 typert 组合",
438
+ "diag.mountFailedShort": "remote mount 失败: ",
439
+ "diag.callFailed": "report remote 调用失败",
440
+ "diag.settingsUnavailable": "设置命名空间不可用(settingsScope 未提供)",
441
+ "diag.missingReportHook": "useDeepPilotReport hook 缺失",
442
+ "diag.missingEnabledHook": "useDeepPilotEnabled hook 缺失",
443
+ "diag.missingRemoteEnabledHook": "useDeepPilotRemoteEnabled hook 缺失",
444
+ "diag.missingRefresh": "refresh 回调缺失",
445
+ "diag.missingReveal": "revealPairingToken 回调缺失",
446
+ "diag.missingRotate": "rotatePairingToken 回调缺失",
447
+ "diag.missingTestRelay": "testRelay 回调缺失",
448
+ "diag.missingTestPush": "testPush 回调缺失",
449
+ "diag.missingSetEnabled": "setDeepPilotEnabled 回调缺失",
450
+ "diag.missingSetRemote": "setDeepPilotRemoteEnabled 回调缺失",
451
+ "diag.renderError": "渲染异常: ",
452
+ "diag.prefix": "diag: ",
453
+ "clipboard.rejected": "浏览器拒绝了剪贴板写入",
454
+ "push.staleHost": "宿主插件版本较旧,不支持推送测试",
455
+ "push.staleHostRelay": "宿主插件版本较旧,不支持中继测试",
456
+ "push.staleHostPush": "宿主插件版本较旧,不支持推送测试 — 请更新 dsh-deeppilot",
457
+ "push.staleHostPushRelay": "宿主插件版本较旧,不支持中继测试 — 请更新 dsh-deeppilot",
458
+ "update.badge": "有新版本"
459
+ },
460
+ en: {
461
+ "nav": "DeepPilot",
462
+ "meta.title": "DeepPilot",
463
+ "meta.intro": "Connect your iPhone to DeepSeek Harness (DSH) on this Mac (protocol v1).",
464
+ "meta.refresh": "Refresh",
465
+ "master.title": "DeepPilot connection",
466
+ "master.loading": "Loading settings…",
467
+ "master.on": "On: accepting phone connections.",
468
+ "master.off": "Off: not accepting phone connections.",
469
+ "remote.title": "Remote connection (Tailscale Funnel)",
470
+ "remote.on": "Configured: the embedded Funnel will start and sync state automatically.",
471
+ "remote.off": "Off: LAN connections only.",
472
+ "remote.openAuth": "Open authorization page",
473
+ "phase.disabled": "Disabled",
474
+ "phase.starting": "Starting",
475
+ "phase.login_required": "Awaiting Tailscale authorization",
476
+ "phase.online": "Remote connection ready",
477
+ "phase.error": "Remote connection failed",
478
+ "phase.unavailable": "Remote helper unavailable",
479
+ "phase.stopped": "Stopped",
480
+ "phase.unknown": "Status unknown",
481
+ "panel.activeConnections": "Active connections",
482
+ "panel.token": "Pairing token",
483
+ "panel.tokenReady": "Ready",
484
+ "panel.tokenNotReady": "Not generated",
485
+ "panel.tokenMasked": "••••••••••••",
486
+ "panel.tokenAction.show": "Show",
487
+ "panel.tokenAction.hide": "Hide",
488
+ "panel.tokenAction.copy": "Copy",
489
+ "panel.tokenAction.rotate": "Rotate",
490
+ "panel.tokenAction.rotateConfirm": "Confirm rotate?",
491
+ "panel.tokenAction.showing": "Reading…",
492
+ "panel.tokenAction.rotating": "Rotating…",
493
+ "panel.tokenAutoHidden": "Token auto-hidden",
494
+ "panel.tokenCopied": "Copied to clipboard",
495
+ "panel.tokenCopyFailed": "Copy failed: ",
496
+ "panel.tokenRevealFailed": "Token reveal failed: ",
497
+ "panel.tokenRotateWarning": "Rotation invalidates the current token immediately and drops every paired phone. Click again within 5s to confirm.",
498
+ "panel.tokenRotated": "New token active, old token invalidated. Please re-pair every device.",
499
+ "panel.tokenRotateFailed": "Rotation failed: ",
500
+ "pair.qrPanelTitle": "Scan to pair this phone",
501
+ "pair.kind.public": "Public",
502
+ "pair.kind.lan": "LAN",
503
+ "pair.noAddress": "No address available",
504
+ "pair.noAddressHelp": "No LAN address reachable from a phone. Make sure this Mac is on the local network.",
505
+ "pair.qrAlt": "DeepPilot pairing QR code",
506
+ "pair.qrShow": "Show QR code",
507
+ "pair.qrHide": "Hide QR code",
508
+ "pair.qrGenerating": "Generating…",
509
+ "pair.qrAutoHidden": "Pairing QR auto-hidden",
510
+ "pair.qrFailed": "QR generation failed: ",
511
+ "pair.publicCopyDone": "Public URL copied",
512
+ "pair.publicCopyFailed": "Copy failed: ",
513
+ "pair.qrHint": "The QR contains a {kind} address and the pairing token; it auto-hides after 60 seconds.",
514
+ "advanced.summary": "Advanced info",
515
+ "advanced.protocolVersion": "Protocol version",
516
+ "advanced.serverVersion": "Server version",
517
+ "advanced.tokenPath": "Token path",
518
+ "advanced.bufferMax": "Replay buffer cap",
519
+ "advanced.frames": " frames",
520
+ "push.relayTitle": "Offline push relay",
521
+ "push.testRelay": "Test reach & enroll",
522
+ "push.testPush": "Send test notification",
523
+ "push.relayTesting": "Testing…",
524
+ "push.pushSending": "Sending…",
525
+ "push.relayDefault": "Verify Mac → push relay connectivity and zero-touch enrollment.",
526
+ "push.pushDefault": "Force one real push to every registered device (ignores online state and category switches).",
527
+ "push.relayOk": "OK",
528
+ "push.relayBad": "Issues found",
529
+ "push.relayUrlEmpty": "(disabled)",
530
+ "push.pushSent": "Delivered",
531
+ "push.pushFailed": "Send failed",
532
+ "push.pushNoTargets": "No registered devices",
533
+ "push.pushNotEnabled": "Push not enabled",
534
+ "push.relayStep.health": "Service reachable",
535
+ "push.relayStep.enroll": "Auto enrollment",
536
+ "push.prefix.ok": "✓ ",
537
+ "push.prefix.fail": "✗ ",
538
+ "devices.title": "Paired devices",
539
+ "devices.col.name": "Device",
540
+ "devices.col.appVersion": "App version",
541
+ "devices.col.push": "Push",
542
+ "devices.col.lastSeen": "Last seen",
543
+ "devices.pushRegistered": "Registered (",
544
+ "devices.pushNotRegistered": "Not registered",
545
+ "devices.pushEnvProduction": "Production",
546
+ "devices.pushEnvDevelopment": "Development",
547
+ "devices.empty": "No devices paired yet. Scan the QR code or enter the host address + token in DeepPilot on iPhone to pair.",
548
+ "help.remoteTitle": "Remote connection help",
549
+ "help.recommended": "Recommended setup",
550
+ "help.step1": "Turn on \"DeepPilot connection\" first, then \"Remote connection (Tailscale Funnel)\".",
551
+ "help.step2": "When the status shows \"Awaiting Tailscale authorization\", click \"Open authorization page\".",
552
+ "help.step3": "Sign in with an account that can manage this Tailnet and follow the on-page prompts to enable Funnel. Owner / Admin / Network admin is usually required.",
553
+ "help.step4": "Come back to this page and click \"Refresh\". When a green dot appears next to the remote title, scan the QR code below to add your phone.",
554
+ "help.funnelHint": "The plugin bundles the Tailscale networking stack, so neither this Mac nor the phone needs the Tailscale app — but a Tailnet administrator must approve first-time use.",
555
+ "help.httpsTitle": "If authorization does not complete: enable HTTPS",
556
+ "help.httpsStep1": "Open the Tailscale admin console → Network → DNS.",
557
+ "help.httpsStep2": "Confirm MagicDNS is enabled.",
558
+ "help.httpsStep3": "In HTTPS Certificates click Enable HTTPS.",
559
+ "help.httpsHint": "Enabling HTTPS publishes the device hostname to public certificate transparency logs. Rename the device in Tailscale first if its name is sensitive.",
560
+ "help.allowTitle": "If authorization does not complete: allow Funnel",
561
+ "help.allowBody": "Open Access controls → Definitions, select Node attributes. Append the snippet below to your existing nodeAttrs array; do not overwrite other access rules and do not add a second top-level nodeAttrs field.",
562
+ "help.allowHint": "Save the policy and refresh this page. Public DNS and policy changes can take a few minutes to propagate.",
563
+ "help.faqTitle": "Frequently asked questions",
564
+ "help.faq1": "\"HTTPS must be enabled\" — finish the HTTPS Certificates steps above.",
565
+ "help.faq2": "\"Funnel not available\" — make sure the nodeAttrs snippet is saved and this node is in the rule's target.",
566
+ "help.faq3": "Public URL exists but the phone times out: wait a few minutes, confirm the Mac is awake and DSH is running, then re-scan the latest QR code.",
567
+ "help.faq4": "No public URL: check that Tailscale authorization completed, then click \"Refresh\" or restart DSH.",
568
+ "help.securityTitle": "Security notes",
569
+ "help.security1": "A Funnel public URL is reachable from the public Internet, but the phone interface still needs the pairing token. Do not share the QR code, the token, or any screenshot that contains them.",
570
+ "help.security2": "If you suspect the token leaked, click \"Rotate\" above to mint a new one. The old token stops working immediately and every paired device must re-pair.",
571
+ "help.security3": "The plugin only forwards /phone and /phone/health through Funnel; no new port 3098 is opened.",
572
+ "help.security4": "LAN connections reuse DSH's existing 3080 port. Use them only on trusted networks; do not expose 3080 directly to the public Internet.",
573
+ "help.security5": "Disabling the remote switch stops Funnel but does not affect any LAN connection you already have.",
574
+ "help.funnelDocs": "Tailscale Funnel docs",
575
+ "help.docsPrefix": "More info: ",
576
+ "diag.remoteUnavailable": "remote service unavailable",
577
+ "diag.remoteUnmounted": "report remote not mounted",
578
+ "diag.mountFailed": "Report remote not mounted (mount failed) — confirm the host includes the typert composition",
579
+ "diag.mountFailedShort": "remote mount failed: ",
580
+ "diag.callFailed": "report remote call failed",
581
+ "diag.settingsUnavailable": "Settings namespace unavailable (settingsScope not provided)",
582
+ "diag.missingReportHook": "useDeepPilotReport hook missing",
583
+ "diag.missingEnabledHook": "useDeepPilotEnabled hook missing",
584
+ "diag.missingRemoteEnabledHook": "useDeepPilotRemoteEnabled hook missing",
585
+ "diag.missingRefresh": "refresh callback missing",
586
+ "diag.missingReveal": "revealPairingToken callback missing",
587
+ "diag.missingRotate": "rotatePairingToken callback missing",
588
+ "diag.missingTestRelay": "testRelay callback missing",
589
+ "diag.missingTestPush": "testPush callback missing",
590
+ "diag.missingSetEnabled": "setDeepPilotEnabled callback missing",
591
+ "diag.missingSetRemote": "setDeepPilotRemoteEnabled callback missing",
592
+ "diag.renderError": "Render error: ",
593
+ "diag.prefix": "diag: ",
594
+ "clipboard.rejected": "Clipboard write rejected by the browser",
595
+ "push.staleHost": "Host plugin is too old for push testing",
596
+ "push.staleHostRelay": "Host plugin is too old for relay testing",
597
+ "push.staleHostPush": "Host plugin is too old for push testing — please update dsh-deeppilot",
598
+ "push.staleHostPushRelay": "Host plugin is too old for relay testing — please update dsh-deeppilot",
599
+ "update.badge": "New version available"
600
+ }
601
+ };
602
+ Object.keys(TABLES.zh);
603
+ /** Substitute every `{name}` in `template` with `String(vars[name])`. The
604
+ * host bind() does not interpolate, so we always run the result through
605
+ * here for any key the caller asked to format. Numeric / object placeholders
606
+ * are coerced to strings; missing placeholders are left intact so a
607
+ * missing key is visible in the rendered output. */
608
+ function interpolate(template, vars) {
609
+ if (!vars) return template;
610
+ return template.replace(/\{(\w+)\}/g, (match, name) => {
611
+ const value = vars[name];
612
+ if (value === void 0) return match;
613
+ return String(value);
614
+ });
615
+ }
616
+ /** Detect the active language from the locale snapshot or browser hints. */
617
+ function detectLocale(ctx) {
618
+ try {
619
+ const active = ctx.locale?.getSnapshot?.().active;
620
+ if (active === "zh" || active?.toLowerCase().startsWith("zh-")) return "zh";
621
+ if (active === "en" || active?.toLowerCase().startsWith("en-")) return "en";
622
+ } catch {}
623
+ if ((typeof document === "undefined" ? "" : document.documentElement.lang).toLowerCase().startsWith("zh")) return "zh";
624
+ if ((typeof navigator === "undefined" ? [] : navigator.languages).some((language) => language.toLowerCase().startsWith("zh"))) return "zh";
625
+ return "en";
626
+ }
627
+ /**
628
+ * Invoke the translation function supplied to a locale-aware slot. Keeping
629
+ * this adapter distinct from `t(ctx, ...)` prevents a translator function
630
+ * from being mistaken for a Cordis Context, which previously forced every
631
+ * settings-page lookup through the English no-host fallback.
632
+ */
633
+ function translateWith(translator, key, vars) {
634
+ if (typeof translator !== "function") return key;
635
+ return translator(key, vars);
636
+ }
637
+ /** Translation function. Callers always go through this — never the
638
+ * underlying locale face — so the substitution / fallback path stays in
639
+ * one place. `ctx` may be omitted (offline / SSR / tests) and we resolve
640
+ * to en automatically. */
641
+ function t(ctx, key, vars) {
642
+ const anyCtx = ctx;
643
+ let template;
644
+ if (anyCtx?.locale) try {
645
+ template = anyCtx.locale.bind(DEEPPILOT_LOCALE_NS)(key);
646
+ } catch {
647
+ template = void 0;
648
+ }
649
+ if (template === void 0 || template === key) {
650
+ const locale = detectLocale(anyCtx ?? { locale: void 0 });
651
+ template = TABLES[locale][key] ?? TABLES.zh[key] ?? TABLES.en[key] ?? key;
652
+ }
653
+ return interpolate(template, vars);
654
+ }
655
+ /** Register both dictionaries and return the host-owned disposer. */
656
+ function registerLocale(ctx) {
657
+ const anyCtx = ctx;
658
+ if (anyCtx.locale === void 0) return () => {};
659
+ return anyCtx.locale.register(DEEPPILOT_LOCALE_NS, {
660
+ zh: TABLES.zh,
661
+ en: TABLES.en
662
+ });
663
+ }
319
664
  //#endregion
320
- //#region node_modules/qrcode/lib/core/finder-pattern.js
321
- var require_finder_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
322
- const getSymbolSize = require_utils$1().getSymbolSize;
323
- const FINDER_PATTERN_SIZE = 7;
324
- /**
325
- * Returns an array containing the positions of each finder pattern.
326
- * Each array's element represent the top-left point of the pattern as (x, y) coordinates
327
- *
328
- * @param {Number} version QR Code version
329
- * @return {Array} Array of coordinates
330
- */
331
- exports.getPositions = function getPositions(version) {
332
- const size = getSymbolSize(version);
333
- return [
334
- [0, 0],
335
- [size - FINDER_PATTERN_SIZE, 0],
336
- [0, size - FINDER_PATTERN_SIZE]
337
- ];
665
+ //#region src/client/styles.ts
666
+ const CSS = [
667
+ ".pbb-section{max-width:720px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:12px}",
668
+ ".pbb-title{margin:0;font-size:18px;font-weight:600}",
669
+ ".pbb-intro{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5;margin:0}",
670
+ ".pbb-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px}",
671
+ ".pbb-field{display:flex;flex-direction:column;gap:6px;padding:12px 0}",
672
+ ".pbb-field+.pbb-field{border-top:1px solid var(--dsw-alias-border-l2)}",
673
+ ".pbb-row{display:flex;align-items:center;gap:8px}",
674
+ ".pbb-label{flex:1;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary);min-width:0}",
675
+ ".pbb-value{font-size:13px;color:var(--dsw-alias-label-secondary);word-break:break-all;text-align:right}",
676
+ ".pbb-badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;white-space:nowrap}",
677
+ ".pbb-ok{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));font-weight:500}",
678
+ ".pbb-bad{color:var(--dsw-alias-label-error);font-weight:500}",
679
+ ".pbb-table{width:100%;border-collapse:collapse;font-size:12px}",
680
+ ".pbb-table th{color:var(--dsw-alias-label-tertiary);text-align:left;font-weight:500;padding:6px 8px;border-bottom:1px solid var(--dsw-alias-border-l2)}",
681
+ ".pbb-table td{padding:6px 8px;border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}",
682
+ ".pbb-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:0}",
683
+ ".pbb-diag{font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5}",
684
+ ".pbb-diagBad{color:var(--dsw-alias-label-error)}",
685
+ ".pbb-refresh{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:none;padding:0}",
686
+ ".pbb-refresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}",
687
+ ".pbb-refresh:disabled{cursor:default;opacity:.5}",
688
+ ".pbb-tokenRow{flex-wrap:wrap}",
689
+ ".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}",
690
+ ".pbb-tokenActions{display:flex;align-items:center;gap:8px}",
691
+ ".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:3px 8px}",
692
+ ".pbb-action:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}",
693
+ ".pbb-action:disabled{cursor:default;opacity:.5}",
694
+ ".pbb-actionDanger:not(:disabled){color:#fff;background:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}",
695
+ ".pbb-qrPanel{display:flex;flex-direction:column;align-items:center;gap:9px;padding:12px 0 4px}",
696
+ ".pbb-qrImage{width:240px;height:240px;max-width:100%;background:#fff;border-radius:10px;padding:8px;box-sizing:border-box}",
697
+ ".pbb-qrHint{max-width:420px;text-align:center;font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
698
+ ".pbb-switchRow{padding:12px 0;display:flex;align-items:center;gap:12px}",
699
+ ".pbb-switchRow+.pbb-field{border-top:1px solid var(--dsw-alias-border-l2)}",
700
+ ".pbb-switchText{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}",
701
+ ".pbb-switchTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
702
+ ".pbb-dotRow{display:flex;align-items:center;gap:7px}",
703
+ ".pbb-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
704
+ ".pbb-dotOk{background:var(--dsw-alias-state-success-primary,#22a06b)}",
705
+ ".pbb-dotWarn{background:var(--dsw-alias-state-warning-primary,#e2b203)}",
706
+ ".pbb-dotBad{background:var(--dsw-alias-label-error)}",
707
+ ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
708
+ ".pbb-switchDesc{font-size:11px;color:var(--dsw-alias-label-tertiary);line-height:1.4}",
709
+ ".pbb-switch{appearance:none;-webkit-appearance:none;width:38px;height:22px;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}",
710
+ ".pbb-switch::after{content:\"\";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-secondary);transition:transform .15s ease,background .15s ease}",
711
+ ".pbb-switchOn{background:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary))}",
712
+ ".pbb-switchOn::after{transform:translateX(16px);background:#fff}",
713
+ ".pbb-switch:disabled{opacity:.5;cursor:default}",
714
+ ".pbb-help{border-top:1px solid var(--dsw-alias-border-l2);padding:12px 0}",
715
+ ".pbb-help summary{cursor:pointer;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;user-select:none}",
716
+ ".pbb-help summary:hover{color:var(--dsw-alias-label-secondary)}",
717
+ ".pbb-helpBody{display:flex;flex-direction:column;gap:14px;padding:12px 0 2px}",
718
+ ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
719
+ ".pbb-helpHeading{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary)}",
720
+ ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
721
+ ".pbb-helpList li+li{margin-top:4px}",
722
+ ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
723
+ ".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}",
724
+ ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
725
+ ".pbb-versionFooter{display:flex;justify-content:center;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-tertiary);padding:8px 0 4px;line-height:1.4}",
726
+ ".pbb-versionFooter a{color:var(--dsw-alias-state-success-primary,#22a06b);text-decoration:none}",
727
+ ".pbb-versionFooter a:hover{text-decoration:underline;text-underline-offset:2px}"
728
+ ].join("\n");
729
+ function injectCss() {
730
+ if (typeof document === "undefined") return;
731
+ const id = "dsh-deeppilot/page.css";
732
+ if (document.querySelector("style[data-plugin-css=\"" + id + "\"]") !== null) return;
733
+ const tag = document.createElement("style");
734
+ tag.setAttribute("data-plugin-css", id);
735
+ tag.textContent = CSS;
736
+ document.head.appendChild(tag);
737
+ }
738
+ //#endregion
739
+ //#region node_modules/qrcode/lib/can-promise.js
740
+ var require_can_promise = /* @__PURE__ */ __commonJSMin(((exports, module) => {
741
+ module.exports = function() {
742
+ return typeof Promise === "function" && Promise.prototype && Promise.prototype.then;
338
743
  };
339
744
  }));
340
745
  //#endregion
341
- //#region node_modules/qrcode/lib/core/mask-pattern.js
342
- var require_mask_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
343
- /**
344
- * Data mask pattern reference
345
- * @type {Object}
346
- */
347
- exports.Patterns = {
348
- PATTERN000: 0,
349
- PATTERN001: 1,
350
- PATTERN010: 2,
351
- PATTERN011: 3,
352
- PATTERN100: 4,
353
- PATTERN101: 5,
354
- PATTERN110: 6,
355
- PATTERN111: 7
356
- };
357
- /**
358
- * Weighted penalty scores for the undesirable features
359
- * @type {Object}
360
- */
361
- const PenaltyScores = {
362
- N1: 3,
363
- N2: 3,
364
- N3: 40,
365
- N4: 10
366
- };
746
+ //#region node_modules/qrcode/lib/core/utils.js
747
+ var require_utils$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
748
+ let toSJISFunction;
749
+ const CODEWORDS_COUNT = [
750
+ 0,
751
+ 26,
752
+ 44,
753
+ 70,
754
+ 100,
755
+ 134,
756
+ 172,
757
+ 196,
758
+ 242,
759
+ 292,
760
+ 346,
761
+ 404,
762
+ 466,
763
+ 532,
764
+ 581,
765
+ 655,
766
+ 733,
767
+ 815,
768
+ 901,
769
+ 991,
770
+ 1085,
771
+ 1156,
772
+ 1258,
773
+ 1364,
774
+ 1474,
775
+ 1588,
776
+ 1706,
777
+ 1828,
778
+ 1921,
779
+ 2051,
780
+ 2185,
781
+ 2323,
782
+ 2465,
783
+ 2611,
784
+ 2761,
785
+ 2876,
786
+ 3034,
787
+ 3196,
788
+ 3362,
789
+ 3532,
790
+ 3706
791
+ ];
367
792
  /**
368
- * Check if mask pattern value is valid
793
+ * Returns the QR Code size for the specified version
369
794
  *
370
- * @param {Number} mask Mask pattern
371
- * @return {Boolean} true if valid, false otherwise
795
+ * @param {Number} version QR Code version
796
+ * @return {Number} size of QR code
372
797
  */
373
- exports.isValid = function isValid(mask) {
374
- return mask != null && mask !== "" && !isNaN(mask) && mask >= 0 && mask <= 7;
798
+ exports.getSymbolSize = function getSymbolSize(version) {
799
+ if (!version) throw new Error("\"version\" cannot be null or undefined");
800
+ if (version < 1 || version > 40) throw new Error("\"version\" should be in range from 1 to 40");
801
+ return version * 4 + 17;
375
802
  };
376
803
  /**
377
- * Returns mask pattern from a value.
378
- * If value is not valid, returns undefined
804
+ * Returns the total number of codewords used to store data and EC information.
379
805
  *
380
- * @param {Number|String} value Mask pattern value
381
- * @return {Number} Valid mask pattern or undefined
806
+ * @param {Number} version QR Code version
807
+ * @return {Number} Data length in bits
382
808
  */
383
- exports.from = function from(value) {
384
- return exports.isValid(value) ? parseInt(value, 10) : void 0;
809
+ exports.getSymbolTotalCodewords = function getSymbolTotalCodewords(version) {
810
+ return CODEWORDS_COUNT[version];
385
811
  };
386
812
  /**
387
- * Find adjacent modules in row/column with the same color
388
- * and assign a penalty value.
813
+ * Encode data with Bose-Chaudhuri-Hocquenghem
389
814
  *
390
- * Points: N1 + i
391
- * i is the amount by which the number of adjacent modules of the same color exceeds 5
815
+ * @param {Number} data Value to encode
816
+ * @return {Number} Encoded value
392
817
  */
393
- exports.getPenaltyN1 = function getPenaltyN1(data) {
394
- const size = data.size;
395
- let points = 0;
396
- let sameCountCol = 0;
397
- let sameCountRow = 0;
398
- let lastCol = null;
399
- let lastRow = null;
400
- for (let row = 0; row < size; row++) {
401
- sameCountCol = sameCountRow = 0;
402
- lastCol = lastRow = null;
403
- for (let col = 0; col < size; col++) {
404
- let module$1 = data.get(row, col);
405
- if (module$1 === lastCol) sameCountCol++;
406
- else {
407
- if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5);
408
- lastCol = module$1;
409
- sameCountCol = 1;
410
- }
411
- module$1 = data.get(col, row);
412
- if (module$1 === lastRow) sameCountRow++;
413
- else {
414
- if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5);
415
- lastRow = module$1;
416
- sameCountRow = 1;
417
- }
418
- }
419
- if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5);
420
- if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5);
818
+ exports.getBCHDigit = function(data) {
819
+ let digit = 0;
820
+ while (data !== 0) {
821
+ digit++;
822
+ data >>>= 1;
421
823
  }
422
- return points;
824
+ return digit;
423
825
  };
424
- /**
425
- * Find 2x2 blocks with the same color and assign a penalty value
426
- *
427
- * Points: N2 * (m - 1) * (n - 1)
428
- */
429
- exports.getPenaltyN2 = function getPenaltyN2(data) {
430
- const size = data.size;
431
- let points = 0;
432
- for (let row = 0; row < size - 1; row++) for (let col = 0; col < size - 1; col++) {
433
- const last = data.get(row, col) + data.get(row, col + 1) + data.get(row + 1, col) + data.get(row + 1, col + 1);
434
- if (last === 4 || last === 0) points++;
826
+ exports.setToSJISFunction = function setToSJISFunction(f) {
827
+ if (typeof f !== "function") throw new Error("\"toSJISFunc\" is not a valid function.");
828
+ toSJISFunction = f;
829
+ };
830
+ exports.isKanjiModeEnabled = function() {
831
+ return typeof toSJISFunction !== "undefined";
832
+ };
833
+ exports.toSJIS = function toSJIS(kanji) {
834
+ return toSJISFunction(kanji);
835
+ };
836
+ }));
837
+ //#endregion
838
+ //#region node_modules/qrcode/lib/core/error-correction-level.js
839
+ var require_error_correction_level = /* @__PURE__ */ __commonJSMin(((exports) => {
840
+ exports.L = { bit: 1 };
841
+ exports.M = { bit: 0 };
842
+ exports.Q = { bit: 3 };
843
+ exports.H = { bit: 2 };
844
+ function fromString(string) {
845
+ if (typeof string !== "string") throw new Error("Param is not a string");
846
+ switch (string.toLowerCase()) {
847
+ case "l":
848
+ case "low": return exports.L;
849
+ case "m":
850
+ case "medium": return exports.M;
851
+ case "q":
852
+ case "quartile": return exports.Q;
853
+ case "h":
854
+ case "high": return exports.H;
855
+ default: throw new Error("Unknown EC Level: " + string);
856
+ }
857
+ }
858
+ exports.isValid = function isValid(level) {
859
+ return level && typeof level.bit !== "undefined" && level.bit >= 0 && level.bit < 4;
860
+ };
861
+ exports.from = function from(value, defaultValue) {
862
+ if (exports.isValid(value)) return value;
863
+ try {
864
+ return fromString(value);
865
+ } catch (e) {
866
+ return defaultValue;
867
+ }
868
+ };
869
+ }));
870
+ //#endregion
871
+ //#region node_modules/qrcode/lib/core/bit-buffer.js
872
+ var require_bit_buffer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
873
+ function BitBuffer() {
874
+ this.buffer = [];
875
+ this.length = 0;
876
+ }
877
+ BitBuffer.prototype = {
878
+ get: function(index) {
879
+ const bufIndex = Math.floor(index / 8);
880
+ return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) === 1;
881
+ },
882
+ put: function(num, length) {
883
+ for (let i = 0; i < length; i++) this.putBit((num >>> length - i - 1 & 1) === 1);
884
+ },
885
+ getLengthInBits: function() {
886
+ return this.length;
887
+ },
888
+ putBit: function(bit) {
889
+ const bufIndex = Math.floor(this.length / 8);
890
+ if (this.buffer.length <= bufIndex) this.buffer.push(0);
891
+ if (bit) this.buffer[bufIndex] |= 128 >>> this.length % 8;
892
+ this.length++;
435
893
  }
436
- return points * PenaltyScores.N2;
437
894
  };
895
+ module.exports = BitBuffer;
896
+ }));
897
+ //#endregion
898
+ //#region node_modules/qrcode/lib/core/bit-matrix.js
899
+ var require_bit_matrix = /* @__PURE__ */ __commonJSMin(((exports, module) => {
438
900
  /**
439
- * Find 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column,
440
- * preceded or followed by light area 4 modules wide
901
+ * Helper class to handle QR Code symbol modules
441
902
  *
442
- * Points: N3 * number of pattern found
903
+ * @param {Number} size Symbol size
443
904
  */
444
- exports.getPenaltyN3 = function getPenaltyN3(data) {
445
- const size = data.size;
446
- let points = 0;
447
- let bitsCol = 0;
448
- let bitsRow = 0;
449
- for (let row = 0; row < size; row++) {
450
- bitsCol = bitsRow = 0;
451
- for (let col = 0; col < size; col++) {
452
- bitsCol = bitsCol << 1 & 2047 | data.get(row, col);
453
- if (col >= 10 && (bitsCol === 1488 || bitsCol === 93)) points++;
454
- bitsRow = bitsRow << 1 & 2047 | data.get(col, row);
455
- if (col >= 10 && (bitsRow === 1488 || bitsRow === 93)) points++;
456
- }
457
- }
458
- return points * PenaltyScores.N3;
905
+ function BitMatrix(size) {
906
+ if (!size || size < 1) throw new Error("BitMatrix size must be defined and greater than 0");
907
+ this.size = size;
908
+ this.data = new Uint8Array(size * size);
909
+ this.reservedBit = new Uint8Array(size * size);
910
+ }
911
+ /**
912
+ * Set bit value at specified location
913
+ * If reserved flag is set, this bit will be ignored during masking process
914
+ *
915
+ * @param {Number} row
916
+ * @param {Number} col
917
+ * @param {Boolean} value
918
+ * @param {Boolean} reserved
919
+ */
920
+ BitMatrix.prototype.set = function(row, col, value, reserved) {
921
+ const index = row * this.size + col;
922
+ this.data[index] = value;
923
+ if (reserved) this.reservedBit[index] = true;
459
924
  };
460
925
  /**
461
- * Calculate proportion of dark modules in entire symbol
926
+ * Returns bit value at specified location
462
927
  *
463
- * Points: N4 * k
928
+ * @param {Number} row
929
+ * @param {Number} col
930
+ * @return {Boolean}
931
+ */
932
+ BitMatrix.prototype.get = function(row, col) {
933
+ return this.data[row * this.size + col];
934
+ };
935
+ /**
936
+ * Applies xor operator at specified location
937
+ * (used during masking process)
464
938
  *
465
- * k is the rating of the deviation of the proportion of dark modules
466
- * in the symbol from 50% in steps of 5%
939
+ * @param {Number} row
940
+ * @param {Number} col
941
+ * @param {Boolean} value
467
942
  */
468
- exports.getPenaltyN4 = function getPenaltyN4(data) {
469
- let darkCount = 0;
470
- const modulesCount = data.data.length;
471
- for (let i = 0; i < modulesCount; i++) darkCount += data.data[i];
472
- return Math.abs(Math.ceil(darkCount * 100 / modulesCount / 5) - 10) * PenaltyScores.N4;
943
+ BitMatrix.prototype.xor = function(row, col, value) {
944
+ this.data[row * this.size + col] ^= value;
473
945
  };
474
946
  /**
475
- * Return mask value at given position
947
+ * Check if bit at specified location is reserved
476
948
  *
477
- * @param {Number} maskPattern Pattern reference value
478
- * @param {Number} i Row
479
- * @param {Number} j Column
480
- * @return {Boolean} Mask value
949
+ * @param {Number} row
950
+ * @param {Number} col
951
+ * @return {Boolean}
481
952
  */
482
- function getMaskAt(maskPattern, i, j) {
483
- switch (maskPattern) {
484
- case exports.Patterns.PATTERN000: return (i + j) % 2 === 0;
485
- case exports.Patterns.PATTERN001: return i % 2 === 0;
486
- case exports.Patterns.PATTERN010: return j % 3 === 0;
487
- case exports.Patterns.PATTERN011: return (i + j) % 3 === 0;
488
- case exports.Patterns.PATTERN100: return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
489
- case exports.Patterns.PATTERN101: return i * j % 2 + i * j % 3 === 0;
490
- case exports.Patterns.PATTERN110: return (i * j % 2 + i * j % 3) % 2 === 0;
491
- case exports.Patterns.PATTERN111: return (i * j % 3 + (i + j) % 2) % 2 === 0;
492
- default: throw new Error("bad maskPattern:" + maskPattern);
493
- }
494
- }
953
+ BitMatrix.prototype.isReserved = function(row, col) {
954
+ return this.reservedBit[row * this.size + col];
955
+ };
956
+ module.exports = BitMatrix;
957
+ }));
958
+ //#endregion
959
+ //#region node_modules/qrcode/lib/core/alignment-pattern.js
960
+ var require_alignment_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
495
961
  /**
496
- * Apply a mask pattern to a BitMatrix
962
+ * Alignment pattern are fixed reference pattern in defined positions
963
+ * in a matrix symbology, which enables the decode software to re-synchronise
964
+ * the coordinate mapping of the image modules in the event of moderate amounts
965
+ * of distortion of the image.
497
966
  *
498
- * @param {Number} pattern Pattern reference number
499
- * @param {BitMatrix} data BitMatrix data
967
+ * Alignment patterns are present only in QR Code symbols of version 2 or larger
968
+ * and their number depends on the symbol version.
500
969
  */
501
- exports.applyMask = function applyMask(pattern, data) {
502
- const size = data.size;
503
- for (let col = 0; col < size; col++) for (let row = 0; row < size; row++) {
504
- if (data.isReserved(row, col)) continue;
505
- data.xor(row, col, getMaskAt(pattern, row, col));
506
- }
970
+ const getSymbolSize = require_utils$1().getSymbolSize;
971
+ /**
972
+ * Calculate the row/column coordinates of the center module of each alignment pattern
973
+ * for the specified QR Code version.
974
+ *
975
+ * The alignment patterns are positioned symmetrically on either side of the diagonal
976
+ * running from the top left corner of the symbol to the bottom right corner.
977
+ *
978
+ * Since positions are simmetrical only half of the coordinates are returned.
979
+ * Each item of the array will represent in turn the x and y coordinate.
980
+ * @see {@link getPositions}
981
+ *
982
+ * @param {Number} version QR Code version
983
+ * @return {Array} Array of coordinate
984
+ */
985
+ exports.getRowColCoords = function getRowColCoords(version) {
986
+ if (version === 1) return [];
987
+ const posCount = Math.floor(version / 7) + 2;
988
+ const size = getSymbolSize(version);
989
+ const intervals = size === 145 ? 26 : Math.ceil((size - 13) / (2 * posCount - 2)) * 2;
990
+ const positions = [size - 7];
991
+ for (let i = 1; i < posCount - 1; i++) positions[i] = positions[i - 1] - intervals;
992
+ positions.push(6);
993
+ return positions.reverse();
507
994
  };
508
995
  /**
509
- * Returns the best mask pattern for data
996
+ * Returns an array containing the positions of each alignment pattern.
997
+ * Each array's element represent the center point of the pattern as (x, y) coordinates
510
998
  *
511
- * @param {BitMatrix} data
512
- * @return {Number} Mask pattern reference number
999
+ * Coordinates are calculated expanding the row/column coordinates returned by {@link getRowColCoords}
1000
+ * and filtering out the items that overlaps with finder pattern
1001
+ *
1002
+ * @example
1003
+ * For a Version 7 symbol {@link getRowColCoords} returns values 6, 22 and 38.
1004
+ * The alignment patterns, therefore, are to be centered on (row, column)
1005
+ * positions (6,22), (22,6), (22,22), (22,38), (38,22), (38,38).
1006
+ * Note that the coordinates (6,6), (6,38), (38,6) are occupied by finder patterns
1007
+ * and are not therefore used for alignment patterns.
1008
+ *
1009
+ * let pos = getPositions(7)
1010
+ * // [[6,22], [22,6], [22,22], [22,38], [38,22], [38,38]]
1011
+ *
1012
+ * @param {Number} version QR Code version
1013
+ * @return {Array} Array of coordinates
513
1014
  */
514
- exports.getBestMask = function getBestMask(data, setupFormatFunc) {
515
- const numPatterns = Object.keys(exports.Patterns).length;
516
- let bestPattern = 0;
517
- let lowerPenalty = Infinity;
518
- for (let p = 0; p < numPatterns; p++) {
519
- setupFormatFunc(p);
520
- exports.applyMask(p, data);
521
- const penalty = exports.getPenaltyN1(data) + exports.getPenaltyN2(data) + exports.getPenaltyN3(data) + exports.getPenaltyN4(data);
522
- exports.applyMask(p, data);
523
- if (penalty < lowerPenalty) {
524
- lowerPenalty = penalty;
525
- bestPattern = p;
526
- }
1015
+ exports.getPositions = function getPositions(version) {
1016
+ const coords = [];
1017
+ const pos = exports.getRowColCoords(version);
1018
+ const posLength = pos.length;
1019
+ for (let i = 0; i < posLength; i++) for (let j = 0; j < posLength; j++) {
1020
+ if (i === 0 && j === 0 || i === 0 && j === posLength - 1 || i === posLength - 1 && j === 0) continue;
1021
+ coords.push([pos[i], pos[j]]);
527
1022
  }
528
- return bestPattern;
1023
+ return coords;
529
1024
  };
530
1025
  }));
531
1026
  //#endregion
532
- //#region node_modules/qrcode/lib/core/error-correction-code.js
533
- var require_error_correction_code = /* @__PURE__ */ __commonJSMin(((exports) => {
1027
+ //#region node_modules/qrcode/lib/core/finder-pattern.js
1028
+ var require_finder_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1029
+ const getSymbolSize = require_utils$1().getSymbolSize;
1030
+ const FINDER_PATTERN_SIZE = 7;
1031
+ /**
1032
+ * Returns an array containing the positions of each finder pattern.
1033
+ * Each array's element represent the top-left point of the pattern as (x, y) coordinates
1034
+ *
1035
+ * @param {Number} version QR Code version
1036
+ * @return {Array} Array of coordinates
1037
+ */
1038
+ exports.getPositions = function getPositions(version) {
1039
+ const size = getSymbolSize(version);
1040
+ return [
1041
+ [0, 0],
1042
+ [size - FINDER_PATTERN_SIZE, 0],
1043
+ [0, size - FINDER_PATTERN_SIZE]
1044
+ ];
1045
+ };
1046
+ }));
1047
+ //#endregion
1048
+ //#region node_modules/qrcode/lib/core/mask-pattern.js
1049
+ var require_mask_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
1050
+ /**
1051
+ * Data mask pattern reference
1052
+ * @type {Object}
1053
+ */
1054
+ exports.Patterns = {
1055
+ PATTERN000: 0,
1056
+ PATTERN001: 1,
1057
+ PATTERN010: 2,
1058
+ PATTERN011: 3,
1059
+ PATTERN100: 4,
1060
+ PATTERN101: 5,
1061
+ PATTERN110: 6,
1062
+ PATTERN111: 7
1063
+ };
1064
+ /**
1065
+ * Weighted penalty scores for the undesirable features
1066
+ * @type {Object}
1067
+ */
1068
+ const PenaltyScores = {
1069
+ N1: 3,
1070
+ N2: 3,
1071
+ N3: 40,
1072
+ N4: 10
1073
+ };
1074
+ /**
1075
+ * Check if mask pattern value is valid
1076
+ *
1077
+ * @param {Number} mask Mask pattern
1078
+ * @return {Boolean} true if valid, false otherwise
1079
+ */
1080
+ exports.isValid = function isValid(mask) {
1081
+ return mask != null && mask !== "" && !isNaN(mask) && mask >= 0 && mask <= 7;
1082
+ };
1083
+ /**
1084
+ * Returns mask pattern from a value.
1085
+ * If value is not valid, returns undefined
1086
+ *
1087
+ * @param {Number|String} value Mask pattern value
1088
+ * @return {Number} Valid mask pattern or undefined
1089
+ */
1090
+ exports.from = function from(value) {
1091
+ return exports.isValid(value) ? parseInt(value, 10) : void 0;
1092
+ };
1093
+ /**
1094
+ * Find adjacent modules in row/column with the same color
1095
+ * and assign a penalty value.
1096
+ *
1097
+ * Points: N1 + i
1098
+ * i is the amount by which the number of adjacent modules of the same color exceeds 5
1099
+ */
1100
+ exports.getPenaltyN1 = function getPenaltyN1(data) {
1101
+ const size = data.size;
1102
+ let points = 0;
1103
+ let sameCountCol = 0;
1104
+ let sameCountRow = 0;
1105
+ let lastCol = null;
1106
+ let lastRow = null;
1107
+ for (let row = 0; row < size; row++) {
1108
+ sameCountCol = sameCountRow = 0;
1109
+ lastCol = lastRow = null;
1110
+ for (let col = 0; col < size; col++) {
1111
+ let module$1 = data.get(row, col);
1112
+ if (module$1 === lastCol) sameCountCol++;
1113
+ else {
1114
+ if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5);
1115
+ lastCol = module$1;
1116
+ sameCountCol = 1;
1117
+ }
1118
+ module$1 = data.get(col, row);
1119
+ if (module$1 === lastRow) sameCountRow++;
1120
+ else {
1121
+ if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5);
1122
+ lastRow = module$1;
1123
+ sameCountRow = 1;
1124
+ }
1125
+ }
1126
+ if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5);
1127
+ if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5);
1128
+ }
1129
+ return points;
1130
+ };
1131
+ /**
1132
+ * Find 2x2 blocks with the same color and assign a penalty value
1133
+ *
1134
+ * Points: N2 * (m - 1) * (n - 1)
1135
+ */
1136
+ exports.getPenaltyN2 = function getPenaltyN2(data) {
1137
+ const size = data.size;
1138
+ let points = 0;
1139
+ for (let row = 0; row < size - 1; row++) for (let col = 0; col < size - 1; col++) {
1140
+ const last = data.get(row, col) + data.get(row, col + 1) + data.get(row + 1, col) + data.get(row + 1, col + 1);
1141
+ if (last === 4 || last === 0) points++;
1142
+ }
1143
+ return points * PenaltyScores.N2;
1144
+ };
1145
+ /**
1146
+ * Find 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column,
1147
+ * preceded or followed by light area 4 modules wide
1148
+ *
1149
+ * Points: N3 * number of pattern found
1150
+ */
1151
+ exports.getPenaltyN3 = function getPenaltyN3(data) {
1152
+ const size = data.size;
1153
+ let points = 0;
1154
+ let bitsCol = 0;
1155
+ let bitsRow = 0;
1156
+ for (let row = 0; row < size; row++) {
1157
+ bitsCol = bitsRow = 0;
1158
+ for (let col = 0; col < size; col++) {
1159
+ bitsCol = bitsCol << 1 & 2047 | data.get(row, col);
1160
+ if (col >= 10 && (bitsCol === 1488 || bitsCol === 93)) points++;
1161
+ bitsRow = bitsRow << 1 & 2047 | data.get(col, row);
1162
+ if (col >= 10 && (bitsRow === 1488 || bitsRow === 93)) points++;
1163
+ }
1164
+ }
1165
+ return points * PenaltyScores.N3;
1166
+ };
1167
+ /**
1168
+ * Calculate proportion of dark modules in entire symbol
1169
+ *
1170
+ * Points: N4 * k
1171
+ *
1172
+ * k is the rating of the deviation of the proportion of dark modules
1173
+ * in the symbol from 50% in steps of 5%
1174
+ */
1175
+ exports.getPenaltyN4 = function getPenaltyN4(data) {
1176
+ let darkCount = 0;
1177
+ const modulesCount = data.data.length;
1178
+ for (let i = 0; i < modulesCount; i++) darkCount += data.data[i];
1179
+ return Math.abs(Math.ceil(darkCount * 100 / modulesCount / 5) - 10) * PenaltyScores.N4;
1180
+ };
1181
+ /**
1182
+ * Return mask value at given position
1183
+ *
1184
+ * @param {Number} maskPattern Pattern reference value
1185
+ * @param {Number} i Row
1186
+ * @param {Number} j Column
1187
+ * @return {Boolean} Mask value
1188
+ */
1189
+ function getMaskAt(maskPattern, i, j) {
1190
+ switch (maskPattern) {
1191
+ case exports.Patterns.PATTERN000: return (i + j) % 2 === 0;
1192
+ case exports.Patterns.PATTERN001: return i % 2 === 0;
1193
+ case exports.Patterns.PATTERN010: return j % 3 === 0;
1194
+ case exports.Patterns.PATTERN011: return (i + j) % 3 === 0;
1195
+ case exports.Patterns.PATTERN100: return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
1196
+ case exports.Patterns.PATTERN101: return i * j % 2 + i * j % 3 === 0;
1197
+ case exports.Patterns.PATTERN110: return (i * j % 2 + i * j % 3) % 2 === 0;
1198
+ case exports.Patterns.PATTERN111: return (i * j % 3 + (i + j) % 2) % 2 === 0;
1199
+ default: throw new Error("bad maskPattern:" + maskPattern);
1200
+ }
1201
+ }
1202
+ /**
1203
+ * Apply a mask pattern to a BitMatrix
1204
+ *
1205
+ * @param {Number} pattern Pattern reference number
1206
+ * @param {BitMatrix} data BitMatrix data
1207
+ */
1208
+ exports.applyMask = function applyMask(pattern, data) {
1209
+ const size = data.size;
1210
+ for (let col = 0; col < size; col++) for (let row = 0; row < size; row++) {
1211
+ if (data.isReserved(row, col)) continue;
1212
+ data.xor(row, col, getMaskAt(pattern, row, col));
1213
+ }
1214
+ };
1215
+ /**
1216
+ * Returns the best mask pattern for data
1217
+ *
1218
+ * @param {BitMatrix} data
1219
+ * @return {Number} Mask pattern reference number
1220
+ */
1221
+ exports.getBestMask = function getBestMask(data, setupFormatFunc) {
1222
+ const numPatterns = Object.keys(exports.Patterns).length;
1223
+ let bestPattern = 0;
1224
+ let lowerPenalty = Infinity;
1225
+ for (let p = 0; p < numPatterns; p++) {
1226
+ setupFormatFunc(p);
1227
+ exports.applyMask(p, data);
1228
+ const penalty = exports.getPenaltyN1(data) + exports.getPenaltyN2(data) + exports.getPenaltyN3(data) + exports.getPenaltyN4(data);
1229
+ exports.applyMask(p, data);
1230
+ if (penalty < lowerPenalty) {
1231
+ lowerPenalty = penalty;
1232
+ bestPattern = p;
1233
+ }
1234
+ }
1235
+ return bestPattern;
1236
+ };
1237
+ }));
1238
+ //#endregion
1239
+ //#region node_modules/qrcode/lib/core/error-correction-code.js
1240
+ var require_error_correction_code = /* @__PURE__ */ __commonJSMin(((exports) => {
534
1241
  const ECLevel = require_error_correction_level();
535
1242
  const EC_BLOCKS_TABLE = [
536
1243
  1,
@@ -2190,893 +2897,350 @@ window.__ModuleLoader__.load({
2190
2897
  * @param {Function} options.toSJISFunc Helper func to convert utf8 to sjis
2191
2898
  */
2192
2899
  exports.create = function create(data, options) {
2193
- if (typeof data === "undefined" || data === "") throw new Error("No input text");
2194
- let errorCorrectionLevel = ECLevel.M;
2195
- let version;
2196
- let mask;
2197
- if (typeof options !== "undefined") {
2198
- errorCorrectionLevel = ECLevel.from(options.errorCorrectionLevel, ECLevel.M);
2199
- version = Version.from(options.version);
2200
- mask = MaskPattern.from(options.maskPattern);
2201
- if (options.toSJISFunc) Utils.setToSJISFunction(options.toSJISFunc);
2202
- }
2203
- return createSymbol(data, version, errorCorrectionLevel, mask);
2204
- };
2205
- }));
2206
- //#endregion
2207
- //#region node_modules/qrcode/lib/renderer/utils.js
2208
- var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
2209
- function hex2rgba(hex) {
2210
- if (typeof hex === "number") hex = hex.toString();
2211
- if (typeof hex !== "string") throw new Error("Color should be defined as hex string");
2212
- let hexCode = hex.slice().replace("#", "").split("");
2213
- if (hexCode.length < 3 || hexCode.length === 5 || hexCode.length > 8) throw new Error("Invalid hex color: " + hex);
2214
- if (hexCode.length === 3 || hexCode.length === 4) hexCode = Array.prototype.concat.apply([], hexCode.map(function(c) {
2215
- return [c, c];
2216
- }));
2217
- if (hexCode.length === 6) hexCode.push("F", "F");
2218
- const hexValue = parseInt(hexCode.join(""), 16);
2219
- return {
2220
- r: hexValue >> 24 & 255,
2221
- g: hexValue >> 16 & 255,
2222
- b: hexValue >> 8 & 255,
2223
- a: hexValue & 255,
2224
- hex: "#" + hexCode.slice(0, 6).join("")
2225
- };
2226
- }
2227
- exports.getOptions = function getOptions(options) {
2228
- if (!options) options = {};
2229
- if (!options.color) options.color = {};
2230
- const margin = typeof options.margin === "undefined" || options.margin === null || options.margin < 0 ? 4 : options.margin;
2231
- const width = options.width && options.width >= 21 ? options.width : void 0;
2232
- const scale = options.scale || 4;
2233
- return {
2234
- width,
2235
- scale: width ? 4 : scale,
2236
- margin,
2237
- color: {
2238
- dark: hex2rgba(options.color.dark || "#000000ff"),
2239
- light: hex2rgba(options.color.light || "#ffffffff")
2240
- },
2241
- type: options.type,
2242
- rendererOpts: options.rendererOpts || {}
2243
- };
2244
- };
2245
- exports.getScale = function getScale(qrSize, opts) {
2246
- return opts.width && opts.width >= qrSize + opts.margin * 2 ? opts.width / (qrSize + opts.margin * 2) : opts.scale;
2247
- };
2248
- exports.getImageWidth = function getImageWidth(qrSize, opts) {
2249
- const scale = exports.getScale(qrSize, opts);
2250
- return Math.floor((qrSize + opts.margin * 2) * scale);
2251
- };
2252
- exports.qrToImageData = function qrToImageData(imgData, qr, opts) {
2253
- const size = qr.modules.size;
2254
- const data = qr.modules.data;
2255
- const scale = exports.getScale(size, opts);
2256
- const symbolSize = Math.floor((size + opts.margin * 2) * scale);
2257
- const scaledMargin = opts.margin * scale;
2258
- const palette = [opts.color.light, opts.color.dark];
2259
- for (let i = 0; i < symbolSize; i++) for (let j = 0; j < symbolSize; j++) {
2260
- let posDst = (i * symbolSize + j) * 4;
2261
- let pxColor = opts.color.light;
2262
- if (i >= scaledMargin && j >= scaledMargin && i < symbolSize - scaledMargin && j < symbolSize - scaledMargin) {
2263
- const iSrc = Math.floor((i - scaledMargin) / scale);
2264
- const jSrc = Math.floor((j - scaledMargin) / scale);
2265
- pxColor = palette[data[iSrc * size + jSrc] ? 1 : 0];
2266
- }
2267
- imgData[posDst++] = pxColor.r;
2268
- imgData[posDst++] = pxColor.g;
2269
- imgData[posDst++] = pxColor.b;
2270
- imgData[posDst] = pxColor.a;
2271
- }
2272
- };
2273
- }));
2274
- //#endregion
2275
- //#region node_modules/qrcode/lib/renderer/canvas.js
2276
- var require_canvas = /* @__PURE__ */ __commonJSMin(((exports) => {
2277
- const Utils = require_utils();
2278
- function clearCanvas(ctx, canvas, size) {
2279
- ctx.clearRect(0, 0, canvas.width, canvas.height);
2280
- if (!canvas.style) canvas.style = {};
2281
- canvas.height = size;
2282
- canvas.width = size;
2283
- canvas.style.height = size + "px";
2284
- canvas.style.width = size + "px";
2285
- }
2286
- function getCanvasElement() {
2287
- try {
2288
- return document.createElement("canvas");
2289
- } catch (e) {
2290
- throw new Error("You need to specify a canvas element");
2291
- }
2292
- }
2293
- exports.render = function render(qrData, canvas, options) {
2294
- let opts = options;
2295
- let canvasEl = canvas;
2296
- if (typeof opts === "undefined" && (!canvas || !canvas.getContext)) {
2297
- opts = canvas;
2298
- canvas = void 0;
2299
- }
2300
- if (!canvas) canvasEl = getCanvasElement();
2301
- opts = Utils.getOptions(opts);
2302
- const size = Utils.getImageWidth(qrData.modules.size, opts);
2303
- const ctx = canvasEl.getContext("2d");
2304
- const image = ctx.createImageData(size, size);
2305
- Utils.qrToImageData(image.data, qrData, opts);
2306
- clearCanvas(ctx, canvasEl, size);
2307
- ctx.putImageData(image, 0, 0);
2308
- return canvasEl;
2309
- };
2310
- exports.renderToDataURL = function renderToDataURL(qrData, canvas, options) {
2311
- let opts = options;
2312
- if (typeof opts === "undefined" && (!canvas || !canvas.getContext)) {
2313
- opts = canvas;
2314
- canvas = void 0;
2315
- }
2316
- if (!opts) opts = {};
2317
- const canvasEl = exports.render(qrData, canvas, opts);
2318
- const type = opts.type || "image/png";
2319
- const rendererOpts = opts.rendererOpts || {};
2320
- return canvasEl.toDataURL(type, rendererOpts.quality);
2321
- };
2322
- }));
2323
- //#endregion
2324
- //#region node_modules/qrcode/lib/renderer/svg-tag.js
2325
- var require_svg_tag = /* @__PURE__ */ __commonJSMin(((exports) => {
2326
- const Utils = require_utils();
2327
- function getColorAttrib(color, attrib) {
2328
- const alpha = color.a / 255;
2329
- const str = attrib + "=\"" + color.hex + "\"";
2330
- return alpha < 1 ? str + " " + attrib + "-opacity=\"" + alpha.toFixed(2).slice(1) + "\"" : str;
2331
- }
2332
- function svgCmd(cmd, x, y) {
2333
- let str = cmd + x;
2334
- if (typeof y !== "undefined") str += " " + y;
2335
- return str;
2336
- }
2337
- function qrToPath(data, size, margin) {
2338
- let path = "";
2339
- let moveBy = 0;
2340
- let newRow = false;
2341
- let lineLength = 0;
2342
- for (let i = 0; i < data.length; i++) {
2343
- const col = Math.floor(i % size);
2344
- const row = Math.floor(i / size);
2345
- if (!col && !newRow) newRow = true;
2346
- if (data[i]) {
2347
- lineLength++;
2348
- if (!(i > 0 && col > 0 && data[i - 1])) {
2349
- path += newRow ? svgCmd("M", col + margin, .5 + row + margin) : svgCmd("m", moveBy, 0);
2350
- moveBy = 0;
2351
- newRow = false;
2352
- }
2353
- if (!(col + 1 < size && data[i + 1])) {
2354
- path += svgCmd("h", lineLength);
2355
- lineLength = 0;
2356
- }
2357
- } else moveBy++;
2358
- }
2359
- return path;
2360
- }
2361
- exports.render = function render(qrData, options, cb) {
2362
- const opts = Utils.getOptions(options);
2363
- const size = qrData.modules.size;
2364
- const data = qrData.modules.data;
2365
- const qrcodesize = size + opts.margin * 2;
2366
- const bg = !opts.color.light.a ? "" : "<path " + getColorAttrib(opts.color.light, "fill") + " d=\"M0 0h" + qrcodesize + "v" + qrcodesize + "H0z\"/>";
2367
- const path = "<path " + getColorAttrib(opts.color.dark, "stroke") + " d=\"" + qrToPath(data, size, opts.margin) + "\"/>";
2368
- const viewBox = "viewBox=\"0 0 " + qrcodesize + " " + qrcodesize + "\"";
2369
- const svgTag = "<svg xmlns=\"http://www.w3.org/2000/svg\" " + (!opts.width ? "" : "width=\"" + opts.width + "\" height=\"" + opts.width + "\" ") + viewBox + " shape-rendering=\"crispEdges\">" + bg + path + "</svg>\n";
2370
- if (typeof cb === "function") cb(null, svgTag);
2371
- return svgTag;
2372
- };
2373
- }));
2374
- //#endregion
2375
- //#region src/pairing-qr.ts
2376
- var import_browser = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
2377
- const canPromise = require_can_promise();
2378
- const QRCode = require_qrcode();
2379
- const CanvasRenderer = require_canvas();
2380
- const SvgRenderer = require_svg_tag();
2381
- function renderCanvas(renderFunc, canvas, text, opts, cb) {
2382
- const args = [].slice.call(arguments, 1);
2383
- const argsNum = args.length;
2384
- const isLastArgCb = typeof args[argsNum - 1] === "function";
2385
- if (!isLastArgCb && !canPromise()) throw new Error("Callback required as last argument");
2386
- if (isLastArgCb) {
2387
- if (argsNum < 2) throw new Error("Too few arguments provided");
2388
- if (argsNum === 2) {
2389
- cb = text;
2390
- text = canvas;
2391
- canvas = opts = void 0;
2392
- } else if (argsNum === 3) {
2393
- if (canvas.getContext && typeof cb === "undefined") {
2394
- cb = opts;
2395
- opts = void 0;
2396
- } else {
2397
- cb = opts;
2398
- opts = text;
2399
- text = canvas;
2400
- canvas = void 0;
2401
- }
2402
- }
2403
- } else {
2404
- if (argsNum < 1) throw new Error("Too few arguments provided");
2405
- if (argsNum === 1) {
2406
- text = canvas;
2407
- canvas = opts = void 0;
2408
- } else if (argsNum === 2 && !canvas.getContext) {
2409
- opts = text;
2410
- text = canvas;
2411
- canvas = void 0;
2412
- }
2413
- return new Promise(function(resolve, reject) {
2414
- try {
2415
- resolve(renderFunc(QRCode.create(text, opts), canvas, opts));
2416
- } catch (e) {
2417
- reject(e);
2418
- }
2419
- });
2420
- }
2421
- try {
2422
- const data = QRCode.create(text, opts);
2423
- cb(null, renderFunc(data, canvas, opts));
2424
- } catch (e) {
2425
- cb(e);
2426
- }
2427
- }
2428
- exports.create = QRCode.create;
2429
- exports.toCanvas = renderCanvas.bind(null, CanvasRenderer.render);
2430
- exports.toDataURL = renderCanvas.bind(null, CanvasRenderer.renderToDataURL);
2431
- exports.toString = renderCanvas.bind(null, function(data, _, opts) {
2432
- return SvgRenderer.render(data, opts);
2433
- });
2434
- })))(), 1);
2435
- /**
2436
- * Protocol-v1 compatibility identifier used by the TestFlight build currently
2437
- * under review. This is wire data, not the plugin or product display name.
2438
- */
2439
- const PAIRING_QR_TYPE = "dsh-pocket-pairing";
2440
- function isLoopbackHostname(hostname) {
2441
- const normalized = hostname.toLowerCase();
2442
- return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "[::1]" || normalized === "::1" || normalized.startsWith("127.");
2443
- }
2444
- /** Prefer an online Funnel; otherwise turn the current web origin into a LAN target. */
2445
- function selectPairingTarget(remote, lanAddresses, currentOrigin) {
2446
- let origin;
2447
- try {
2448
- if (currentOrigin) origin = new URL(currentOrigin);
2449
- } catch {}
2450
- if (remote.publicURL && origin?.origin === remote.publicURL) return {
2451
- host: remote.publicURL,
2452
- kind: "public"
2453
- };
2454
- if (remote.phase === "online" && remote.publicURL) return {
2455
- host: remote.publicURL,
2456
- kind: "public"
2457
- };
2458
- if (origin && ["http:", "https:"].includes(origin.protocol) && !isLoopbackHostname(origin.hostname)) return {
2459
- host: origin.origin,
2460
- kind: "lan"
2461
- };
2462
- const address = lanAddresses[0];
2463
- if (!address) return null;
2464
- return {
2465
- host: `${origin?.protocol === "https:" ? "https:" : "http:"}//${address}${origin?.port ? `:${origin.port}` : ""}`,
2466
- kind: "lan"
2467
- };
2468
- }
2469
- /** Encode an out-of-band pairing payload without putting the token in a URL. */
2470
- function encodePairingQRPayload(host, token) {
2471
- const normalizedHost = host.trim();
2472
- const parsed = new URL(normalizedHost);
2473
- if (![
2474
- "http:",
2475
- "https:",
2476
- "ws:",
2477
- "wss:"
2478
- ].includes(parsed.protocol) || parsed.hostname === "" || parsed.username !== "" || parsed.password !== "") throw new TypeError("pairing QR requires a valid HTTP(S)/WS(S) host");
2479
- if (token.trim().length < 32) throw new TypeError("pairing token is invalid");
2480
- const payload = {
2481
- v: 1,
2482
- type: PAIRING_QR_TYPE,
2483
- host: normalizedHost,
2484
- token: token.trim()
2485
- };
2486
- return JSON.stringify(payload);
2487
- }
2488
- //#endregion
2489
- //#region src/report-wire.ts
2490
- /** The npm package identity both contribution registrations claim. */
2491
- const REPORT_REMOTE_PACKAGE = "dsh-deeppilot";
2492
- /** Canonical `<namespace>/<method>` endpoint of the report Remote. */
2493
- const REPORT_ENDPOINT = "deeppilot/report";
2494
- /** Explicit, user-triggered endpoint for revealing the pairing secret. */
2495
- const REVEAL_TOKEN_ENDPOINT = "deeppilot/revealToken";
2496
- /**
2497
- * Explicit, user-triggered endpoint that replaces the pairing secret. The old
2498
- * token stops working immediately; the fresh one is returned so the page can
2499
- * show/QR it right away.
2500
- */
2501
- const ROTATE_TOKEN_ENDPOINT = "deeppilot/rotateToken";
2502
- function reject(field) {
2503
- throw new TypeError(`deeppilot/report result: invalid ${field}`);
2504
- }
2505
- function str(source, key, field) {
2506
- const value = source[key];
2507
- if (typeof value !== "string") reject(field);
2508
- return value;
2509
- }
2510
- function num(source, key, field) {
2511
- const value = source[key];
2512
- if (typeof value !== "number" || !Number.isFinite(value)) reject(field);
2513
- return value;
2514
- }
2515
- function bool(source, key, field) {
2516
- const value = source[key];
2517
- if (typeof value !== "boolean") reject(field);
2518
- return value;
2519
- }
2520
- function rec(value, field) {
2521
- if (typeof value !== "object" || value === null || Array.isArray(value)) reject(field);
2522
- return value;
2523
- }
2524
- function parseDevice(value) {
2525
- const s = rec(value, "device");
2526
- let apns;
2527
- if (s.apns !== void 0) {
2528
- const a = rec(s.apns, "device.apns");
2529
- const environment = str(a, "environment", "device.apns.environment");
2530
- if (environment !== "development" && environment !== "production") reject("device.apns.environment");
2531
- apns = {
2532
- environment,
2533
- updatedAt: num(a, "updatedAt", "device.apns.updatedAt")
2534
- };
2535
- }
2536
- return {
2537
- deviceId: str(s, "deviceId", "device.deviceId"),
2538
- deviceName: str(s, "deviceName", "device.deviceName"),
2539
- appVersion: str(s, "appVersion", "device.appVersion"),
2540
- firstSeenTs: num(s, "firstSeenTs", "device.firstSeenTs"),
2541
- lastSeenTs: num(s, "lastSeenTs", "device.lastSeenTs"),
2542
- ...apns ? { apns } : {}
2543
- };
2544
- }
2545
- function parseRemote(value) {
2546
- const s = rec(value, "remote");
2547
- const provider = str(s, "provider", "remote.provider");
2548
- const phase = str(s, "phase", "remote.phase");
2549
- if (provider !== "tailscale-funnel") reject("remote.provider");
2550
- if (![
2551
- "disabled",
2552
- "starting",
2553
- "login_required",
2554
- "online",
2555
- "error",
2556
- "unavailable",
2557
- "stopped"
2558
- ].includes(phase)) reject("remote.phase");
2559
- const publicURL = s.publicURL;
2560
- const authURL = s.authURL;
2561
- const message = s.message;
2562
- if (publicURL !== void 0 && typeof publicURL !== "string") reject("remote.publicURL");
2563
- if (authURL !== void 0 && typeof authURL !== "string") reject("remote.authURL");
2564
- if (message !== void 0 && typeof message !== "string") reject("remote.message");
2565
- return {
2566
- provider,
2567
- phase,
2568
- ...typeof publicURL === "string" ? { publicURL } : {},
2569
- ...typeof authURL === "string" ? { authURL } : {},
2570
- ...typeof message === "string" ? { message } : {},
2571
- updatedAt: num(s, "updatedAt", "remote.updatedAt")
2572
- };
2573
- }
2574
- function parseRelayTestStep(value) {
2575
- const st = rec(value, "step");
2576
- const id = str(st, "id", "step.id");
2577
- if (id !== "health" && id !== "enroll") reject("step.id");
2578
- const latencyMs = st.latencyMs;
2579
- if (latencyMs !== void 0 && typeof latencyMs !== "number") reject("step.latencyMs");
2580
- return {
2581
- id,
2582
- ok: bool(st, "ok", "step.ok"),
2583
- message: str(st, "message", "step.message"),
2584
- ...typeof latencyMs === "number" ? { latencyMs } : {}
2585
- };
2586
- }
2587
- function parseRelayTestResult(value) {
2588
- const s = rec(value, "result");
2589
- const overall = str(s, "overall", "overall");
2590
- if (overall !== "ok" && overall !== "failed") reject("overall");
2591
- const stepsRaw = s.steps;
2592
- if (!Array.isArray(stepsRaw)) reject("steps");
2593
- return {
2594
- url: str(s, "url", "url"),
2595
- overall,
2596
- tokenIssued: bool(s, "tokenIssued", "tokenIssued"),
2597
- steps: stepsRaw.map(parseRelayTestStep)
2598
- };
2599
- }
2600
- function parsePushTestResult(value) {
2601
- const s = rec(value, "result");
2602
- const transport = str(s, "transport", "transport");
2603
- if (transport !== "apns" && transport !== "relay" && transport !== "none") reject("transport");
2604
- const overall = str(s, "overall", "overall");
2605
- if (![
2606
- "sent",
2607
- "failed",
2608
- "no-targets",
2609
- "not-configured"
2610
- ].includes(overall)) reject("overall");
2611
- const resultsRaw = s.results;
2612
- if (!Array.isArray(resultsRaw)) reject("results");
2613
- const results = resultsRaw.map((value) => {
2614
- const r = rec(value, "device result");
2615
- const reason = r.reason;
2616
- const tokenFingerprint = r.tokenFingerprint;
2617
- return {
2618
- name: str(r, "name", "result.name"),
2619
- environment: str(r, "environment", "result.environment"),
2620
- outcome: str(r, "outcome", "result.outcome"),
2621
- ...typeof reason === "string" && reason.length > 0 ? { reason } : {},
2622
- ...typeof tokenFingerprint === "string" && /^[0-9a-f]{1,32}$/.test(tokenFingerprint) ? { tokenFingerprint } : {}
2623
- };
2624
- });
2625
- const message = s.message;
2626
- return {
2627
- transport,
2628
- overall,
2629
- ...typeof message === "string" && message.length > 0 ? { message } : {},
2630
- results
2631
- };
2632
- }
2633
- function parseReport(value) {
2634
- const s = rec(value, "report");
2635
- const devices = s.devices;
2636
- const lanAddresses = s.lanAddresses;
2637
- if (!Array.isArray(devices)) reject("devices");
2638
- if (!Array.isArray(lanAddresses) || lanAddresses.some((value) => typeof value !== "string")) reject("lanAddresses");
2639
- const releaseUrl = s.releaseUrl;
2640
- return {
2641
- protocolVersion: num(s, "protocolVersion", "protocolVersion"),
2642
- serverVersion: str(s, "serverVersion", "serverVersion"),
2643
- pluginVersion: str(s, "pluginVersion", "pluginVersion"),
2644
- ...s.updateAvailable === true ? { updateAvailable: true } : {},
2645
- ...typeof releaseUrl === "string" && releaseUrl.length > 0 ? { releaseUrl } : {},
2646
- enabled: bool(s, "enabled", "enabled"),
2647
- tokenPath: str(s, "tokenPath", "tokenPath"),
2648
- tokenReady: bool(s, "tokenReady", "tokenReady"),
2649
- activeConnections: num(s, "activeConnections", "activeConnections"),
2650
- historyBufferMax: num(s, "historyBufferMax", "historyBufferMax"),
2651
- debug: bool(s, "debug", "debug"),
2652
- lanAddresses,
2653
- remote: parseRemote(s.remote),
2654
- devices: devices.map(parseDevice)
2655
- };
2656
- }
2657
- const reportSchema = { parse: parseReport };
2658
- const relayTestSchema = { parse: parseRelayTestResult };
2659
- const pushTestSchema = { parse: parsePushTestResult };
2660
- const pairingTokenSchema = { parse(value) {
2661
- if (typeof value !== "string" || value.length < 32) throw new TypeError("deeppilot/revealToken result: invalid token");
2662
- return value;
2663
- } };
2664
- const REPORT_REMOTE_CONTRIBUTION = {
2665
- package: REPORT_REMOTE_PACKAGE,
2666
- descriptors: [
2667
- {
2668
- id: `${REPORT_REMOTE_PACKAGE}#${REPORT_ENDPOINT}`,
2669
- service: "deeppilotReport",
2670
- namespace: "deeppilot",
2671
- method: "report",
2672
- invocation: { kind: "direct" },
2673
- parameters: [],
2674
- result: {
2675
- mode: "strict",
2676
- typeSymbol: `${REPORT_REMOTE_PACKAGE}#DeepPilotReport`,
2677
- schema: reportSchema
2678
- }
2679
- },
2680
- {
2681
- id: `${REPORT_REMOTE_PACKAGE}#${REVEAL_TOKEN_ENDPOINT}`,
2682
- service: "deeppilotReport",
2683
- namespace: "deeppilot",
2684
- method: "revealToken",
2685
- invocation: { kind: "direct" },
2686
- parameters: [],
2687
- result: {
2688
- mode: "strict",
2689
- typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,
2690
- schema: pairingTokenSchema
2691
- }
2692
- },
2693
- {
2694
- id: `${REPORT_REMOTE_PACKAGE}#${ROTATE_TOKEN_ENDPOINT}`,
2695
- service: "deeppilotReport",
2696
- namespace: "deeppilot",
2697
- method: "rotateToken",
2698
- invocation: { kind: "direct" },
2699
- parameters: [],
2700
- result: {
2701
- mode: "strict",
2702
- typeSymbol: `${REPORT_REMOTE_PACKAGE}#PairingToken`,
2703
- schema: pairingTokenSchema
2704
- }
2705
- },
2706
- {
2707
- id: `${REPORT_REMOTE_PACKAGE}#deeppilot/testRelay`,
2708
- service: "deeppilotReport",
2709
- namespace: "deeppilot",
2710
- method: "testRelay",
2711
- invocation: { kind: "direct" },
2712
- parameters: [],
2713
- result: {
2714
- mode: "strict",
2715
- typeSymbol: `${REPORT_REMOTE_PACKAGE}#RelayTestResult`,
2716
- schema: relayTestSchema
2717
- }
2718
- },
2719
- {
2720
- id: `${REPORT_REMOTE_PACKAGE}#deeppilot/testPush`,
2721
- service: "deeppilotReport",
2722
- namespace: "deeppilot",
2723
- method: "testPush",
2724
- invocation: { kind: "direct" },
2725
- parameters: [],
2726
- result: {
2727
- mode: "strict",
2728
- typeSymbol: `${REPORT_REMOTE_PACKAGE}#PushTestResult`,
2729
- schema: pushTestSchema
2730
- }
2900
+ if (typeof data === "undefined" || data === "") throw new Error("No input text");
2901
+ let errorCorrectionLevel = ECLevel.M;
2902
+ let version;
2903
+ let mask;
2904
+ if (typeof options !== "undefined") {
2905
+ errorCorrectionLevel = ECLevel.from(options.errorCorrectionLevel, ECLevel.M);
2906
+ version = Version.from(options.version);
2907
+ mask = MaskPattern.from(options.maskPattern);
2908
+ if (options.toSJISFunc) Utils.setToSJISFunction(options.toSJISFunc);
2731
2909
  }
2732
- ]
2733
- };
2910
+ return createSymbol(data, version, errorCorrectionLevel, mask);
2911
+ };
2912
+ }));
2734
2913
  //#endregion
2735
- //#region src/client/report-mount.ts
2736
- /** Mount the contribution and resolve the namespace service it installs. */
2737
- async function mountReportRemote(remote, resolveNamespace) {
2738
- const dispose = await remote.$mount(REPORT_REMOTE_CONTRIBUTION);
2739
- const namespace = resolveNamespace();
2740
- if (namespace === void 0) {
2741
- await dispose();
2742
- throw new Error("remote.deeppilot 未注册");
2914
+ //#region node_modules/qrcode/lib/renderer/utils.js
2915
+ var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
2916
+ function hex2rgba(hex) {
2917
+ if (typeof hex === "number") hex = hex.toString();
2918
+ if (typeof hex !== "string") throw new Error("Color should be defined as hex string");
2919
+ let hexCode = hex.slice().replace("#", "").split("");
2920
+ if (hexCode.length < 3 || hexCode.length === 5 || hexCode.length > 8) throw new Error("Invalid hex color: " + hex);
2921
+ if (hexCode.length === 3 || hexCode.length === 4) hexCode = Array.prototype.concat.apply([], hexCode.map(function(c) {
2922
+ return [c, c];
2923
+ }));
2924
+ if (hexCode.length === 6) hexCode.push("F", "F");
2925
+ const hexValue = parseInt(hexCode.join(""), 16);
2926
+ return {
2927
+ r: hexValue >> 24 & 255,
2928
+ g: hexValue >> 16 & 255,
2929
+ b: hexValue >> 8 & 255,
2930
+ a: hexValue & 255,
2931
+ hex: "#" + hexCode.slice(0, 6).join("")
2932
+ };
2743
2933
  }
2744
- return {
2745
- namespace,
2746
- dispose
2934
+ exports.getOptions = function getOptions(options) {
2935
+ if (!options) options = {};
2936
+ if (!options.color) options.color = {};
2937
+ const margin = typeof options.margin === "undefined" || options.margin === null || options.margin < 0 ? 4 : options.margin;
2938
+ const width = options.width && options.width >= 21 ? options.width : void 0;
2939
+ const scale = options.scale || 4;
2940
+ return {
2941
+ width,
2942
+ scale: width ? 4 : scale,
2943
+ margin,
2944
+ color: {
2945
+ dark: hex2rgba(options.color.dark || "#000000ff"),
2946
+ light: hex2rgba(options.color.light || "#ffffffff")
2947
+ },
2948
+ type: options.type,
2949
+ rendererOpts: options.rendererOpts || {}
2950
+ };
2747
2951
  };
2748
- }
2749
- //#endregion
2750
- //#region src/client/index.ts
2751
- const CSS = [
2752
- ".pbb-section{max-width:720px;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:12px}",
2753
- ".pbb-title{margin:0;font-size:18px;font-weight:600}",
2754
- ".pbb-intro{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5;margin:0}",
2755
- ".pbb-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px}",
2756
- ".pbb-field{display:flex;flex-direction:column;gap:6px;padding:12px 0}",
2757
- ".pbb-field+.pbb-field{border-top:1px solid var(--dsw-alias-border-l2)}",
2758
- ".pbb-row{display:flex;align-items:center;gap:8px}",
2759
- ".pbb-label{flex:1;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary);min-width:0}",
2760
- ".pbb-value{font-size:13px;color:var(--dsw-alias-label-secondary);word-break:break-all;text-align:right}",
2761
- ".pbb-badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;white-space:nowrap}",
2762
- ".pbb-ok{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));font-weight:500}",
2763
- ".pbb-bad{color:var(--dsw-alias-label-error);font-weight:500}",
2764
- ".pbb-table{width:100%;border-collapse:collapse;font-size:12px}",
2765
- ".pbb-table th{color:var(--dsw-alias-label-tertiary);text-align:left;font-weight:500;padding:6px 8px;border-bottom:1px solid var(--dsw-alias-border-l2)}",
2766
- ".pbb-table td{padding:6px 8px;border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}",
2767
- ".pbb-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:0}",
2768
- ".pbb-diag{font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0;line-height:1.5}",
2769
- ".pbb-diagBad{color:var(--dsw-alias-label-error)}",
2770
- ".pbb-refresh{font:inherit;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:none;border:none;padding:0}",
2771
- ".pbb-refresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}",
2772
- ".pbb-refresh:disabled{cursor:default;opacity:.5}",
2773
- ".pbb-tokenRow{flex-wrap:wrap}",
2774
- ".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}",
2775
- ".pbb-tokenActions{display:flex;align-items:center;gap:8px}",
2776
- ".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:3px 8px}",
2777
- ".pbb-action:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}",
2778
- ".pbb-action:disabled{cursor:default;opacity:.5}",
2779
- ".pbb-actionDanger:not(:disabled){color:#fff;background:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}",
2780
- ".pbb-qrPanel{display:flex;flex-direction:column;align-items:center;gap:9px;padding:12px 0 4px}",
2781
- ".pbb-qrImage{width:240px;height:240px;max-width:100%;background:#fff;border-radius:10px;padding:8px;box-sizing:border-box}",
2782
- ".pbb-qrHint{max-width:420px;text-align:center;font-size:11px;line-height:1.5;color:var(--dsw-alias-label-tertiary);margin:0}",
2783
- ".pbb-switchRow{padding:12px 0;display:flex;align-items:center;gap:12px}",
2784
- ".pbb-switchRow+.pbb-field{border-top:1px solid var(--dsw-alias-border-l2)}",
2785
- ".pbb-switchText{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}",
2786
- ".pbb-switchTitle{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary)}",
2787
- ".pbb-dotRow{display:flex;align-items:center;gap:7px}",
2788
- ".pbb-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}",
2789
- ".pbb-dotOk{background:var(--dsw-alias-state-success-primary,#22a06b)}",
2790
- ".pbb-dotWarn{background:var(--dsw-alias-state-warning-primary,#e2b203)}",
2791
- ".pbb-dotBad{background:var(--dsw-alias-label-error)}",
2792
- ".pbb-rowAction{display:flex;gap:8px;margin-top:4px}",
2793
- ".pbb-switchDesc{font-size:11px;color:var(--dsw-alias-label-tertiary);line-height:1.4}",
2794
- ".pbb-switch{appearance:none;-webkit-appearance:none;width:38px;height:22px;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}",
2795
- ".pbb-switch::after{content:\"\";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-secondary);transition:transform .15s ease,background .15s ease}",
2796
- ".pbb-switchOn{background:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary))}",
2797
- ".pbb-switchOn::after{transform:translateX(16px);background:#fff}",
2798
- ".pbb-switch:disabled{opacity:.5;cursor:default}",
2799
- ".pbb-help{border-top:1px solid var(--dsw-alias-border-l2);padding:12px 0}",
2800
- ".pbb-help summary{cursor:pointer;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;user-select:none}",
2801
- ".pbb-help summary:hover{color:var(--dsw-alias-label-secondary)}",
2802
- ".pbb-helpBody{display:flex;flex-direction:column;gap:14px;padding:12px 0 2px}",
2803
- ".pbb-helpSection{display:flex;flex-direction:column;gap:6px}",
2804
- ".pbb-helpHeading{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary)}",
2805
- ".pbb-helpList{margin:0;padding-left:20px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
2806
- ".pbb-helpList li+li{margin-top:4px}",
2807
- ".pbb-helpText{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.6}",
2808
- ".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}",
2809
- ".pbb-helpLink{color:var(--dsw-alias-label-secondary);text-decoration:underline;text-underline-offset:2px}",
2810
- ".pbb-versionFooter{display:flex;justify-content:center;align-items:center;gap:8px;font-size:11px;color:var(--dsw-alias-label-tertiary);padding:8px 0 4px;line-height:1.4}",
2811
- ".pbb-versionFooter a{color:var(--dsw-alias-state-success-primary,#22a06b);text-decoration:none}",
2812
- ".pbb-versionFooter a:hover{text-decoration:underline;text-underline-offset:2px}"
2813
- ].join("\n");
2814
- function injectCss() {
2815
- if (typeof document === "undefined") return;
2816
- if (document.querySelector("style[data-plugin-css=\"dsh-deeppilot/page.css\"]") !== null) return;
2817
- const tag = document.createElement("style");
2818
- tag.dataset.plugin = "dsh-deeppilot";
2819
- tag.textContent = CSS;
2820
- document.head.appendChild(tag);
2821
- }
2822
- async function writeClipboard(value) {
2823
- try {
2824
- await navigator.clipboard.writeText(value);
2825
- return;
2826
- } catch {}
2827
- const textarea = document.createElement("textarea");
2828
- textarea.value = value;
2829
- textarea.style.position = "fixed";
2830
- textarea.style.opacity = "0";
2831
- document.body.appendChild(textarea);
2832
- textarea.select();
2833
- const copied = document.execCommand("copy");
2834
- textarea.remove();
2835
- if (!copied) throw new Error("浏览器拒绝了剪贴板写入");
2836
- }
2837
- /** Polls the report remote and owns the page state transitions. */
2838
- var ReportController = class {
2839
- fetchReport;
2840
- listeners = /* @__PURE__ */ new Set();
2841
- snap = {
2842
- status: "loading",
2843
- report: null,
2844
- message: ""
2952
+ exports.getScale = function getScale(qrSize, opts) {
2953
+ return opts.width && opts.width >= qrSize + opts.margin * 2 ? opts.width / (qrSize + opts.margin * 2) : opts.scale;
2845
2954
  };
2846
- constructor(fetchReport) {
2847
- this.fetchReport = fetchReport;
2955
+ exports.getImageWidth = function getImageWidth(qrSize, opts) {
2956
+ const scale = exports.getScale(qrSize, opts);
2957
+ return Math.floor((qrSize + opts.margin * 2) * scale);
2958
+ };
2959
+ exports.qrToImageData = function qrToImageData(imgData, qr, opts) {
2960
+ const size = qr.modules.size;
2961
+ const data = qr.modules.data;
2962
+ const scale = exports.getScale(size, opts);
2963
+ const symbolSize = Math.floor((size + opts.margin * 2) * scale);
2964
+ const scaledMargin = opts.margin * scale;
2965
+ const palette = [opts.color.light, opts.color.dark];
2966
+ for (let i = 0; i < symbolSize; i++) for (let j = 0; j < symbolSize; j++) {
2967
+ let posDst = (i * symbolSize + j) * 4;
2968
+ let pxColor = opts.color.light;
2969
+ if (i >= scaledMargin && j >= scaledMargin && i < symbolSize - scaledMargin && j < symbolSize - scaledMargin) {
2970
+ const iSrc = Math.floor((i - scaledMargin) / scale);
2971
+ const jSrc = Math.floor((j - scaledMargin) / scale);
2972
+ pxColor = palette[data[iSrc * size + jSrc] ? 1 : 0];
2973
+ }
2974
+ imgData[posDst++] = pxColor.r;
2975
+ imgData[posDst++] = pxColor.g;
2976
+ imgData[posDst++] = pxColor.b;
2977
+ imgData[posDst] = pxColor.a;
2978
+ }
2979
+ };
2980
+ }));
2981
+ //#endregion
2982
+ //#region node_modules/qrcode/lib/renderer/canvas.js
2983
+ var require_canvas = /* @__PURE__ */ __commonJSMin(((exports) => {
2984
+ const Utils = require_utils();
2985
+ function clearCanvas(ctx, canvas, size) {
2986
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
2987
+ if (!canvas.style) canvas.style = {};
2988
+ canvas.height = size;
2989
+ canvas.width = size;
2990
+ canvas.style.height = size + "px";
2991
+ canvas.style.width = size + "px";
2848
2992
  }
2849
- state() {
2850
- return this.snap;
2993
+ function getCanvasElement() {
2994
+ try {
2995
+ return document.createElement("canvas");
2996
+ } catch (e) {
2997
+ throw new Error("You need to specify a canvas element");
2998
+ }
2851
2999
  }
2852
- subscribe(listener) {
2853
- this.listeners.add(listener);
2854
- return () => this.listeners.delete(listener);
3000
+ exports.render = function render(qrData, canvas, options) {
3001
+ let opts = options;
3002
+ let canvasEl = canvas;
3003
+ if (typeof opts === "undefined" && (!canvas || !canvas.getContext)) {
3004
+ opts = canvas;
3005
+ canvas = void 0;
3006
+ }
3007
+ if (!canvas) canvasEl = getCanvasElement();
3008
+ opts = Utils.getOptions(opts);
3009
+ const size = Utils.getImageWidth(qrData.modules.size, opts);
3010
+ const ctx = canvasEl.getContext("2d");
3011
+ const image = ctx.createImageData(size, size);
3012
+ Utils.qrToImageData(image.data, qrData, opts);
3013
+ clearCanvas(ctx, canvasEl, size);
3014
+ ctx.putImageData(image, 0, 0);
3015
+ return canvasEl;
3016
+ };
3017
+ exports.renderToDataURL = function renderToDataURL(qrData, canvas, options) {
3018
+ let opts = options;
3019
+ if (typeof opts === "undefined" && (!canvas || !canvas.getContext)) {
3020
+ opts = canvas;
3021
+ canvas = void 0;
3022
+ }
3023
+ if (!opts) opts = {};
3024
+ const canvasEl = exports.render(qrData, canvas, opts);
3025
+ const type = opts.type || "image/png";
3026
+ const rendererOpts = opts.rendererOpts || {};
3027
+ return canvasEl.toDataURL(type, rendererOpts.quality);
3028
+ };
3029
+ }));
3030
+ //#endregion
3031
+ //#region node_modules/qrcode/lib/renderer/svg-tag.js
3032
+ var require_svg_tag = /* @__PURE__ */ __commonJSMin(((exports) => {
3033
+ const Utils = require_utils();
3034
+ function getColorAttrib(color, attrib) {
3035
+ const alpha = color.a / 255;
3036
+ const str = attrib + "=\"" + color.hex + "\"";
3037
+ return alpha < 1 ? str + " " + attrib + "-opacity=\"" + alpha.toFixed(2).slice(1) + "\"" : str;
2855
3038
  }
2856
- dispose() {
2857
- this.listeners.clear();
3039
+ function svgCmd(cmd, x, y) {
3040
+ let str = cmd + x;
3041
+ if (typeof y !== "undefined") str += " " + y;
3042
+ return str;
3043
+ }
3044
+ function qrToPath(data, size, margin) {
3045
+ let path = "";
3046
+ let moveBy = 0;
3047
+ let newRow = false;
3048
+ let lineLength = 0;
3049
+ for (let i = 0; i < data.length; i++) {
3050
+ const col = Math.floor(i % size);
3051
+ const row = Math.floor(i / size);
3052
+ if (!col && !newRow) newRow = true;
3053
+ if (data[i]) {
3054
+ lineLength++;
3055
+ if (!(i > 0 && col > 0 && data[i - 1])) {
3056
+ path += newRow ? svgCmd("M", col + margin, .5 + row + margin) : svgCmd("m", moveBy, 0);
3057
+ moveBy = 0;
3058
+ newRow = false;
3059
+ }
3060
+ if (!(col + 1 < size && data[i + 1])) {
3061
+ path += svgCmd("h", lineLength);
3062
+ lineLength = 0;
3063
+ }
3064
+ } else moveBy++;
3065
+ }
3066
+ return path;
2858
3067
  }
2859
- async refresh() {
3068
+ exports.render = function render(qrData, options, cb) {
3069
+ const opts = Utils.getOptions(options);
3070
+ const size = qrData.modules.size;
3071
+ const data = qrData.modules.data;
3072
+ const qrcodesize = size + opts.margin * 2;
3073
+ const bg = !opts.color.light.a ? "" : "<path " + getColorAttrib(opts.color.light, "fill") + " d=\"M0 0h" + qrcodesize + "v" + qrcodesize + "H0z\"/>";
3074
+ const path = "<path " + getColorAttrib(opts.color.dark, "stroke") + " d=\"" + qrToPath(data, size, opts.margin) + "\"/>";
3075
+ const viewBox = "viewBox=\"0 0 " + qrcodesize + " " + qrcodesize + "\"";
3076
+ const svgTag = "<svg xmlns=\"http://www.w3.org/2000/svg\" " + (!opts.width ? "" : "width=\"" + opts.width + "\" height=\"" + opts.width + "\" ") + viewBox + " shape-rendering=\"crispEdges\">" + bg + path + "</svg>\n";
3077
+ if (typeof cb === "function") cb(null, svgTag);
3078
+ return svgTag;
3079
+ };
3080
+ }));
3081
+ //#endregion
3082
+ //#region src/pairing-qr.ts
3083
+ var import_browser = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
3084
+ const canPromise = require_can_promise();
3085
+ const QRCode = require_qrcode();
3086
+ const CanvasRenderer = require_canvas();
3087
+ const SvgRenderer = require_svg_tag();
3088
+ function renderCanvas(renderFunc, canvas, text, opts, cb) {
3089
+ const args = [].slice.call(arguments, 1);
3090
+ const argsNum = args.length;
3091
+ const isLastArgCb = typeof args[argsNum - 1] === "function";
3092
+ if (!isLastArgCb && !canPromise()) throw new Error("Callback required as last argument");
3093
+ if (isLastArgCb) {
3094
+ if (argsNum < 2) throw new Error("Too few arguments provided");
3095
+ if (argsNum === 2) {
3096
+ cb = text;
3097
+ text = canvas;
3098
+ canvas = opts = void 0;
3099
+ } else if (argsNum === 3) {
3100
+ if (canvas.getContext && typeof cb === "undefined") {
3101
+ cb = opts;
3102
+ opts = void 0;
3103
+ } else {
3104
+ cb = opts;
3105
+ opts = text;
3106
+ text = canvas;
3107
+ canvas = void 0;
3108
+ }
3109
+ }
3110
+ } else {
3111
+ if (argsNum < 1) throw new Error("Too few arguments provided");
3112
+ if (argsNum === 1) {
3113
+ text = canvas;
3114
+ canvas = opts = void 0;
3115
+ } else if (argsNum === 2 && !canvas.getContext) {
3116
+ opts = text;
3117
+ text = canvas;
3118
+ canvas = void 0;
3119
+ }
3120
+ return new Promise(function(resolve, reject) {
3121
+ try {
3122
+ resolve(renderFunc(QRCode.create(text, opts), canvas, opts));
3123
+ } catch (e) {
3124
+ reject(e);
3125
+ }
3126
+ });
3127
+ }
2860
3128
  try {
2861
- const report = await this.fetchReport();
2862
- this.snap = report !== null ? {
2863
- status: "ready",
2864
- report,
2865
- message: ""
2866
- } : {
2867
- status: "error",
2868
- report: null,
2869
- message: "报告远程未挂载(remote mount 失败)— 请确认宿主包含 typert 组合"
2870
- };
2871
- } catch (error) {
2872
- this.snap = {
2873
- status: "error",
2874
- report: null,
2875
- message: error instanceof Error ? error.message : String(error)
2876
- };
3129
+ const data = QRCode.create(text, opts);
3130
+ cb(null, renderFunc(data, canvas, opts));
3131
+ } catch (e) {
3132
+ cb(e);
2877
3133
  }
2878
- this.emit();
2879
- }
2880
- emit() {
2881
- for (const listener of [...this.listeners]) try {
2882
- listener();
2883
- } catch {}
2884
3134
  }
2885
- };
2886
- const inject = [
2887
- "slots",
2888
- "locale",
2889
- "remote",
2890
- "settingsScope"
2891
- ];
2892
- function apply(ctx) {
2893
- if (typeof document !== "undefined") injectCss();
2894
- const anyCtx = ctx;
2895
- anyCtx.locale?.register("settings.deeppilot", {
2896
- zh: { nav: "DeepPilot" },
2897
- en: { nav: "DeepPilot" }
3135
+ exports.create = QRCode.create;
3136
+ exports.toCanvas = renderCanvas.bind(null, CanvasRenderer.render);
3137
+ exports.toDataURL = renderCanvas.bind(null, CanvasRenderer.renderToDataURL);
3138
+ exports.toString = renderCanvas.bind(null, function(data, _, opts) {
3139
+ return SvgRenderer.render(data, opts);
2898
3140
  });
2899
- let namespace;
2900
- let mountError;
2901
- const fetchReport = async () => {
2902
- if (namespace === void 0) throw new Error(mountError !== void 0 ? "remote mount 失败: " + mountError : "report remote 未挂载");
2903
- const result = await namespace.report();
2904
- if (!result.ok) throw new Error(result.error.message ?? "report remote 调用失败");
2905
- return result.value;
2906
- };
2907
- const controller = new ReportController(fetchReport);
2908
- ctx.effect(() => () => controller.dispose(), "dsh-deeppilot: report controller");
2909
- const store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(controller.state());
2910
- controller.subscribe(() => store.set(controller.state()));
2911
- ctx.effect(() => {
2912
- let cancelled = false;
2913
- let unmount;
2914
- if (anyCtx.remote === void 0) {
2915
- mountError = "remote 服务不可用";
2916
- controller.refresh();
2917
- return () => {};
2918
- }
2919
- mountReportRemote(anyCtx.remote, () => ctx.get("remote.deeppilot")).then((mounted) => {
2920
- if (cancelled) {
2921
- mounted.dispose();
2922
- return;
2923
- }
2924
- namespace = mounted.namespace;
2925
- unmount = mounted.dispose;
2926
- mountError = void 0;
2927
- controller.refresh();
2928
- }, (error) => {
2929
- mountError = error instanceof Error ? error.message : String(error);
2930
- controller.refresh();
2931
- });
2932
- return () => {
2933
- cancelled = true;
2934
- namespace = void 0;
2935
- if (unmount !== void 0) unmount();
2936
- };
2937
- }, "dsh-deeppilot: report remote mount");
2938
- const revealPairingToken = async () => {
2939
- if (namespace === void 0) throw new Error(mountError !== void 0 ? "remote mount 失败: " + mountError : "report remote 未挂载");
2940
- const result = await namespace.revealToken();
2941
- if (!result.ok) throw new Error(result.error.message ?? "Token 读取失败");
2942
- return result.value;
2943
- };
2944
- const sendTestPush = async () => {
2945
- if (namespace === void 0) throw new Error(mountError !== void 0 ? "remote mount 失败: " + mountError : "report remote 未挂载");
2946
- if (typeof namespace.testPush !== "function") throw new Error("宿主插件版本较旧,不支持推送测试 — 请更新 dsh-deeppilot");
2947
- const result = await namespace.testPush();
2948
- if (!result.ok) throw new Error(result.error.message ?? "推送测试失败");
2949
- return result.value;
2950
- };
2951
- const testRelayConnection = async () => {
2952
- if (namespace === void 0) throw new Error(mountError !== void 0 ? "remote mount 失败: " + mountError : "report remote 未挂载");
2953
- if (typeof namespace.testRelay !== "function") throw new Error("宿主插件版本较旧,不支持中继测试 — 请更新 dsh-deeppilot");
2954
- const result = await namespace.testRelay();
2955
- if (!result.ok) throw new Error(result.error.message ?? "中继测试失败");
2956
- return result.value;
2957
- };
2958
- const rotatePairingToken = async () => {
2959
- if (namespace === void 0) throw new Error(mountError !== void 0 ? "remote mount 失败: " + mountError : "report remote 未挂载");
2960
- const result = await namespace.rotateToken();
2961
- if (!result.ok) throw new Error(result.error.message ?? "Token 更换失败");
2962
- return result.value;
3141
+ })))(), 1);
3142
+ /**
3143
+ * Protocol-v1 compatibility identifier used by the TestFlight build currently
3144
+ * under review. This is wire data, not the plugin or product display name.
3145
+ */
3146
+ const PAIRING_QR_TYPE = "dsh-pocket-pairing";
3147
+ function isLoopbackHostname(hostname) {
3148
+ const normalized = hostname.toLowerCase();
3149
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "[::1]" || normalized === "::1" || normalized.startsWith("127.");
3150
+ }
3151
+ /** Prefer an online Funnel; otherwise turn the current web origin into a LAN target. */
3152
+ function selectPairingTarget(remote, lanAddresses, currentOrigin) {
3153
+ let origin;
3154
+ try {
3155
+ if (currentOrigin) origin = new URL(currentOrigin);
3156
+ } catch {}
3157
+ if (remote.publicURL && origin?.origin === remote.publicURL) return {
3158
+ host: remote.publicURL,
3159
+ kind: "public"
2963
3160
  };
2964
- const enabledStore = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
2965
- status: "loading",
2966
- enabled: true
2967
- });
2968
- const scope = anyCtx.settingsScope?.bind({ namespace: "deeppilot" });
2969
- const adoptEnabled = () => {
2970
- if (scope === void 0) return;
2971
- const snap = scope.getSnapshot();
2972
- if (snap.status === "ready" && snap.value !== void 0) enabledStore.set({
2973
- status: "ready",
2974
- enabled: snap.value.enabled !== false
2975
- });
2976
- else if (snap.status === "unavailable") enabledStore.set({
2977
- status: "unavailable",
2978
- enabled: true
2979
- });
3161
+ if (remote.phase === "online" && remote.publicURL) return {
3162
+ host: remote.publicURL,
3163
+ kind: "public"
2980
3164
  };
2981
- if (scope !== void 0) {
2982
- scope.subscribe(adoptEnabled);
2983
- adoptEnabled();
2984
- }
2985
- const setDeepPilotEnabled = (value) => {
2986
- enabledStore.set({
2987
- status: "ready",
2988
- enabled: value
2989
- });
2990
- if (scope !== void 0) scope.set("enabled", value);
3165
+ if (origin && ["http:", "https:"].includes(origin.protocol) && !isLoopbackHostname(origin.hostname)) return {
3166
+ host: origin.origin,
3167
+ kind: "lan"
2991
3168
  };
2992
- const remoteEnabledStore = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
2993
- status: "loading",
2994
- enabled: false
2995
- });
2996
- const adoptRemoteEnabled = () => {
2997
- if (scope === void 0) return;
2998
- const snap = scope.getSnapshot();
2999
- if (snap.status === "ready") remoteEnabledStore.set({
3000
- status: "ready",
3001
- enabled: snap.value?.remote?.enabled === true
3002
- });
3003
- else if (snap.status === "unavailable") remoteEnabledStore.set({
3004
- status: "unavailable",
3005
- enabled: false
3006
- });
3169
+ const address = lanAddresses[0];
3170
+ if (!address) return null;
3171
+ return {
3172
+ host: `${origin?.protocol === "https:" ? "https:" : "http:"}//${address}${origin?.port ? `:${origin.port}` : ""}`,
3173
+ kind: "lan"
3007
3174
  };
3008
- if (scope !== void 0) {
3009
- scope.subscribe(adoptRemoteEnabled);
3010
- adoptRemoteEnabled();
3011
- }
3012
- const setDeepPilotRemoteEnabled = (value) => {
3013
- remoteEnabledStore.set({
3014
- status: "ready",
3015
- enabled: value
3016
- });
3017
- if (scope === void 0) return;
3018
- const currentRemote = scope.getSnapshot().value?.remote ?? {};
3019
- scope.set("remote", {
3020
- ...currentRemote,
3021
- enabled: value
3022
- });
3175
+ }
3176
+ /** Encode an out-of-band pairing payload without putting the token in a URL. */
3177
+ function encodePairingQRPayload(host, token) {
3178
+ const normalizedHost = host.trim();
3179
+ const parsed = new URL(normalizedHost);
3180
+ if (![
3181
+ "http:",
3182
+ "https:",
3183
+ "ws:",
3184
+ "wss:"
3185
+ ].includes(parsed.protocol) || parsed.hostname === "" || parsed.username !== "" || parsed.password !== "") throw new TypeError("pairing QR requires a valid HTTP(S)/WS(S) host");
3186
+ if (token.trim().length < 32) throw new TypeError("pairing token is invalid");
3187
+ const payload = {
3188
+ v: 1,
3189
+ type: PAIRING_QR_TYPE,
3190
+ host: normalizedHost,
3191
+ token: token.trim()
3023
3192
  };
3024
- anyCtx.slots?.inject("settings.section", () => anyCtx.slots.register({
3025
- name: "settings.section",
3026
- id: "deeppilot",
3027
- order: 13,
3028
- label: () => {
3029
- const bind = anyCtx.locale?.bind("settings.deeppilot");
3030
- return bind ? bind("nav") : "DeepPilot";
3031
- },
3032
- locale: "settings.deeppilot",
3033
- inject: () => ({
3034
- hooks: {
3035
- deepPilotReport: store,
3036
- deepPilotEnabled: enabledStore,
3037
- deepPilotRemoteEnabled: remoteEnabledStore
3038
- },
3039
- refresh: () => {
3040
- controller.refresh();
3041
- },
3042
- revealPairingToken,
3043
- rotatePairingToken,
3044
- testRelay: testRelayConnection,
3045
- testPush: sendTestPush,
3046
- setDeepPilotEnabled,
3047
- setDeepPilotRemoteEnabled
3048
- })
3049
- }, DeepPilotSettingsPage));
3193
+ return JSON.stringify(payload);
3194
+ }
3195
+ //#endregion
3196
+ //#region src/client/settings-page.ts
3197
+ async function writeClipboard(t, value) {
3198
+ try {
3199
+ await navigator.clipboard.writeText(value);
3200
+ return;
3201
+ } catch {}
3202
+ const textarea = document.createElement("textarea");
3203
+ textarea.value = value;
3204
+ textarea.style.position = "fixed";
3205
+ textarea.style.opacity = "0";
3206
+ document.body.appendChild(textarea);
3207
+ textarea.select();
3208
+ const copied = document.execCommand("copy");
3209
+ textarea.remove();
3210
+ if (!copied) throw new Error(t("clipboard.rejected"));
3050
3211
  }
3051
- /** Visual state of the embedded Funnel: a colored dot plus its spoken label. */
3212
+ /** Visual state of the embedded Funnel: a colored dot plus its spoken label.
3213
+ * The label is a function because the surrounding constant lives at module
3214
+ * scope where the locale-bound t() is not in scope; the page resolves the
3215
+ * label at render time via t(props.t, ...). */
3052
3216
  const REMOTE_PHASE_META = {
3053
3217
  disabled: {
3054
3218
  dot: "",
3055
- label: "未启用"
3219
+ labelKey: "phase.disabled"
3056
3220
  },
3057
3221
  starting: {
3058
3222
  dot: " pbb-dotWarn",
3059
- label: "正在启动"
3223
+ labelKey: "phase.starting"
3060
3224
  },
3061
3225
  login_required: {
3062
3226
  dot: " pbb-dotWarn",
3063
- label: "等待 Tailscale 授权"
3227
+ labelKey: "phase.login_required"
3064
3228
  },
3065
3229
  online: {
3066
3230
  dot: " pbb-dotOk",
3067
- label: "远程连接已就绪"
3231
+ labelKey: "phase.online"
3068
3232
  },
3069
3233
  error: {
3070
3234
  dot: " pbb-dotBad",
3071
- label: "远程连接失败"
3235
+ labelKey: "phase.error"
3072
3236
  },
3073
3237
  unavailable: {
3074
3238
  dot: " pbb-dotBad",
3075
- label: "远程 helper 不可用"
3239
+ labelKey: "phase.unavailable"
3076
3240
  },
3077
3241
  stopped: {
3078
3242
  dot: "",
3079
- label: "已停止"
3243
+ labelKey: "phase.stopped"
3080
3244
  }
3081
3245
  };
3082
3246
  /** Slot component: hooks come from the slot renderer, named use<Key>. */
@@ -3104,7 +3268,7 @@ window.__ModuleLoader__.load({
3104
3268
  const sendPushTest = () => {
3105
3269
  if (pushTestBusy) return;
3106
3270
  if (typeof props.testPush !== "function") {
3107
- setPushTestError("宿主插件版本较旧,不支持推送测试");
3271
+ setPushTestError(translateWith(props.t, "push.staleHost"));
3108
3272
  return;
3109
3273
  }
3110
3274
  setPushTestBusy(true);
@@ -3120,7 +3284,7 @@ window.__ModuleLoader__.load({
3120
3284
  const runRelayTest = () => {
3121
3285
  if (relayTestBusy) return;
3122
3286
  if (typeof props.testRelay !== "function") {
3123
- setRelayTestError("宿主插件版本较旧,不支持中继测试");
3287
+ setRelayTestError(translateWith(props.t, "push.staleHostRelay"));
3124
3288
  return;
3125
3289
  }
3126
3290
  setRelayTestBusy(true);
@@ -3137,7 +3301,7 @@ window.__ModuleLoader__.load({
3137
3301
  if (revealedToken === null) return;
3138
3302
  const timer = globalThis.setTimeout(() => {
3139
3303
  setRevealedToken(null);
3140
- setTokenMessage("Token 已自动隐藏");
3304
+ setTokenMessage(translateWith(props.t, "panel.tokenAutoHidden"));
3141
3305
  }, 3e4);
3142
3306
  return () => globalThis.clearTimeout(timer);
3143
3307
  }, [revealedToken]);
@@ -3160,7 +3324,7 @@ window.__ModuleLoader__.load({
3160
3324
  if (qrDataURL === null) return;
3161
3325
  const timer = globalThis.setTimeout(() => {
3162
3326
  setQRDataURL(null);
3163
- setQRMessage("配对二维码已自动隐藏");
3327
+ setQRMessage(translateWith(props.t, "pair.qrAutoHidden"));
3164
3328
  }, 6e4);
3165
3329
  return () => globalThis.clearTimeout(timer);
3166
3330
  }, [qrDataURL]);
@@ -3178,7 +3342,7 @@ window.__ModuleLoader__.load({
3178
3342
  let failed = false;
3179
3343
  try {
3180
3344
  if (typeof props.useDeepPilotReport !== "function") {
3181
- diag.push("useDeepPilotReport hook 缺失");
3345
+ diag.push(translateWith(props.t, "diag.missingReportHook"));
3182
3346
  failed = true;
3183
3347
  } else {
3184
3348
  const state = props.useDeepPilotReport((s) => s);
@@ -3192,22 +3356,22 @@ window.__ModuleLoader__.load({
3192
3356
  const state = props.useDeepPilotEnabled((s) => s);
3193
3357
  enabled = state.enabled;
3194
3358
  switchReady = state.status === "ready";
3195
- if (state.status === "unavailable") diag.push("设置命名空间不可用(settingsScope 未提供)");
3196
- } else diag.push("useDeepPilotEnabled hook 缺失");
3197
- if (typeof props.refresh !== "function") diag.push("refresh 回调缺失");
3198
- if (typeof props.revealPairingToken !== "function") diag.push("revealPairingToken 回调缺失");
3199
- if (typeof props.rotatePairingToken !== "function") diag.push("rotatePairingToken 回调缺失");
3200
- if (typeof props.testRelay !== "function") diag.push("testRelay 回调缺失");
3201
- if (typeof props.testPush !== "function") diag.push("testPush 回调缺失");
3202
- if (typeof props.setDeepPilotEnabled !== "function") diag.push("setDeepPilotEnabled 回调缺失");
3359
+ if (state.status === "unavailable") diag.push(translateWith(props.t, "diag.settingsUnavailable"));
3360
+ } else diag.push(translateWith(props.t, "diag.missingEnabledHook"));
3361
+ if (typeof props.refresh !== "function") diag.push(translateWith(props.t, "diag.missingRefresh"));
3362
+ if (typeof props.revealPairingToken !== "function") diag.push(translateWith(props.t, "diag.missingReveal"));
3363
+ if (typeof props.rotatePairingToken !== "function") diag.push(translateWith(props.t, "diag.missingRotate"));
3364
+ if (typeof props.testRelay !== "function") diag.push(translateWith(props.t, "diag.missingTestRelay"));
3365
+ if (typeof props.testPush !== "function") diag.push(translateWith(props.t, "diag.missingTestPush"));
3366
+ if (typeof props.setDeepPilotEnabled !== "function") diag.push(translateWith(props.t, "diag.missingSetEnabled"));
3203
3367
  if (typeof props.useDeepPilotRemoteEnabled === "function") {
3204
3368
  const state = props.useDeepPilotRemoteEnabled((s) => s);
3205
3369
  remoteEnabled = state.enabled;
3206
3370
  remoteSwitchReady = state.status === "ready";
3207
- } else diag.push("useDeepPilotRemoteEnabled hook 缺失");
3208
- if (typeof props.setDeepPilotRemoteEnabled !== "function") diag.push("setDeepPilotRemoteEnabled 回调缺失");
3371
+ } else diag.push(translateWith(props.t, "diag.missingRemoteEnabledHook"));
3372
+ if (typeof props.setDeepPilotRemoteEnabled !== "function") diag.push(translateWith(props.t, "diag.missingSetRemote"));
3209
3373
  } catch (error) {
3210
- diag.push("渲染异常: " + (error instanceof Error ? error.message : String(error)));
3374
+ diag.push(translateWith(props.t, "diag.renderError") + (error instanceof Error ? error.message : String(error)));
3211
3375
  failed = true;
3212
3376
  }
3213
3377
  const pairingTarget = report === null ? null : selectPairingTarget(report.remote, report.lanAddresses, typeof window === "undefined" ? void 0 : window.location.origin);
@@ -3226,15 +3390,15 @@ window.__ModuleLoader__.load({
3226
3390
  props.revealPairingToken().then((token) => {
3227
3391
  setRevealedToken(token);
3228
3392
  }, (error) => {
3229
- setTokenMessage("Token 读取失败:" + (error instanceof Error ? error.message : String(error)));
3393
+ setTokenMessage(translateWith(props.t, "panel.tokenRevealFailed") + (error instanceof Error ? error.message : String(error)));
3230
3394
  }).finally(() => setTokenBusy(false));
3231
3395
  };
3232
3396
  const copyToken = () => {
3233
3397
  if (typeof props.revealPairingToken !== "function") return;
3234
3398
  setTokenBusy(true);
3235
3399
  setTokenMessage("");
3236
- (revealedToken !== null ? Promise.resolve(revealedToken) : props.revealPairingToken()).then((value) => writeClipboard(value)).then(() => setTokenMessage("已复制到剪贴板"), (error) => {
3237
- setTokenMessage("复制失败:" + (error instanceof Error ? error.message : String(error)));
3400
+ (revealedToken !== null ? Promise.resolve(revealedToken) : props.revealPairingToken()).then((value) => writeClipboard(props.t, value)).then(() => setTokenMessage(translateWith(props.t, "panel.tokenCopied")), (error) => {
3401
+ setTokenMessage(translateWith(props.t, "pair.publicCopyFailed") + (error instanceof Error ? error.message : String(error)));
3238
3402
  }).finally(() => setTokenBusy(false));
3239
3403
  };
3240
3404
  const rotateToken = () => {
@@ -3243,7 +3407,7 @@ window.__ModuleLoader__.load({
3243
3407
  setRotateArmed(true);
3244
3408
  setRevealedToken(null);
3245
3409
  setQRDataURL(null);
3246
- setTokenMessage("更换会使当前 Token 立即失效、断开所有手机连接;5 秒内再次点击确认。");
3410
+ setTokenMessage(translateWith(props.t, "panel.tokenRotateWarning"));
3247
3411
  return;
3248
3412
  }
3249
3413
  setRotateArmed(false);
@@ -3251,17 +3415,17 @@ window.__ModuleLoader__.load({
3251
3415
  setTokenMessage("");
3252
3416
  props.rotatePairingToken().then((token) => {
3253
3417
  setRevealedToken(token);
3254
- setTokenMessage("新 Token 已生效,旧 Token 已失效;请重新配对所有设备。");
3418
+ setTokenMessage(translateWith(props.t, "panel.tokenRotated"));
3255
3419
  }, (error) => {
3256
- setTokenMessage("更换失败:" + (error instanceof Error ? error.message : String(error)));
3420
+ setTokenMessage(translateWith(props.t, "panel.tokenRotateFailed") + (error instanceof Error ? error.message : String(error)));
3257
3421
  }).finally(() => setTokenBusy(false));
3258
3422
  };
3259
3423
  const copyRemoteURL = (url) => {
3260
3424
  setRemoteMessage("");
3261
- writeClipboard(url).then(() => {
3262
- setRemoteMessage("公网地址已复制");
3425
+ writeClipboard(props.t, url).then(() => {
3426
+ setRemoteMessage(translateWith(props.t, "pair.publicCopyDone"));
3263
3427
  }, (error) => {
3264
- setRemoteMessage("复制失败:" + (error instanceof Error ? error.message : String(error)));
3428
+ setRemoteMessage(translateWith(props.t, "pair.publicCopyFailed") + (error instanceof Error ? error.message : String(error)));
3265
3429
  });
3266
3430
  };
3267
3431
  const showPairingQR = () => {
@@ -3275,7 +3439,7 @@ window.__ModuleLoader__.load({
3275
3439
  margin: 2,
3276
3440
  width: 512
3277
3441
  })).then((svg) => setQRDataURL("data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg)), (error) => {
3278
- setQRMessage("二维码生成失败:" + (error instanceof Error ? error.message : String(error)));
3442
+ setQRMessage(translateWith(props.t, "pair.qrFailed") + (error instanceof Error ? error.message : String(error)));
3279
3443
  }).finally(() => setQRBusy(false));
3280
3444
  };
3281
3445
  const primaryRows = [];
@@ -3284,48 +3448,48 @@ window.__ModuleLoader__.load({
3284
3448
  primaryRows.push((0, react.createElement)("div", {
3285
3449
  className: "pbb-field",
3286
3450
  key: "conn"
3287
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "当前连接"), (0, react.createElement)("span", { className: "pbb-value" }, String(report.activeConnections)))), (0, react.createElement)("div", {
3451
+ }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "panel.activeConnections")), (0, react.createElement)("span", { className: "pbb-value" }, String(report.activeConnections)))), (0, react.createElement)("div", {
3288
3452
  className: "pbb-field",
3289
3453
  key: "token"
3290
- }, (0, react.createElement)("div", { className: "pbb-row pbb-tokenRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "配对 Token"), (0, react.createElement)("code", { className: "pbb-token " + (report.tokenReady ? "pbb-ok" : "pbb-bad") }, report.tokenReady ? revealedToken ?? "••••••••••••" : "未生成"), report.tokenReady ? (0, react.createElement)("span", { className: "pbb-tokenActions" }, (0, react.createElement)("button", {
3454
+ }, (0, react.createElement)("div", { className: "pbb-row pbb-tokenRow" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "panel.token")), (0, react.createElement)("code", { className: "pbb-token " + (report.tokenReady ? "pbb-ok" : "pbb-bad") }, report.tokenReady ? revealedToken ?? "••••••••••••" : translateWith(props.t, "panel.tokenNotReady")), report.tokenReady ? (0, react.createElement)("span", { className: "pbb-tokenActions" }, (0, react.createElement)("button", {
3291
3455
  type: "button",
3292
3456
  className: "pbb-action",
3293
3457
  disabled: tokenBusy,
3294
3458
  onClick: toggleToken
3295
- }, tokenBusy ? "读取中…" : revealedToken === null ? "显示" : "隐藏"), (0, react.createElement)("button", {
3459
+ }, tokenBusy ? translateWith(props.t, "panel.tokenAction.showing") : revealedToken === null ? translateWith(props.t, "panel.tokenAction.show") : translateWith(props.t, "panel.tokenAction.hide")), (0, react.createElement)("button", {
3296
3460
  type: "button",
3297
3461
  className: "pbb-action",
3298
3462
  disabled: tokenBusy,
3299
3463
  onClick: copyToken
3300
- }, "复制"), (0, react.createElement)("button", {
3464
+ }, translateWith(props.t, "panel.tokenAction.copy")), (0, react.createElement)("button", {
3301
3465
  type: "button",
3302
3466
  className: "pbb-action" + (rotateArmed ? " pbb-actionDanger" : ""),
3303
3467
  disabled: tokenBusy,
3304
3468
  onClick: rotateToken
3305
- }, rotateArmed ? "确认更换?" : tokenBusy ? "更换中…" : "更换")) : null), tokenMessage ? (0, react.createElement)("p", { className: "pbb-diag" }, tokenMessage) : null));
3469
+ }, rotateArmed ? translateWith(props.t, "panel.tokenAction.rotateConfirm") : tokenBusy ? translateWith(props.t, "panel.tokenAction.rotating") : translateWith(props.t, "panel.tokenAction.rotate"))) : null), tokenMessage ? (0, react.createElement)("p", { className: "pbb-diag" }, tokenMessage) : null));
3306
3470
  advancedRows.push((0, react.createElement)("div", {
3307
3471
  className: "pbb-field",
3308
3472
  key: "proto"
3309
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "协议版本"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + String(report.protocolVersion)))), (0, react.createElement)("div", {
3473
+ }, (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", {
3310
3474
  className: "pbb-field",
3311
3475
  key: "server"
3312
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "服务器版本"), (0, react.createElement)("span", { className: "pbb-value" }, report.serverVersion))), (0, react.createElement)("div", {
3476
+ }, (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", {
3313
3477
  className: "pbb-field",
3314
3478
  key: "path"
3315
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "Token 路径"), (0, react.createElement)("span", { className: "pbb-value" }, report.tokenPath))), (0, react.createElement)("div", {
3479
+ }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.tokenPath")), (0, react.createElement)("span", { className: "pbb-value" }, report.tokenPath))), (0, react.createElement)("div", {
3316
3480
  className: "pbb-field",
3317
3481
  key: "buffer"
3318
- }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "重放缓冲上限"), (0, react.createElement)("span", { className: "pbb-value" }, String(report.historyBufferMax) + " "))));
3482
+ }, (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")))));
3319
3483
  }
3320
- const deviceTable = report !== null && report.devices.length > 0 ? (0, react.createElement)("table", { className: "pbb-table" }, (0, react.createElement)("thead", null, (0, react.createElement)("tr", null, (0, react.createElement)("th", null, "设备"), (0, react.createElement)("th", null, "App 版本"), (0, react.createElement)("th", null, "离线推送"), (0, react.createElement)("th", null, "最近在线"))), (0, react.createElement)("tbody", null, report.devices.map((d) => (0, react.createElement)("tr", { key: d.deviceId }, (0, react.createElement)("td", null, d.deviceName), (0, react.createElement)("td", null, d.appVersion), (0, react.createElement)("td", null, d.apns ? "已注册(" + (d.apns.environment === "production" ? "生产" : "开发") + ")" : "未注册"), (0, react.createElement)("td", null, new Date(d.lastSeenTs).toLocaleString()))))) : (0, react.createElement)("p", { className: "pbb-empty" }, "还没有设备配对过。在 iPhone 上的 DeepPilot App 中扫码或填入本机地址与 Token 即可配对。");
3321
- const switchTitle = "DeepPilot 连接";
3322
- const switchDesc = switchReady ? enabled ? "已开启:接受手机连接。" : "已关闭:不接受手机连接。" : "正在读取配置…";
3484
+ const deviceTable = report !== null && report.devices.length > 0 ? (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.appVersion")), (0, react.createElement)("th", null, translateWith(props.t, "devices.col.push")), (0, react.createElement)("th", null, translateWith(props.t, "devices.col.lastSeen")))), (0, react.createElement)("tbody", null, report.devices.map((d) => (0, react.createElement)("tr", { key: d.deviceId }, (0, react.createElement)("td", null, d.deviceName), (0, react.createElement)("td", null, d.appVersion), (0, react.createElement)("td", null, d.apns ? translateWith(props.t, "devices.pushRegistered") + (d.apns.environment === "production" ? translateWith(props.t, "devices.pushEnvProduction") : translateWith(props.t, "devices.pushEnvDevelopment")) + ")" : translateWith(props.t, "devices.pushNotRegistered")), (0, react.createElement)("td", null, new Date(d.lastSeenTs).toLocaleString()))))) : (0, react.createElement)("p", { className: "pbb-empty" }, translateWith(props.t, "devices.empty"));
3485
+ const switchTitle = translateWith(props.t, "master.title");
3486
+ const switchDesc = switchReady ? enabled ? translateWith(props.t, "master.on") : translateWith(props.t, "master.off") : translateWith(props.t, "master.loading");
3323
3487
  return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("div", { style: { flex: "1" } }), (0, react.createElement)("button", {
3324
3488
  className: "pbb-refresh",
3325
3489
  onClick: () => {
3326
3490
  if (typeof props.refresh === "function") props.refresh();
3327
3491
  }
3328
- }, "刷新")), (0, react.createElement)("p", { className: "pbb-intro" }, "把 iPhone 与这台电脑上的 DeepSeek Harness(DSH)连接起来(协议 v1)。"), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-switchRow" }, (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", {
3492
+ }, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro")), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-switchRow" }, (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", {
3329
3493
  type: "button",
3330
3494
  role: "switch",
3331
3495
  "aria-checked": enabled,
@@ -3338,29 +3502,29 @@ window.__ModuleLoader__.load({
3338
3502
  })), (0, react.createElement)("div", { className: "pbb-switchRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle pbb-dotRow" }, (0, react.createElement)("span", {
3339
3503
  className: "pbb-dot" + (report !== null ? REMOTE_PHASE_META[report.remote.phase].dot : ""),
3340
3504
  role: "img",
3341
- "aria-label": report !== null ? REMOTE_PHASE_META[report.remote.phase].label : "状态未知",
3342
- title: report !== null ? REMOTE_PHASE_META[report.remote.phase].label : void 0
3343
- }), "远程连接(Tailscale Funnel)"), (0, react.createElement)("span", { className: "pbb-switchDesc" }, remoteSwitchReady ? remoteEnabled ? "已配置:内嵌 Funnel 会自动启动并同步状态。" : "关闭:仅保留局域网连接。" : "正在读取配置…"), 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", {
3505
+ "aria-label": report !== null ? translateWith(props.t, REMOTE_PHASE_META[report.remote.phase].labelKey) : translateWith(props.t, "phase.unknown"),
3506
+ title: report !== null ? translateWith(props.t, REMOTE_PHASE_META[report.remote.phase].labelKey) : void 0
3507
+ }), translateWith(props.t, "remote.title")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading")), 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", {
3344
3508
  className: "pbb-action",
3345
3509
  href: report.remote.authURL,
3346
3510
  target: "_blank",
3347
3511
  rel: "noreferrer"
3348
- }, "打开授权页面")) : 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, remoteMessage ? (0, react.createElement)("p", { className: "pbb-diag" }, remoteMessage) : null), (0, react.createElement)("button", {
3512
+ }, 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, remoteMessage ? (0, react.createElement)("p", { className: "pbb-diag" }, remoteMessage) : null), (0, react.createElement)("button", {
3349
3513
  type: "button",
3350
3514
  role: "switch",
3351
3515
  "aria-checked": remoteEnabled,
3352
- "aria-label": "远程连接",
3516
+ "aria-label": translateWith(props.t, "remote.title"),
3353
3517
  disabled: !remoteSwitchReady,
3354
3518
  className: "pbb-switch" + (remoteEnabled ? " pbb-switchOn" : ""),
3355
3519
  onClick: () => {
3356
3520
  if (typeof props.setDeepPilotRemoteEnabled === "function") props.setDeepPilotRemoteEnabled(!remoteEnabled);
3357
3521
  }
3358
- })), (0, react.createElement)("details", { className: "pbb-help" }, (0, react.createElement)("summary", null, "远程连接帮助"), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, "推荐设置流程"), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, "先打开“DeepPilot 连接”,再打开“远程连接(Tailscale Funnel)”。"), (0, react.createElement)("li", null, "状态变为“等待 Tailscale 授权”后,点击“打开授权页面”。"), (0, react.createElement)("li", null, "使用有权管理此 Tailnet 的账号登录,并按授权页提示启用 Funnel。通常需要 Owner、Admin 或 Network admin 权限。"), (0, react.createElement)("li", null, "返回此页面并点击“刷新”。远程连接标题旁出现绿点后,即可扫描下方二维码添加手机。")), (0, react.createElement)("p", { className: "pbb-helpText" }, "插件已内嵌 Tailscale 网络组件,这台电脑和手机都不需要另外安装 Tailscale App;但首次启用仍需由 Tailnet 管理员授权。")), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, "授权页未自动完成时:开启 HTTPS"), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, "打开 Tailscale 管理后台的 Network → DNS。"), (0, react.createElement)("li", null, "确认 MagicDNS 已开启。"), (0, react.createElement)("li", null, "在 HTTPS Certificates 中点击 Enable HTTPS。")), (0, react.createElement)("p", { className: "pbb-helpText" }, "启用 HTTPS 后,设备的完整域名会写入公开的证书透明度日志。如果设备名包含敏感信息,请先在 Tailscale 中重命名设备。")), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, "授权页未自动完成时:允许 Funnel"), (0, react.createElement)("p", { className: "pbb-helpText" }, "打开 Access controls → Definitions,选择 Node attributes。在现有 nodeAttrs 数组中追加下面这一项;不要覆盖已有访问规则,也不要创建第二个 nodeAttrs 顶层字段。"), (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" }, "保存策略后回到本页刷新。公共 DNS 和权限变更可能需要几分钟生效。")), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, "常见问题"), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, "提示“HTTPS must be enabled”:完成上面的 HTTPS Certificates 设置。"), (0, react.createElement)("li", null, "提示“Funnel not available”:确认 nodeAttrs 已保存,并且当前节点属于规则的 target。"), (0, react.createElement)("li", null, "已有公网地址但手机超时:等待几分钟后重试,同时确认这台电脑未休眠、DSH 正在运行,再重新扫描最新二维码。"), (0, react.createElement)("li", null, "没有公网地址:检查 Tailscale 授权是否完成,然后点击“刷新”或重启 DSH。"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, "安全说明"), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, "Funnel 公网地址可从互联网访问,但手机接口仍需要配对 Token。不要分享二维码、Token 或包含它们的截图。"), (0, react.createElement)("li", null, "怀疑 Token 泄露时,点击上方“更换”生成新 Token:旧 Token 立即失效,所有已连接设备会被断开并需要重新配对。"), (0, react.createElement)("li", null, "插件只通过 Funnel 转发 /phone 和 /phone/health,不会新增 3098 端口。"), (0, react.createElement)("li", null, "局域网连接沿用 DSH 的 3080 端口;仅在可信网络中使用,不建议直接把 3080 暴露到公网。"), (0, react.createElement)("li", null, "关闭远程连接开关会停止 Funnel,但不会影响可用的局域网连接。")), (0, react.createElement)("p", { className: "pbb-helpText" }, "更多信息:", (0, react.createElement)("a", {
3522
+ })), (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", {
3359
3523
  className: "pbb-helpLink",
3360
3524
  href: "https://tailscale.com/docs/features/tailscale-funnel",
3361
3525
  target: "_blank",
3362
3526
  rel: "noreferrer"
3363
- }, "Tailscale Funnel 官方文档")))))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "扫码添加当前手机"), pairingTarget === null ? (0, react.createElement)("span", { className: "pbb-value pbb-bad" }, "没有可用地址") : (0, react.createElement)("span", { className: "pbb-tokenActions" }, (0, react.createElement)("span", { className: "pbb-badge" }, pairingTarget.kind === "public" ? "公网" : "内网"), (0, react.createElement)("button", {
3527
+ }, translateWith(props.t, "help.funnelDocs"))))))), report === null ? null : (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "pair.qrPanelTitle")), pairingTarget === null ? (0, react.createElement)("span", { className: "pbb-value pbb-bad" }, translateWith(props.t, "pair.noAddress")) : (0, react.createElement)("span", { className: "pbb-tokenActions" }, (0, react.createElement)("span", { className: "pbb-badge" }, pairingTarget.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan")), (0, react.createElement)("button", {
3364
3528
  type: "button",
3365
3529
  className: "pbb-action",
3366
3530
  disabled: qrBusy || !report.tokenReady,
@@ -3368,35 +3532,280 @@ window.__ModuleLoader__.load({
3368
3532
  if (qrDataURL === null) showPairingQR();
3369
3533
  else setQRDataURL(null);
3370
3534
  }
3371
- }, qrBusy ? "生成中…" : qrDataURL === null ? "显示二维码" : "隐藏二维码"))), pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, "未发现可供手机访问的局域网地址,请确认这台电脑已连接局域网。") : null, qrMessage ? (0, react.createElement)("p", { className: "pbb-diag" }, qrMessage) : null, pairingTarget === null || qrDataURL === null ? null : (0, react.createElement)("div", { className: "pbb-qrPanel" }, (0, react.createElement)("img", {
3535
+ }, qrBusy ? translateWith(props.t, "pair.qrGenerating") : qrDataURL === null ? translateWith(props.t, "pair.qrShow") : translateWith(props.t, "pair.qrHide")))), pairingTarget === null ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "pair.noAddressHelp")) : null, qrMessage ? (0, react.createElement)("p", { className: "pbb-diag" }, qrMessage) : null, pairingTarget === null || qrDataURL === null ? null : (0, react.createElement)("div", { className: "pbb-qrPanel" }, (0, react.createElement)("img", {
3372
3536
  className: "pbb-qrImage",
3373
3537
  src: qrDataURL,
3374
- alt: "DeepPilot 配对二维码"
3538
+ alt: translateWith(props.t, "pair.qrAlt")
3375
3539
  }), (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token" }, pairingTarget.host), (0, react.createElement)("button", {
3376
3540
  type: "button",
3377
3541
  className: "pbb-action",
3378
3542
  onClick: () => copyRemoteURL(pairingTarget.host)
3379
- }, "复制")), (0, react.createElement)("p", { className: "pbb-qrHint" }, `二维码包含${pairingTarget.kind === "public" ? "公网" : "内网"}地址和配对 Token,将在 60 秒后自动隐藏。`)))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, primaryRows, advancedRows.length > 0 ? (0, react.createElement)("details", { className: "pbb-help" }, (0, react.createElement)("summary", null, "高级信息"), (0, react.createElement)("div", { className: "pbb-helpBody" }, advancedRows)) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "离线推送中继"), (0, react.createElement)("span", { className: "pbb-tokenActions" }, (0, react.createElement)("button", {
3543
+ }, translateWith(props.t, "panel.tokenAction.copy"))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: pairingTarget.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") }))))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, primaryRows, advancedRows.length > 0 ? (0, react.createElement)("details", { className: "pbb-help" }, (0, react.createElement)("summary", null, translateWith(props.t, "advanced.summary")), (0, react.createElement)("div", { className: "pbb-helpBody" }, advancedRows)) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "push.relayTitle")), (0, react.createElement)("span", { className: "pbb-tokenActions" }, (0, react.createElement)("button", {
3380
3544
  type: "button",
3381
3545
  className: "pbb-action",
3382
3546
  disabled: relayTestBusy,
3383
3547
  onClick: runRelayTest
3384
- }, relayTestBusy ? "测试中…" : "测试访问与注册"), (0, react.createElement)("button", {
3548
+ }, relayTestBusy ? translateWith(props.t, "push.relayTesting") : translateWith(props.t, "push.testRelay")), (0, react.createElement)("button", {
3385
3549
  type: "button",
3386
3550
  className: "pbb-action",
3387
3551
  disabled: pushTestBusy,
3388
3552
  onClick: sendPushTest
3389
- }, pushTestBusy ? "发送中…" : "发送测试通知"))), relayTestError ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, relayTestError) : null, relayTestResult === null ? (0, react.createElement)("p", { className: "pbb-diag" }, "验证 Mac 到推送中继的连通性与自动注册是否正常。") : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || "(未启用)"), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? "通过" : "存在问题")), relayTestResult.steps.map((step, index) => (0, react.createElement)("p", {
3553
+ }, pushTestBusy ? translateWith(props.t, "push.pushSending") : translateWith(props.t, "push.testPush")))), 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", {
3390
3554
  className: "pbb-diag",
3391
3555
  key: String(index)
3392
- }, (step.ok ? "✓ " : "✗ ") + (step.id === "health" ? "服务可达" : "自动注册") + (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" }, "向所有已注册设备强制发送一条真实推送(不受在线状态与分类开关影响)。") : (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" ? "已送达" : pushTestResult.overall === "failed" ? "发送失败" : pushTestResult.overall === "no-targets" ? "无已注册设备" : "推送未启用")), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, pushTestResult.message) : null, pushTestResult.results.map((r, index) => (0, react.createElement)("p", {
3556
+ }, (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", {
3393
3557
  className: "pbb-diag",
3394
3558
  key: String(index)
3395
- }, (r.outcome === "sent" ? "✓ " : "✗ ") + r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : "")))))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, "已配对设备"), (0, react.createElement)("span", { className: "pbb-badge" }, String(report !== null ? report.devices.length : 0))), deviceTable)), report === null ? null : (0, react.createElement)("div", { className: "pbb-versionFooter" }, (0, react.createElement)("span", null, "DeepPilot v" + report.pluginVersion), report.updateAvailable === true ? (0, react.createElement)("a", {
3559
+ }, (r.outcome === "sent" ? "✓ " : "✗ ") + r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : "")))))), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-field" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "devices.title")), (0, react.createElement)("span", { className: "pbb-badge" }, String(report !== null ? report.devices.length : 0))), deviceTable)), report === null ? null : (0, react.createElement)("div", { className: "pbb-versionFooter" }, (0, react.createElement)("span", null, "DeepPilot v" + report.pluginVersion), report.updateAvailable === true ? (0, react.createElement)("a", {
3396
3560
  href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
3397
3561
  target: "_blank",
3398
3562
  rel: "noreferrer"
3399
- }, "有新版本") : null), diag.length > 0 ? (0, react.createElement)("p", { className: "pbb-diag" + (failed ? " pbb-diagBad" : "") }, "diag: " + diag.join(" | ")) : null);
3563
+ }, translateWith(props.t, "update.badge")) : null), diag.length > 0 ? (0, react.createElement)("p", { className: "pbb-diag" + (failed ? " pbb-diagBad" : "") }, translateWith(props.t, "diag.prefix") + diag.join(" | ")) : null);
3564
+ }
3565
+ //#endregion
3566
+ //#region src/client/index.ts
3567
+ /** Polls the report remote and owns the page state transitions. */
3568
+ var ReportController = class {
3569
+ fetchReport;
3570
+ t;
3571
+ listeners = /* @__PURE__ */ new Set();
3572
+ snap = {
3573
+ status: "loading",
3574
+ report: null,
3575
+ message: ""
3576
+ };
3577
+ constructor(fetchReport, t) {
3578
+ this.fetchReport = fetchReport;
3579
+ this.t = t;
3580
+ }
3581
+ state() {
3582
+ return this.snap;
3583
+ }
3584
+ subscribe(listener) {
3585
+ this.listeners.add(listener);
3586
+ return () => this.listeners.delete(listener);
3587
+ }
3588
+ dispose() {
3589
+ this.listeners.clear();
3590
+ }
3591
+ async refresh() {
3592
+ try {
3593
+ const report = await this.fetchReport();
3594
+ this.snap = report !== null ? {
3595
+ status: "ready",
3596
+ report,
3597
+ message: ""
3598
+ } : {
3599
+ status: "error",
3600
+ report: null,
3601
+ message: this.t("diag.mountFailed")
3602
+ };
3603
+ } catch (error) {
3604
+ this.snap = {
3605
+ status: "error",
3606
+ report: null,
3607
+ message: error instanceof Error ? error.message : String(error)
3608
+ };
3609
+ }
3610
+ this.emit();
3611
+ }
3612
+ emit() {
3613
+ for (const listener of [...this.listeners]) try {
3614
+ listener();
3615
+ } catch {}
3616
+ }
3617
+ };
3618
+ const inject = [
3619
+ "slots",
3620
+ "locale",
3621
+ "remote",
3622
+ "settingsScope"
3623
+ ];
3624
+ function apply(ctx) {
3625
+ if (typeof document !== "undefined") injectCss();
3626
+ const anyCtx = ctx;
3627
+ ctx.effect(() => registerLocale(ctx), "dsh-deeppilot: locale dictionaries");
3628
+ let namespace;
3629
+ let mountError;
3630
+ const fetchReport = async () => {
3631
+ if (namespace === void 0) throw new Error(mountError !== void 0 ? t(ctx, "diag.mountFailedShort") + mountError : t(ctx, "diag.remoteUnmounted"));
3632
+ const result = await namespace.report();
3633
+ if (!result.ok) throw new Error(result.error.message ?? t(ctx, "diag.callFailed"));
3634
+ return result.value;
3635
+ };
3636
+ const tPage = (key, vars) => t(ctx, key, vars);
3637
+ const controller = new ReportController(fetchReport, tPage);
3638
+ ctx.effect(() => () => controller.dispose(), "dsh-deeppilot: report controller");
3639
+ const store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(controller.state());
3640
+ controller.subscribe(() => store.set(controller.state()));
3641
+ ctx.effect(() => {
3642
+ let cancelled = false;
3643
+ let unmount;
3644
+ if (anyCtx.remote === void 0) {
3645
+ mountError = t(ctx, "diag.remoteUnavailable");
3646
+ controller.refresh();
3647
+ return () => {};
3648
+ }
3649
+ mountReportRemote(anyCtx.remote, () => ctx.get("remote.deeppilot")).then((mounted) => {
3650
+ if (cancelled) {
3651
+ mounted.dispose();
3652
+ return;
3653
+ }
3654
+ namespace = mounted.namespace;
3655
+ unmount = mounted.dispose;
3656
+ mountError = void 0;
3657
+ controller.refresh();
3658
+ }, (error) => {
3659
+ mountError = error instanceof Error ? error.message : String(error);
3660
+ controller.refresh();
3661
+ });
3662
+ return () => {
3663
+ cancelled = true;
3664
+ namespace = void 0;
3665
+ if (unmount !== void 0) unmount();
3666
+ };
3667
+ }, "dsh-deeppilot: report remote mount");
3668
+ const revealPairingToken = async () => {
3669
+ if (namespace === void 0) throw new Error(mountError !== void 0 ? t(ctx, "diag.mountFailedShort") + mountError : t(ctx, "diag.remoteUnmounted"));
3670
+ const result = await namespace.revealToken();
3671
+ if (!result.ok) throw new Error(result.error.message ?? t(ctx, "panel.tokenRevealFailed"));
3672
+ return result.value;
3673
+ };
3674
+ const sendTestPush = async () => {
3675
+ if (namespace === void 0) throw new Error(mountError !== void 0 ? t(ctx, "diag.mountFailedShort") + mountError : t(ctx, "diag.remoteUnmounted"));
3676
+ if (typeof namespace.testPush !== "function") throw new Error(t(ctx, "push.staleHostPush"));
3677
+ const result = await namespace.testPush();
3678
+ if (!result.ok) throw new Error(result.error.message ?? t(ctx, "push.pushFailed"));
3679
+ return result.value;
3680
+ };
3681
+ const testRelayConnection = async () => {
3682
+ if (namespace === void 0) throw new Error(mountError !== void 0 ? t(ctx, "diag.mountFailedShort") + mountError : t(ctx, "diag.remoteUnmounted"));
3683
+ if (typeof namespace.testRelay !== "function") throw new Error(t(ctx, "push.staleHostPushRelay"));
3684
+ const result = await namespace.testRelay();
3685
+ if (!result.ok) throw new Error(result.error.message ?? t(ctx, "push.relayBad"));
3686
+ return result.value;
3687
+ };
3688
+ const rotatePairingToken = async () => {
3689
+ if (namespace === void 0) throw new Error(mountError !== void 0 ? t(ctx, "diag.mountFailedShort") + mountError : t(ctx, "diag.remoteUnmounted"));
3690
+ const result = await namespace.rotateToken();
3691
+ if (!result.ok) throw new Error(result.error.message ?? t(ctx, "panel.tokenRotateFailed"));
3692
+ return result.value;
3693
+ };
3694
+ const enabledStore = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
3695
+ status: "loading",
3696
+ enabled: true
3697
+ });
3698
+ const scope = anyCtx.settingsScope?.bind({ namespace: "deeppilot" });
3699
+ const adoptEnabled = () => {
3700
+ if (scope === void 0) return;
3701
+ const snap = scope.getSnapshot();
3702
+ if (snap.status === "ready" && snap.value !== void 0) enabledStore.set({
3703
+ status: "ready",
3704
+ enabled: snap.value.enabled !== false
3705
+ });
3706
+ else if (snap.status === "unavailable") enabledStore.set({
3707
+ status: "unavailable",
3708
+ enabled: true
3709
+ });
3710
+ };
3711
+ if (scope !== void 0) {
3712
+ scope.subscribe(adoptEnabled);
3713
+ adoptEnabled();
3714
+ }
3715
+ const lastConfirmedEnabled = () => {
3716
+ const snap = scope?.getSnapshot();
3717
+ if (snap?.status === "ready" && snap.value !== void 0) return snap.value.enabled !== false;
3718
+ return enabledStore.getSnapshot().enabled;
3719
+ };
3720
+ const lastConfirmedRemoteEnabled = () => {
3721
+ const snap = scope?.getSnapshot();
3722
+ if (snap?.status === "ready") return snap.value?.remote?.enabled === true;
3723
+ return remoteEnabledStore.getSnapshot().enabled;
3724
+ };
3725
+ const setDeepPilotEnabled = (value) => {
3726
+ const previous = lastConfirmedEnabled();
3727
+ enabledStore.set({
3728
+ status: "ready",
3729
+ enabled: value
3730
+ });
3731
+ if (scope === void 0) return;
3732
+ scope.set("enabled", value).then(() => {}, (error) => {
3733
+ enabledStore.set({
3734
+ status: "ready",
3735
+ enabled: previous
3736
+ });
3737
+ const message = error instanceof Error ? error.message : String(error);
3738
+ console.error("[deeppilot] failed to persist enabled=" + String(value) + ": " + message);
3739
+ });
3740
+ };
3741
+ const remoteEnabledStore = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
3742
+ status: "loading",
3743
+ enabled: false
3744
+ });
3745
+ const adoptRemoteEnabled = () => {
3746
+ if (scope === void 0) return;
3747
+ const snap = scope.getSnapshot();
3748
+ if (snap.status === "ready") remoteEnabledStore.set({
3749
+ status: "ready",
3750
+ enabled: snap.value?.remote?.enabled === true
3751
+ });
3752
+ else if (snap.status === "unavailable") remoteEnabledStore.set({
3753
+ status: "unavailable",
3754
+ enabled: false
3755
+ });
3756
+ };
3757
+ if (scope !== void 0) {
3758
+ scope.subscribe(adoptRemoteEnabled);
3759
+ adoptRemoteEnabled();
3760
+ }
3761
+ const setDeepPilotRemoteEnabled = (value) => {
3762
+ const previous = lastConfirmedRemoteEnabled();
3763
+ remoteEnabledStore.set({
3764
+ status: "ready",
3765
+ enabled: value
3766
+ });
3767
+ if (scope === void 0) return;
3768
+ const currentRemote = scope.getSnapshot().value?.remote ?? {};
3769
+ scope.set("remote", {
3770
+ ...currentRemote,
3771
+ enabled: value
3772
+ }).then(() => {}, (error) => {
3773
+ remoteEnabledStore.set({
3774
+ status: "ready",
3775
+ enabled: previous
3776
+ });
3777
+ const message = error instanceof Error ? error.message : String(error);
3778
+ console.error("[deeppilot] failed to persist remote.enabled=" + String(value) + ": " + message);
3779
+ });
3780
+ };
3781
+ if (anyCtx.slots === void 0) console.error("[deeppilot] settings slots service unavailable; the DeepPilot section will not appear");
3782
+ anyCtx.slots?.inject("settings.section", () => anyCtx.slots.register({
3783
+ name: "settings.section",
3784
+ id: "deeppilot",
3785
+ order: 13,
3786
+ label: () => {
3787
+ const bind = anyCtx.locale?.bind("settings.deeppilot");
3788
+ return bind ? bind("nav") : "DeepPilot";
3789
+ },
3790
+ locale: "settings.deeppilot",
3791
+ inject: () => ({
3792
+ hooks: {
3793
+ deepPilotReport: store,
3794
+ deepPilotEnabled: enabledStore,
3795
+ deepPilotRemoteEnabled: remoteEnabledStore
3796
+ },
3797
+ refresh: () => {
3798
+ controller.refresh();
3799
+ },
3800
+ revealPairingToken,
3801
+ rotatePairingToken,
3802
+ testRelay: testRelayConnection,
3803
+ testPush: sendTestPush,
3804
+ setDeepPilotEnabled,
3805
+ setDeepPilotRemoteEnabled,
3806
+ t: (key, vars) => t(ctx, key, vars)
3807
+ })
3808
+ }, DeepPilotSettingsPage));
3400
3809
  }
3401
3810
  //#endregion
3402
3811
  exports.DeepPilotSettingsPage = DeepPilotSettingsPage;