dsh-remote-control 1.2.0 → 2.0.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/dist/bundle/client.cjs +142 -71
- package/dist/bundle/index.js +1013 -222
- package/package.json +2 -2
package/dist/bundle/client.cjs
CHANGED
|
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(main_exports);
|
|
|
36
36
|
var STATUS_ROUTE = "/plugins/dsh-remote-control/status";
|
|
37
37
|
var NEW_ROUTE = "/plugins/dsh-remote-control/pairing/new";
|
|
38
38
|
var IMAGE_ROUTE = "/plugins/dsh-remote-control/pairing.png";
|
|
39
|
+
var UNPAIR_ROUTE = "/plugins/dsh-remote-control/unpair";
|
|
39
40
|
var SLOT_NAME = "conversation.composer.dock";
|
|
40
41
|
var PILL_ID = "dsh-remote-control";
|
|
41
42
|
var PILL_ORDER = 20;
|
|
@@ -65,34 +66,53 @@ function panelViewFor(answer, httpStatus) {
|
|
|
65
66
|
function pillLabel(status) {
|
|
66
67
|
const relay = status?.relay;
|
|
67
68
|
if (relay === "idle") return { text: "\u8FDC\u7A0B\u672A\u542F\u52A8", tone: "off" };
|
|
68
|
-
if (relay === "offline") return { text: "\
|
|
69
|
+
if (relay === "offline") return { text: "\u5DF2\u65AD\u5F00\u8FDE\u63A5", tone: "error" };
|
|
69
70
|
if (relay === "connecting") return { text: "\u8FDE\u63A5\u4E2D", tone: "wait" };
|
|
70
71
|
if (relay !== "online") return { text: "\u8FDC\u7A0B\u63A7\u5236", tone: "off" };
|
|
71
72
|
const paired = typeof status?.paired === "number" ? status.paired : 0;
|
|
72
|
-
|
|
73
|
+
if (paired > 0) {
|
|
74
|
+
const waiting = waitingCount(status);
|
|
75
|
+
if (waiting > 0) return { text: `\u7B49 ${waiting} \u4EF6\u4E8B`, tone: "wait" };
|
|
76
|
+
return { text: "\u5DF2\u914D\u5BF9", tone: "on" };
|
|
77
|
+
}
|
|
78
|
+
return { text: "\u672A\u914D\u5BF9", tone: "off" };
|
|
79
|
+
}
|
|
80
|
+
function waitingCount(status) {
|
|
81
|
+
const count = status?.waiting;
|
|
82
|
+
return typeof count === "number" && count > 0 ? Math.min(Math.floor(count), 99) : 0;
|
|
83
|
+
}
|
|
84
|
+
function humanDuration(sec) {
|
|
85
|
+
const total = typeof sec === "number" && sec > 0 ? Math.floor(sec) : 0;
|
|
86
|
+
if (total < 60) return `${total} \u79D2`;
|
|
87
|
+
const minutes = Math.floor(total / 60);
|
|
88
|
+
return minutes >= 10 ? `${minutes} \u5206` : `${minutes} \u5206 ${total % 60} \u79D2`;
|
|
89
|
+
}
|
|
90
|
+
function waitingRow(status) {
|
|
91
|
+
const count = waitingCount(status);
|
|
92
|
+
if (count === 0) return "";
|
|
93
|
+
const oldest = typeof status?.waitingOldestSec === "number" ? status.waitingOldestSec : 0;
|
|
94
|
+
return `${count} \u4EF6${oldest > 0 ? ` \xB7 \u6700\u4E45 ${humanDuration(oldest)}` : ""}`.slice(0, 40);
|
|
73
95
|
}
|
|
74
|
-
var RELAY_TEXT = {
|
|
75
|
-
online: "\u5DF2\u8FDE\u63A5",
|
|
76
|
-
connecting: "\u8FDE\u63A5\u4E2D",
|
|
77
|
-
offline: "\u672A\u8FDE\u63A5",
|
|
78
|
-
idle: "\u672A\u542F\u52A8"
|
|
79
|
-
};
|
|
80
96
|
function relayAddress(url) {
|
|
81
97
|
if (typeof url !== "string" || url.trim() === "") return "";
|
|
82
98
|
const withoutScheme = url.trim().replace(/^[a-z][a-z0-9+.-]*:\/\//i, "");
|
|
83
99
|
const authority = /^[^/?]*/.exec(withoutScheme)?.[0] ?? "";
|
|
84
100
|
return authority.slice(0, 40);
|
|
85
101
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
102
|
+
function relayRow(status) {
|
|
103
|
+
return relayAddress(status?.serverUrl);
|
|
104
|
+
}
|
|
105
|
+
function relayStateRow(status) {
|
|
106
|
+
const relay = status?.relay;
|
|
107
|
+
if (relay === "online") return "\u5DF2\u8FDE\u63A5";
|
|
108
|
+
if (relay === "connecting") return "\u8FDE\u63A5\u4E2D";
|
|
109
|
+
if (relay === "offline") return "\u5DF2\u65AD\u5F00";
|
|
110
|
+
if (relay === "idle") return "\u672A\u542F\u52A8";
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
function versionRow(status) {
|
|
114
|
+
const version = status?.version;
|
|
115
|
+
return typeof version === "string" && version.trim() !== "" ? version.trim().slice(0, 24) : "";
|
|
96
116
|
}
|
|
97
117
|
function secondsText(ms) {
|
|
98
118
|
const whole = Math.max(0, Math.round(ms / 1e3));
|
|
@@ -112,24 +132,36 @@ var CSS = `
|
|
|
112
132
|
.drc-dot { width: 6px; height: 6px; flex: 0 0 auto; border-radius: 50%; background: var(--dsw-alias-label-dimmed, #9aa0a6); }
|
|
113
133
|
.drc-dot[data-tone="wait"] { background: var(--dsw-alias-state-warn-primary, #faad14); }
|
|
114
134
|
.drc-dot[data-tone="on"] { background: var(--dsw-alias-state-success-primary, #52c41a); }
|
|
135
|
+
.drc-dot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #ff4d4f); }
|
|
115
136
|
.drc-panel { position: absolute; bottom: calc(100% + 8px); right: 0; z-index: 30; width: 240px;
|
|
116
|
-
padding: 12px
|
|
137
|
+
padding: 10px 12px; border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1)); border-radius: 12px;
|
|
117
138
|
background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-1, #ffffff));
|
|
118
139
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14), 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
119
140
|
color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 18px; }
|
|
120
|
-
/* \
|
|
121
|
-
.drc-
|
|
122
|
-
|
|
123
|
-
.drc-
|
|
141
|
+
/* \u62AC\u5934\u4E00\u884C\uFF1A\u5DE6\u4FA7\u72B6\u6001\uFF0C\u53F3\u4FA7\u52A8\u4F5C\u3002\u4E24\u9897\u6309\u94AE\u90FD\u6536\u5728\u8FD9\u4E2A\u53F3\u4E0A\u89D2\uFF082026-10-03 \u91CD\u8BBE\u8BA1\u7684\u8981\u6C42\uFF09\u3002 */
|
|
142
|
+
.drc-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
143
|
+
.drc-head-label { font-weight: 600; }
|
|
144
|
+
.drc-head-label[data-tone="off"] { color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
145
|
+
.drc-head-label[data-tone="wait"] { color: var(--dsw-alias-state-warn-primary, #faad14); }
|
|
146
|
+
.drc-head-label[data-tone="on"] { color: var(--dsw-alias-state-success-primary, #52c41a); }
|
|
147
|
+
.drc-head-label[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #ff4d4f); }
|
|
148
|
+
.drc-actions { display: flex; gap: 6px; flex: 0 0 auto; }
|
|
149
|
+
.drc-btn { padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1));
|
|
150
|
+
border-radius: 8px; background: transparent; color: inherit; font: inherit; font-size: 12px; cursor: pointer;
|
|
151
|
+
white-space: nowrap; }
|
|
152
|
+
.drc-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.04)); }
|
|
153
|
+
/* \u6B63\u6587\u90A3\u4E09\u884C\uFF08\u4E2D\u7EE7 / \u72B6\u6001 / \u7248\u672C\uFF09\uFF1A\u952E\u5DE6\u503C\u53F3\uFF08space-between\uFF09\uFF0C\u503C\u88AB\u622A\u65AD\u65F6\u7701\u7565\u53F7\u2014\u2014
|
|
154
|
+
\u548C\u90A3\u9897 pill \u540C\u4E00\u5957"\u6324\u4E0D\u4E0B\u5C31\u622A\u6587\u5B57\u4E0D\u6491\u5BB9\u5668"\u3002\u7B2C\u4E00\u884C\u5E26\u90A3\u6761\u5206\u9694\u7EBF\uFF0C\u540E\u4E24\u884C\u53EA\u7559\u884C\u8DDD\u3002 */
|
|
155
|
+
.drc-info { display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
|
|
156
|
+
white-space: nowrap; margin-top: 8px; padding-top: 6px;
|
|
157
|
+
border-top: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.08)); }
|
|
158
|
+
.drc-info + .drc-info { margin-top: 2px; padding-top: 0; border-top: 0; }
|
|
124
159
|
.drc-key { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); }
|
|
125
|
-
.drc-value { min-width: 0; overflow: hidden; text-overflow: ellipsis;
|
|
126
|
-
.drc-qr { display: block; width: 200px; height: 200px; margin:
|
|
160
|
+
.drc-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
161
|
+
.drc-qr { display: block; width: 200px; height: 200px; margin: 8px auto 0; image-rendering: pixelated; }
|
|
127
162
|
.drc-code { margin: 8px 0 0; text-align: center; font-weight: 600; font-size: 14px; letter-spacing: 2px; }
|
|
128
163
|
.drc-note { margin: 4px 0 0; text-align: center; color: var(--dsw-alias-label-tertiary); }
|
|
129
164
|
.drc-note[data-kind="failed"] { color: var(--dsw-alias-state-error-primary, #ff4d4f); }
|
|
130
|
-
.drc-actions { display: flex; gap: 6px; justify-content: center; margin-top: 8px; }
|
|
131
|
-
.drc-btn { padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1));
|
|
132
|
-
border-radius: 8px; background: transparent; color: inherit; font: inherit; font-size: 12px; cursor: pointer; }
|
|
133
165
|
`;
|
|
134
166
|
function isSlots(value) {
|
|
135
167
|
if (!value || typeof value !== "object") return false;
|
|
@@ -168,7 +200,11 @@ function warn(message, error) {
|
|
|
168
200
|
}
|
|
169
201
|
function ensureStyle(doc) {
|
|
170
202
|
try {
|
|
171
|
-
|
|
203
|
+
const existing = doc.querySelector(`style[data-plugin-css="${STYLE_ID}"]`);
|
|
204
|
+
if (existing) {
|
|
205
|
+
if (existing.textContent !== CSS) existing.textContent = CSS;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
172
208
|
const element = doc.createElement("style");
|
|
173
209
|
element.setAttribute("data-plugin-css", STYLE_ID);
|
|
174
210
|
element.textContent = CSS;
|
|
@@ -222,59 +258,61 @@ function buildPill(root, deps) {
|
|
|
222
258
|
node.addEventListener("click", onClick);
|
|
223
259
|
return node;
|
|
224
260
|
};
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
261
|
+
const qrNote = () => expiresInMs > 0 ? `\u626B\u7801\u914D\u5BF9 \xB7 ${secondsText(expiresInMs)}\u540E\u8FC7\u671F` : "\u914D\u5BF9\u7801\u5DF2\u8FC7\u671F\uFF0C\u70B9\u53F3\u4E0A\u89D2\u300C\u5237\u65B0\u300D\u91CD\u65B0\u751F\u6210";
|
|
262
|
+
const header = () => {
|
|
263
|
+
const head = doc.createElement("div");
|
|
264
|
+
head.className = "drc-head";
|
|
265
|
+
const state = pillLabel(lastStatus);
|
|
266
|
+
const stateText = text("span", "drc-head-label", state.text);
|
|
267
|
+
stateText.setAttribute("data-tone", state.tone);
|
|
268
|
+
head.appendChild(stateText);
|
|
269
|
+
const actions = doc.createElement("div");
|
|
270
|
+
actions.className = "drc-actions";
|
|
271
|
+
const action = pairedNow() > 0 ? { label: "\u9000\u51FA\u914D\u5BF9", run: requestUnpair } : { label: "\u5237\u65B0", run: requestPairing };
|
|
272
|
+
actions.appendChild(buttonOf(action.label, () => void action.run()));
|
|
273
|
+
head.appendChild(actions);
|
|
274
|
+
return head;
|
|
275
|
+
};
|
|
276
|
+
const infoRows = () => {
|
|
277
|
+
if (!lastStatus) return [];
|
|
278
|
+
const rows = [
|
|
279
|
+
// 排第一:它是唯一一条"看完要做点什么"的,其余三条都是"知道就行"。
|
|
280
|
+
["\u5F85\u5904\u7406", waitingRow(lastStatus)],
|
|
281
|
+
["\u4E2D\u7EE7", relayRow(lastStatus)],
|
|
282
|
+
["\u72B6\u6001", relayStateRow(lastStatus)],
|
|
283
|
+
["\u7248\u672C", versionRow(lastStatus)]
|
|
284
|
+
];
|
|
285
|
+
return rows.filter(([, value]) => value !== "").map(([key, value]) => {
|
|
230
286
|
const row = doc.createElement("div");
|
|
231
|
-
row.className = "drc-
|
|
287
|
+
row.className = "drc-info";
|
|
232
288
|
row.appendChild(text("span", "drc-key", key));
|
|
233
289
|
row.appendChild(text("span", "drc-value", value));
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
return box;
|
|
290
|
+
return row;
|
|
291
|
+
});
|
|
237
292
|
};
|
|
238
293
|
const paint = () => {
|
|
239
294
|
if (!panel) return;
|
|
240
295
|
panel.replaceChildren();
|
|
241
|
-
|
|
242
|
-
if (info) panel.appendChild(info);
|
|
243
|
-
if (view.kind === "info") {
|
|
244
|
-
const actions2 = doc.createElement("div");
|
|
245
|
-
actions2.className = "drc-actions";
|
|
246
|
-
actions2.appendChild(buttonOf(pairedNow() > 0 ? "\u518D\u914D\u4E00\u53F0" : "\u751F\u6210\u914D\u5BF9\u7801", () => void requestPairing()));
|
|
247
|
-
panel.appendChild(actions2);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
296
|
+
panel.appendChild(header());
|
|
250
297
|
if (view.kind === "loading") {
|
|
251
298
|
panel.appendChild(text("p", "drc-note", "\u6B63\u5728\u751F\u6210\u914D\u5BF9\u7801\u2026"));
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
if (view.kind === "qr") {
|
|
299
|
+
} else if (view.kind === "qr") {
|
|
255
300
|
const image = doc.createElement("img");
|
|
256
301
|
image.className = "drc-qr";
|
|
257
302
|
image.setAttribute("alt", "\u914D\u5BF9\u4E8C\u7EF4\u7801");
|
|
258
303
|
image.src = view.imageSrc;
|
|
259
304
|
panel.appendChild(image);
|
|
260
305
|
panel.appendChild(text("p", "drc-code", view.token));
|
|
261
|
-
panel.appendChild(text("p", "drc-note",
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
306
|
+
panel.appendChild(text("p", "drc-note", qrNote()));
|
|
307
|
+
} else if (view.kind === "unavailable" || view.kind === "failed") {
|
|
308
|
+
const note = view.kind === "unavailable" ? text("p", "drc-note", view.reason) : (() => {
|
|
309
|
+
const node = text("p", "drc-note", `\u914D\u5BF9\u8BF7\u6C42\u6CA1\u6210\u529F\uFF1A${view.detail}`);
|
|
310
|
+
node.setAttribute("data-kind", "failed");
|
|
311
|
+
return node;
|
|
312
|
+
})();
|
|
313
|
+
panel.appendChild(note);
|
|
267
314
|
}
|
|
268
|
-
const
|
|
269
|
-
const node = text("p", "drc-note", `\u914D\u5BF9\u8BF7\u6C42\u6CA1\u6210\u529F\uFF1A${view.detail}`);
|
|
270
|
-
node.setAttribute("data-kind", "failed");
|
|
271
|
-
return node;
|
|
272
|
-
})();
|
|
273
|
-
panel.appendChild(note);
|
|
274
|
-
const actions = doc.createElement("div");
|
|
275
|
-
actions.className = "drc-actions";
|
|
276
|
-
actions.appendChild(buttonOf("\u518D\u8BD5\u4E00\u6B21", () => void requestPairing()));
|
|
277
|
-
panel.appendChild(actions);
|
|
315
|
+
for (const row of infoRows()) panel.appendChild(row);
|
|
278
316
|
};
|
|
279
317
|
const closePanel = () => {
|
|
280
318
|
if (countTimer !== void 0) {
|
|
@@ -313,24 +351,49 @@ function buildPill(root, deps) {
|
|
|
313
351
|
if (countTimer === void 0) {
|
|
314
352
|
countTimer = setInterval(() => {
|
|
315
353
|
expiresInMs -= 1e3;
|
|
316
|
-
if (expiresInMs <= 0)
|
|
317
|
-
|
|
354
|
+
if (expiresInMs <= 0) {
|
|
355
|
+
clearInterval(countTimer);
|
|
356
|
+
countTimer = void 0;
|
|
357
|
+
}
|
|
358
|
+
paint();
|
|
318
359
|
}, 1e3);
|
|
319
360
|
}
|
|
320
361
|
}
|
|
321
362
|
paint();
|
|
322
363
|
}
|
|
364
|
+
async function requestUnpair() {
|
|
365
|
+
if (disposed || !panel) return;
|
|
366
|
+
try {
|
|
367
|
+
await deps.fetchImpl(UNPAIR_ROUTE, {
|
|
368
|
+
method: "POST",
|
|
369
|
+
headers: { "x-drc-pair": "1" },
|
|
370
|
+
credentials: "same-origin"
|
|
371
|
+
});
|
|
372
|
+
} catch (error) {
|
|
373
|
+
warn("\u9000\u51FA\u914D\u5BF9\u8BF7\u6C42\u5931\u8D25\uFF08\u9762\u677F\u4F1A\u9760\u4E0B\u4E00\u6B21\u8F6E\u8BE2\u5BF9\u8D26\uFF09", error);
|
|
374
|
+
}
|
|
375
|
+
if (disposed || !panel) return;
|
|
376
|
+
if (lastStatus) lastStatus = { ...lastStatus, paired: 0 };
|
|
377
|
+
write();
|
|
378
|
+
view = { kind: "loading" };
|
|
379
|
+
paint();
|
|
380
|
+
void requestPairing();
|
|
381
|
+
}
|
|
323
382
|
const openPanel = () => {
|
|
324
383
|
if (panel || disposed) return;
|
|
325
384
|
panel = doc.createElement("div");
|
|
326
385
|
panel.className = "drc-panel";
|
|
327
386
|
panel.setAttribute("role", "dialog");
|
|
328
|
-
panel.setAttribute("aria-label", "dsh-remote-control \
|
|
387
|
+
panel.setAttribute("aria-label", "dsh-remote-control \u914D\u5BF9");
|
|
329
388
|
panel.tabIndex = -1;
|
|
330
389
|
root.appendChild(panel);
|
|
331
390
|
button.setAttribute("aria-expanded", "true");
|
|
332
|
-
|
|
333
|
-
|
|
391
|
+
if (pairedNow() > 0) {
|
|
392
|
+
view = { kind: "info" };
|
|
393
|
+
paint();
|
|
394
|
+
} else {
|
|
395
|
+
void requestPairing();
|
|
396
|
+
}
|
|
334
397
|
try {
|
|
335
398
|
panel.focus();
|
|
336
399
|
} catch {
|
|
@@ -367,6 +430,14 @@ function buildPill(root, deps) {
|
|
|
367
430
|
const body = await response.json?.();
|
|
368
431
|
if (!body || typeof body !== "object") return;
|
|
369
432
|
lastStatus = body;
|
|
433
|
+
if (view.kind === "qr" && pairedNow() > 0) {
|
|
434
|
+
view = { kind: "info" };
|
|
435
|
+
if (countTimer !== void 0) {
|
|
436
|
+
clearInterval(countTimer);
|
|
437
|
+
countTimer = void 0;
|
|
438
|
+
}
|
|
439
|
+
if (panel) paint();
|
|
440
|
+
}
|
|
370
441
|
write();
|
|
371
442
|
} catch (error) {
|
|
372
443
|
deps.warn?.("status poll failed");
|