dsh-remote-control 1.2.0 → 2.0.1
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 +155 -72
- package/dist/bundle/index.js +1164 -243
- 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,58 @@ 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
|
+
if (pairingCount(status) > 0) return { text: "\u624B\u673A\u79BB\u7EBF", tone: "wait" };
|
|
79
|
+
return { text: "\u672A\u914D\u5BF9", tone: "off" };
|
|
80
|
+
}
|
|
81
|
+
function waitingCount(status) {
|
|
82
|
+
const count = status?.waiting;
|
|
83
|
+
return typeof count === "number" && count > 0 ? Math.min(Math.floor(count), 99) : 0;
|
|
84
|
+
}
|
|
85
|
+
function pairingCount(status) {
|
|
86
|
+
const count = status?.pairings;
|
|
87
|
+
return typeof count === "number" ? count : 0;
|
|
88
|
+
}
|
|
89
|
+
function humanDuration(sec) {
|
|
90
|
+
const total = typeof sec === "number" && sec > 0 ? Math.floor(sec) : 0;
|
|
91
|
+
if (total < 60) return `${total} \u79D2`;
|
|
92
|
+
const minutes = Math.floor(total / 60);
|
|
93
|
+
return minutes >= 10 ? `${minutes} \u5206` : `${minutes} \u5206 ${total % 60} \u79D2`;
|
|
94
|
+
}
|
|
95
|
+
function waitingRow(status) {
|
|
96
|
+
const count = waitingCount(status);
|
|
97
|
+
if (count === 0) return "";
|
|
98
|
+
const oldest = typeof status?.waitingOldestSec === "number" ? status.waitingOldestSec : 0;
|
|
99
|
+
return `${count} \u4EF6${oldest > 0 ? ` \xB7 \u6700\u4E45 ${humanDuration(oldest)}` : ""}`.slice(0, 40);
|
|
73
100
|
}
|
|
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
101
|
function relayAddress(url) {
|
|
81
102
|
if (typeof url !== "string" || url.trim() === "") return "";
|
|
82
103
|
const withoutScheme = url.trim().replace(/^[a-z][a-z0-9+.-]*:\/\//i, "");
|
|
83
104
|
const authority = /^[^/?]*/.exec(withoutScheme)?.[0] ?? "";
|
|
84
105
|
return authority.slice(0, 40);
|
|
85
106
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
107
|
+
function relayRow(status) {
|
|
108
|
+
return relayAddress(status?.serverUrl);
|
|
109
|
+
}
|
|
110
|
+
function relayStateRow(status) {
|
|
111
|
+
const relay = status?.relay;
|
|
112
|
+
if (relay === "online") return "\u5DF2\u8FDE\u63A5";
|
|
113
|
+
if (relay === "connecting") return "\u8FDE\u63A5\u4E2D";
|
|
114
|
+
if (relay === "offline") return "\u5DF2\u65AD\u5F00";
|
|
115
|
+
if (relay === "idle") return "\u672A\u542F\u52A8";
|
|
116
|
+
return "";
|
|
117
|
+
}
|
|
118
|
+
function versionRow(status) {
|
|
119
|
+
const version = status?.version;
|
|
120
|
+
return typeof version === "string" && version.trim() !== "" ? version.trim().slice(0, 24) : "";
|
|
96
121
|
}
|
|
97
122
|
function secondsText(ms) {
|
|
98
123
|
const whole = Math.max(0, Math.round(ms / 1e3));
|
|
@@ -112,24 +137,36 @@ var CSS = `
|
|
|
112
137
|
.drc-dot { width: 6px; height: 6px; flex: 0 0 auto; border-radius: 50%; background: var(--dsw-alias-label-dimmed, #9aa0a6); }
|
|
113
138
|
.drc-dot[data-tone="wait"] { background: var(--dsw-alias-state-warn-primary, #faad14); }
|
|
114
139
|
.drc-dot[data-tone="on"] { background: var(--dsw-alias-state-success-primary, #52c41a); }
|
|
140
|
+
.drc-dot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #ff4d4f); }
|
|
115
141
|
.drc-panel { position: absolute; bottom: calc(100% + 8px); right: 0; z-index: 30; width: 240px;
|
|
116
|
-
padding: 12px
|
|
142
|
+
padding: 10px 12px; border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1)); border-radius: 12px;
|
|
117
143
|
background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-1, #ffffff));
|
|
118
144
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14), 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
119
145
|
color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 18px; }
|
|
120
|
-
/* \
|
|
121
|
-
.drc-
|
|
122
|
-
|
|
123
|
-
.drc-
|
|
146
|
+
/* \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 */
|
|
147
|
+
.drc-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
148
|
+
.drc-head-label { font-weight: 600; }
|
|
149
|
+
.drc-head-label[data-tone="off"] { color: var(--dsw-alias-label-secondary, #6b7280); }
|
|
150
|
+
.drc-head-label[data-tone="wait"] { color: var(--dsw-alias-state-warn-primary, #faad14); }
|
|
151
|
+
.drc-head-label[data-tone="on"] { color: var(--dsw-alias-state-success-primary, #52c41a); }
|
|
152
|
+
.drc-head-label[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #ff4d4f); }
|
|
153
|
+
.drc-actions { display: flex; gap: 6px; flex: 0 0 auto; }
|
|
154
|
+
.drc-btn { padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1));
|
|
155
|
+
border-radius: 8px; background: transparent; color: inherit; font: inherit; font-size: 12px; cursor: pointer;
|
|
156
|
+
white-space: nowrap; }
|
|
157
|
+
.drc-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.04)); }
|
|
158
|
+
/* \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
|
|
159
|
+
\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 */
|
|
160
|
+
.drc-info { display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
|
|
161
|
+
white-space: nowrap; margin-top: 8px; padding-top: 6px;
|
|
162
|
+
border-top: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.08)); }
|
|
163
|
+
.drc-info + .drc-info { margin-top: 2px; padding-top: 0; border-top: 0; }
|
|
124
164
|
.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:
|
|
165
|
+
.drc-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
166
|
+
.drc-qr { display: block; width: 200px; height: 200px; margin: 8px auto 0; image-rendering: pixelated; }
|
|
127
167
|
.drc-code { margin: 8px 0 0; text-align: center; font-weight: 600; font-size: 14px; letter-spacing: 2px; }
|
|
128
168
|
.drc-note { margin: 4px 0 0; text-align: center; color: var(--dsw-alias-label-tertiary); }
|
|
129
169
|
.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
170
|
`;
|
|
134
171
|
function isSlots(value) {
|
|
135
172
|
if (!value || typeof value !== "object") return false;
|
|
@@ -168,7 +205,11 @@ function warn(message, error) {
|
|
|
168
205
|
}
|
|
169
206
|
function ensureStyle(doc) {
|
|
170
207
|
try {
|
|
171
|
-
|
|
208
|
+
const existing = doc.querySelector(`style[data-plugin-css="${STYLE_ID}"]`);
|
|
209
|
+
if (existing) {
|
|
210
|
+
if (existing.textContent !== CSS) existing.textContent = CSS;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
172
213
|
const element = doc.createElement("style");
|
|
173
214
|
element.setAttribute("data-plugin-css", STYLE_ID);
|
|
174
215
|
element.textContent = CSS;
|
|
@@ -200,7 +241,7 @@ function buildPill(root, deps) {
|
|
|
200
241
|
let expiresInMs = 0;
|
|
201
242
|
let lastStatus;
|
|
202
243
|
let disposed = false;
|
|
203
|
-
const
|
|
244
|
+
const pairingNow = () => pairingCount(lastStatus);
|
|
204
245
|
const write = () => {
|
|
205
246
|
const next = pillLabel(lastStatus);
|
|
206
247
|
label.textContent = next.text;
|
|
@@ -222,59 +263,68 @@ function buildPill(root, deps) {
|
|
|
222
263
|
node.addEventListener("click", onClick);
|
|
223
264
|
return node;
|
|
224
265
|
};
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
266
|
+
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";
|
|
267
|
+
const header = () => {
|
|
268
|
+
const head = doc.createElement("div");
|
|
269
|
+
head.className = "drc-head";
|
|
270
|
+
const state = pillLabel(lastStatus);
|
|
271
|
+
const stateText = text("span", "drc-head-label", state.text);
|
|
272
|
+
stateText.setAttribute("data-tone", state.tone);
|
|
273
|
+
head.appendChild(stateText);
|
|
274
|
+
const actions = doc.createElement("div");
|
|
275
|
+
actions.className = "drc-actions";
|
|
276
|
+
const action = pairingNow() > 0 ? { label: "\u9000\u51FA\u914D\u5BF9", run: requestUnpair } : { label: "\u5237\u65B0", run: requestPairing };
|
|
277
|
+
actions.appendChild(buttonOf(action.label, () => void action.run()));
|
|
278
|
+
head.appendChild(actions);
|
|
279
|
+
return head;
|
|
280
|
+
};
|
|
281
|
+
const offlineNote = () => {
|
|
282
|
+
if (!lastStatus) return null;
|
|
283
|
+
if (pillLabel(lastStatus).text !== "\u624B\u673A\u79BB\u7EBF") return null;
|
|
284
|
+
return text("p", "drc-note", "\u914D\u5BF9\u8FD8\u5728\uFF1A\u5C0F\u7A0B\u5E8F\u56DE\u524D\u53F0\u4F1A\u81EA\u52A8\u91CD\u8FDE\uFF0C\u4E0D\u7528\u91CD\u65B0\u626B\u7801\u3002");
|
|
285
|
+
};
|
|
286
|
+
const infoRows = () => {
|
|
287
|
+
if (!lastStatus) return [];
|
|
288
|
+
const rows = [
|
|
289
|
+
// 排第一:它是唯一一条"看完要做点什么"的,其余三条都是"知道就行"。
|
|
290
|
+
["\u5F85\u5904\u7406", waitingRow(lastStatus)],
|
|
291
|
+
["\u4E2D\u7EE7", relayRow(lastStatus)],
|
|
292
|
+
["\u72B6\u6001", relayStateRow(lastStatus)],
|
|
293
|
+
["\u7248\u672C", versionRow(lastStatus)]
|
|
294
|
+
];
|
|
295
|
+
return rows.filter(([, value]) => value !== "").map(([key, value]) => {
|
|
230
296
|
const row = doc.createElement("div");
|
|
231
|
-
row.className = "drc-
|
|
297
|
+
row.className = "drc-info";
|
|
232
298
|
row.appendChild(text("span", "drc-key", key));
|
|
233
299
|
row.appendChild(text("span", "drc-value", value));
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
return box;
|
|
300
|
+
return row;
|
|
301
|
+
});
|
|
237
302
|
};
|
|
238
303
|
const paint = () => {
|
|
239
304
|
if (!panel) return;
|
|
240
305
|
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
|
-
}
|
|
306
|
+
panel.appendChild(header());
|
|
250
307
|
if (view.kind === "loading") {
|
|
251
308
|
panel.appendChild(text("p", "drc-note", "\u6B63\u5728\u751F\u6210\u914D\u5BF9\u7801\u2026"));
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
if (view.kind === "qr") {
|
|
309
|
+
} else if (view.kind === "qr") {
|
|
255
310
|
const image = doc.createElement("img");
|
|
256
311
|
image.className = "drc-qr";
|
|
257
312
|
image.setAttribute("alt", "\u914D\u5BF9\u4E8C\u7EF4\u7801");
|
|
258
313
|
image.src = view.imageSrc;
|
|
259
314
|
panel.appendChild(image);
|
|
260
315
|
panel.appendChild(text("p", "drc-code", view.token));
|
|
261
|
-
panel.appendChild(text("p", "drc-note",
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
316
|
+
panel.appendChild(text("p", "drc-note", qrNote()));
|
|
317
|
+
} else if (view.kind === "unavailable" || view.kind === "failed") {
|
|
318
|
+
const note2 = view.kind === "unavailable" ? text("p", "drc-note", view.reason) : (() => {
|
|
319
|
+
const node = text("p", "drc-note", `\u914D\u5BF9\u8BF7\u6C42\u6CA1\u6210\u529F\uFF1A${view.detail}`);
|
|
320
|
+
node.setAttribute("data-kind", "failed");
|
|
321
|
+
return node;
|
|
322
|
+
})();
|
|
323
|
+
panel.appendChild(note2);
|
|
267
324
|
}
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
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);
|
|
325
|
+
for (const row of infoRows()) panel.appendChild(row);
|
|
326
|
+
const note = offlineNote();
|
|
327
|
+
if (note) panel.appendChild(note);
|
|
278
328
|
};
|
|
279
329
|
const closePanel = () => {
|
|
280
330
|
if (countTimer !== void 0) {
|
|
@@ -313,24 +363,49 @@ function buildPill(root, deps) {
|
|
|
313
363
|
if (countTimer === void 0) {
|
|
314
364
|
countTimer = setInterval(() => {
|
|
315
365
|
expiresInMs -= 1e3;
|
|
316
|
-
if (expiresInMs <= 0)
|
|
317
|
-
|
|
366
|
+
if (expiresInMs <= 0) {
|
|
367
|
+
clearInterval(countTimer);
|
|
368
|
+
countTimer = void 0;
|
|
369
|
+
}
|
|
370
|
+
paint();
|
|
318
371
|
}, 1e3);
|
|
319
372
|
}
|
|
320
373
|
}
|
|
321
374
|
paint();
|
|
322
375
|
}
|
|
376
|
+
async function requestUnpair() {
|
|
377
|
+
if (disposed || !panel) return;
|
|
378
|
+
try {
|
|
379
|
+
await deps.fetchImpl(UNPAIR_ROUTE, {
|
|
380
|
+
method: "POST",
|
|
381
|
+
headers: { "x-drc-pair": "1" },
|
|
382
|
+
credentials: "same-origin"
|
|
383
|
+
});
|
|
384
|
+
} catch (error) {
|
|
385
|
+
warn("\u9000\u51FA\u914D\u5BF9\u8BF7\u6C42\u5931\u8D25\uFF08\u9762\u677F\u4F1A\u9760\u4E0B\u4E00\u6B21\u8F6E\u8BE2\u5BF9\u8D26\uFF09", error);
|
|
386
|
+
}
|
|
387
|
+
if (disposed || !panel) return;
|
|
388
|
+
if (lastStatus) lastStatus = { ...lastStatus, paired: 0, pairings: 0 };
|
|
389
|
+
write();
|
|
390
|
+
view = { kind: "loading" };
|
|
391
|
+
paint();
|
|
392
|
+
void requestPairing();
|
|
393
|
+
}
|
|
323
394
|
const openPanel = () => {
|
|
324
395
|
if (panel || disposed) return;
|
|
325
396
|
panel = doc.createElement("div");
|
|
326
397
|
panel.className = "drc-panel";
|
|
327
398
|
panel.setAttribute("role", "dialog");
|
|
328
|
-
panel.setAttribute("aria-label", "dsh-remote-control \
|
|
399
|
+
panel.setAttribute("aria-label", "dsh-remote-control \u914D\u5BF9");
|
|
329
400
|
panel.tabIndex = -1;
|
|
330
401
|
root.appendChild(panel);
|
|
331
402
|
button.setAttribute("aria-expanded", "true");
|
|
332
|
-
|
|
333
|
-
|
|
403
|
+
if (pairingNow() > 0) {
|
|
404
|
+
view = { kind: "info" };
|
|
405
|
+
paint();
|
|
406
|
+
} else {
|
|
407
|
+
void requestPairing();
|
|
408
|
+
}
|
|
334
409
|
try {
|
|
335
410
|
panel.focus();
|
|
336
411
|
} catch {
|
|
@@ -367,6 +442,14 @@ function buildPill(root, deps) {
|
|
|
367
442
|
const body = await response.json?.();
|
|
368
443
|
if (!body || typeof body !== "object") return;
|
|
369
444
|
lastStatus = body;
|
|
445
|
+
if (view.kind === "qr" && pairingNow() > 0) {
|
|
446
|
+
view = { kind: "info" };
|
|
447
|
+
if (countTimer !== void 0) {
|
|
448
|
+
clearInterval(countTimer);
|
|
449
|
+
countTimer = void 0;
|
|
450
|
+
}
|
|
451
|
+
if (panel) paint();
|
|
452
|
+
}
|
|
370
453
|
write();
|
|
371
454
|
} catch (error) {
|
|
372
455
|
deps.warn?.("status poll failed");
|