dsh-remote-control 1.1.0 → 1.2.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/cordis.patch.yml CHANGED
@@ -19,9 +19,7 @@
19
19
  enabled: true
20
20
  idleReleaseSec: 300
21
21
  keepDisplay: false
22
- # 右栏自动弹码(原独立条目 dsh-remote-control-presentation,2026-10-03 折进来)。
23
- # imageFile 故意不写在这里:默认值取 homedir(见 src/shell/config.ts),
24
- # 写进 YAML 就把某个人的家目录焊进分发包了。
25
- sidebarQr:
22
+ # 状态栏那颗 pill:配对的**唯一**入口(`/drc pair` 与右栏自动弹码都在 2026-10-03 删了)。
23
+ # 关掉它这台主机就没有配对入口了——所以关掉会在 status.json 的 problems 里留一条 warn:pill。
24
+ pill:
26
25
  enabled: true
27
- refreshMs: 2000
@@ -31,112 +31,450 @@ __export(main_exports, {
31
31
  name: () => name
32
32
  });
33
33
  module.exports = __toCommonJS(main_exports);
34
- var ROUTE = "/plugins/dsh-remote-control/pairing";
34
+
35
+ // client/src/pill.ts
36
+ var STATUS_ROUTE = "/plugins/dsh-remote-control/status";
37
+ var NEW_ROUTE = "/plugins/dsh-remote-control/pairing/new";
38
+ var IMAGE_ROUTE = "/plugins/dsh-remote-control/pairing.png";
39
+ var SLOT_NAME = "conversation.composer.dock";
40
+ var PILL_ID = "dsh-remote-control";
41
+ var PILL_ORDER = 20;
42
+ var STYLE_ID = "dsh-remote-control/pill.css";
35
43
  var POLL_MS = 2e3;
36
- var timer;
37
- var stopped = true;
38
- var inFlight = false;
39
- var lastEpoch = null;
40
- var lastSessionId = null;
41
- var reported = /* @__PURE__ */ new Set();
42
- function report(message, error) {
43
- if (reported.has(message)) return;
44
- reported.add(message);
45
- try {
46
- const detail = error instanceof Error ? error.stack ?? error.message : error === void 0 ? "" : String(error);
47
- console.error(`[dsh-remote-control sidebar] ${message} ${detail}`);
48
- } catch {
44
+ function panelViewFor(answer, httpStatus) {
45
+ if (!answer || typeof answer !== "object") return { kind: "failed", detail: `HTTP ${httpStatus}` };
46
+ if (answer.state === "ready") {
47
+ const token = typeof answer.token === "string" ? answer.token : "";
48
+ const epoch = typeof answer.epoch === "string" ? answer.epoch : "";
49
+ const expiresInMs = typeof answer.expiresInMs === "number" ? answer.expiresInMs : 0;
50
+ if (token === "" || expiresInMs <= 0) return { kind: "failed", detail: "\u53D1\u7801\u56DE\u7B54\u91CC\u7684\u7801\u4E0D\u5B8C\u6574" };
51
+ return { kind: "qr", token, expiresInMs, imageSrc: `${IMAGE_ROUTE}?e=${encodeURIComponent(epoch)}` };
52
+ }
53
+ if (answer.state === "unavailable") {
54
+ return {
55
+ kind: "unavailable",
56
+ reason: answer.reason === "relay-offline" ? "\u4E2D\u7EE7\u8FD8\u6CA1\u8FDE\u4E0A\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u5BF9\u3002" : "\u73B0\u5728\u53D1\u4E0D\u51FA\u914D\u5BF9\u7801\u3002"
57
+ };
49
58
  }
59
+ return {
60
+ kind: "failed",
61
+ // 把宿主说的那道守卫一起印出来:这条路由在浏览器里点,屏幕上的这句话就是唯一的现场。
62
+ detail: typeof answer.error === "string" ? typeof answer.guard === "string" ? `${answer.error}\uFF08${answer.guard}\uFF09` : answer.error : `HTTP ${httpStatus}`
63
+ };
64
+ }
65
+ function pillLabel(status) {
66
+ const relay = status?.relay;
67
+ if (relay === "idle") return { text: "\u8FDC\u7A0B\u672A\u542F\u52A8", tone: "off" };
68
+ if (relay === "offline") return { text: "\u8FDC\u7A0B\u672A\u8FDE\u63A5", tone: "off" };
69
+ if (relay === "connecting") return { text: "\u8FDE\u63A5\u4E2D", tone: "wait" };
70
+ if (relay !== "online") return { text: "\u8FDC\u7A0B\u63A7\u5236", tone: "off" };
71
+ const paired = typeof status?.paired === "number" ? status.paired : 0;
72
+ return paired > 0 ? { text: "\u5DF2\u914D\u5BF9", tone: "on" } : { text: "\u672A\u914D\u5BF9", tone: "off" };
73
+ }
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
+ function relayAddress(url) {
81
+ if (typeof url !== "string" || url.trim() === "") return "";
82
+ const withoutScheme = url.trim().replace(/^[a-z][a-z0-9+.-]*:\/\//i, "");
83
+ const authority = /^[^/?]*/.exec(withoutScheme)?.[0] ?? "";
84
+ return authority.slice(0, 40);
85
+ }
86
+ function infoRows(status) {
87
+ const rows = [];
88
+ const label = typeof status?.hostLabel === "string" ? status.hostLabel.trim() : "";
89
+ if (label !== "") rows.push(["\u672C\u673A", label]);
90
+ const server = relayAddress(status?.serverUrl);
91
+ if (server !== "") rows.push(["\u4E2D\u7EE7", server]);
92
+ const relay = typeof status?.relay === "string" ? status.relay : "";
93
+ rows.push(["\u72B6\u6001", RELAY_TEXT[relay] ?? "\u672A\u77E5"]);
94
+ rows.push(["\u5DF2\u914D\u5BF9", `${typeof status?.paired === "number" ? status.paired : 0} \u53F0`]);
95
+ return rows;
50
96
  }
51
- function safeSessionId(held) {
52
- const id = held && typeof held === "object" ? held.sessionId : void 0;
53
- return typeof id === "string" && id !== "" ? id : null;
97
+ function secondsText(ms) {
98
+ const whole = Math.max(0, Math.round(ms / 1e3));
99
+ if (whole < 60) return `${whole} \u79D2`;
100
+ return `${Math.floor(whole / 60)} \u5206 ${whole % 60} \u79D2`;
54
101
  }
55
- async function poll(ctx) {
56
- if (stopped || inFlight) return;
102
+ var CSS = `
103
+ .drc-pill { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 2px 8px;
104
+ border: 0; border-radius: 999px; background: transparent; color: inherit; font: inherit;
105
+ font-size: 12px; line-height: 18px; cursor: pointer;
106
+ /* \u5BBF\u4E3B\u90A3\u4E00\u6392\u6324\u4E0D\u4E0B\u65F6\u4F1A\u81EA\u5DF1\u622A\u65AD\uFF08\u300C6 \u8F6E \u2026\u300D\u90A3\u79CD\uFF09\uFF0C\u6211\u4EEC\u4E5F\u8DDF\u8FD9\u4E2A\u5F62\u72B6\u8D70\uFF1A
107
+ \u4E00\u884C\u5199\u5B8C\uFF0C\u5199\u4E0D\u4E0B\u5C31\u7701\u7565\u53F7\u3002**\u6CA1\u6709\u8FD9\u4E24\u6761**\u7684\u8BDD\uFF0Cflex \u9879\u4F1A\u88AB\u538B\u5230\u300C\u4E2D\u6587\u6700\u5C0F\u5185\u5BB9\u5BBD\u5EA6
108
+ = \u4E00\u4E2A\u5B57\u300D\uFF0C\u4E8E\u662F\u300C\u5DF2\u8FDE 1 \u53F0\u300D\u7AD6\u7740\u6392\u6210\u56DB\u884C\uFF082026-10-03 \u771F\u5C4F\u5E55\u622A\u56FE\uFF09\u3002 */
109
+ flex: 0 1 auto; white-space: nowrap; }
110
+ .drc-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
111
+ .drc-pill:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.04)); }
112
+ .drc-dot { width: 6px; height: 6px; flex: 0 0 auto; border-radius: 50%; background: var(--dsw-alias-label-dimmed, #9aa0a6); }
113
+ .drc-dot[data-tone="wait"] { background: var(--dsw-alias-state-warn-primary, #faad14); }
114
+ .drc-dot[data-tone="on"] { background: var(--dsw-alias-state-success-primary, #52c41a); }
115
+ .drc-panel { position: absolute; bottom: calc(100% + 8px); right: 0; z-index: 30; width: 240px;
116
+ padding: 12px 14px; border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.1)); border-radius: 12px;
117
+ background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-1, #ffffff));
118
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14), 0 2px 6px rgba(0, 0, 0, 0.06);
119
+ color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 18px; }
120
+ /* \u8FDE\u63A5\u4FE1\u606F\u90A3\u4E00\u680F\uFF1A\u952E\u5DE6\u503C\u53F3\uFF0C\u503C\u88AB\u622A\u65AD\u65F6\u7701\u7565\u53F7\u2014\u2014\u548C\u90A3\u9897 pill \u540C\u4E00\u5957"\u6324\u4E0D\u4E0B\u5C31\u622A\u6587\u5B57\u4E0D\u6491\u5BB9\u5668"\u3002 */
121
+ .drc-info { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px;
122
+ padding-bottom: 6px; border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.08)); }
123
+ .drc-row { display: flex; align-items: baseline; gap: 8px; white-space: nowrap; }
124
+ .drc-key { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); }
125
+ .drc-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; text-align: right; }
126
+ .drc-qr { display: block; width: 200px; height: 200px; margin: 0 auto; image-rendering: pixelated; }
127
+ .drc-code { margin: 8px 0 0; text-align: center; font-weight: 600; font-size: 14px; letter-spacing: 2px; }
128
+ .drc-note { margin: 4px 0 0; text-align: center; color: var(--dsw-alias-label-tertiary); }
129
+ .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
+ `;
134
+ function isSlots(value) {
135
+ if (!value || typeof value !== "object") return false;
136
+ const record = value;
137
+ return typeof record.inject === "function" && typeof record.register === "function";
138
+ }
139
+ function pickSlots(source) {
140
+ if (isSlots(source)) return source;
141
+ const asGettable = source;
142
+ if (asGettable && typeof asGettable.get === "function") {
143
+ try {
144
+ const got = asGettable.get("slots", true);
145
+ if (isSlots(got)) return got;
146
+ } catch {
147
+ }
148
+ }
149
+ return void 0;
150
+ }
151
+ function findCreateElement() {
57
152
  try {
58
- if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
153
+ if (typeof require !== "function") return void 0;
154
+ const moduleId = "react";
155
+ const react = require(moduleId);
156
+ const create = react?.createElement ?? react?.default?.createElement;
157
+ return typeof create === "function" ? create : void 0;
59
158
  } catch {
159
+ return void 0;
60
160
  }
61
- let sessionId;
161
+ }
162
+ function warn(message, error) {
62
163
  try {
63
- const held = ctx.sidebarRight.require();
64
- const id = safeSessionId(held);
65
- if (id === null) return;
66
- sessionId = id;
164
+ const detail = error instanceof Error ? error.message : error === void 0 ? "" : String(error);
165
+ console.warn(`[dsh-remote-control pill] ${message} ${detail}`);
67
166
  } catch {
68
- return;
69
- }
70
- if (sessionId !== lastSessionId) {
71
- lastSessionId = sessionId;
72
- lastEpoch = null;
73
167
  }
74
- inFlight = true;
168
+ }
169
+ function ensureStyle(doc) {
75
170
  try {
76
- const response = await fetch(`${ROUTE}?session=${encodeURIComponent(sessionId)}`, {
77
- headers: { accept: "application/json" },
78
- credentials: "same-origin"
79
- });
80
- if (!response.ok) {
81
- if (response.status === 403) report("route refused the request (403): \u73AF\u56DE\u5B88\u536B\u5224\u5B9A\u975E\u540C\u6E90");
82
- else if (response.status !== 404) report(`route answered ${response.status}`);
171
+ if (doc.querySelector(`style[data-plugin-css="${STYLE_ID}"]`)) return;
172
+ const element = doc.createElement("style");
173
+ element.setAttribute("data-plugin-css", STYLE_ID);
174
+ element.textContent = CSS;
175
+ doc.head.appendChild(element);
176
+ } catch (error) {
177
+ warn("\u6837\u5F0F\u6CE8\u5165\u5931\u8D25\uFF08pill \u4ECD\u53EF\u7528\uFF0C\u53EA\u662F\u6CA1\u914D\u8272\uFF09", error);
178
+ }
179
+ }
180
+ function buildPill(root, deps) {
181
+ const doc = root.ownerDocument;
182
+ ensureStyle(doc);
183
+ const button = doc.createElement("button");
184
+ button.type = "button";
185
+ button.className = "drc-pill";
186
+ button.setAttribute("aria-haspopup", "dialog");
187
+ button.setAttribute("aria-expanded", "false");
188
+ const dot = doc.createElement("span");
189
+ dot.className = "drc-dot";
190
+ dot.setAttribute("aria-hidden", "true");
191
+ const label = doc.createElement("span");
192
+ label.className = "drc-label";
193
+ button.appendChild(dot);
194
+ button.appendChild(label);
195
+ root.appendChild(button);
196
+ let panel;
197
+ let statusTimer;
198
+ let countTimer;
199
+ let view = { kind: "info" };
200
+ let expiresInMs = 0;
201
+ let lastStatus;
202
+ let disposed = false;
203
+ const pairedNow = () => typeof lastStatus?.paired === "number" ? lastStatus.paired : 0;
204
+ const write = () => {
205
+ const next = pillLabel(lastStatus);
206
+ label.textContent = next.text;
207
+ dot.setAttribute("data-tone", next.tone);
208
+ button.setAttribute("title", `dsh-remote-control\uFF1A${next.text}`);
209
+ button.setAttribute("aria-label", next.text);
210
+ };
211
+ const text = (tag, className, content) => {
212
+ const node = doc.createElement(tag);
213
+ node.className = className;
214
+ node.textContent = content;
215
+ return node;
216
+ };
217
+ const buttonOf = (content, onClick) => {
218
+ const node = doc.createElement("button");
219
+ node.type = "button";
220
+ node.className = "drc-btn";
221
+ node.textContent = content;
222
+ node.addEventListener("click", onClick);
223
+ return node;
224
+ };
225
+ const infoBlock = () => {
226
+ if (!lastStatus) return void 0;
227
+ const box = doc.createElement("div");
228
+ box.className = "drc-info";
229
+ for (const [key, value] of infoRows(lastStatus)) {
230
+ const row = doc.createElement("div");
231
+ row.className = "drc-row";
232
+ row.appendChild(text("span", "drc-key", key));
233
+ row.appendChild(text("span", "drc-value", value));
234
+ box.appendChild(row);
235
+ }
236
+ return box;
237
+ };
238
+ const paint = () => {
239
+ if (!panel) return;
240
+ panel.replaceChildren();
241
+ const info = infoBlock();
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
+ }
250
+ if (view.kind === "loading") {
251
+ panel.appendChild(text("p", "drc-note", "\u6B63\u5728\u751F\u6210\u914D\u5BF9\u7801\u2026"));
83
252
  return;
84
253
  }
85
- const body = await response.json();
86
- if (body.state !== "ready") return;
87
- const epoch = typeof body.epoch === "string" ? body.epoch : "";
88
- const address = typeof body.address === "string" ? body.address : "";
89
- const expiresAt = typeof body.expiresAt === "number" ? body.expiresAt : 0;
90
- if (epoch === "" || address === "" || epoch === lastEpoch) return;
91
- if (expiresAt <= Date.now()) return;
254
+ if (view.kind === "qr") {
255
+ const image = doc.createElement("img");
256
+ image.className = "drc-qr";
257
+ image.setAttribute("alt", "\u914D\u5BF9\u4E8C\u7EF4\u7801");
258
+ image.src = view.imageSrc;
259
+ panel.appendChild(image);
260
+ panel.appendChild(text("p", "drc-code", view.token));
261
+ panel.appendChild(text("p", "drc-note", `\u626B\u7801\u914D\u5BF9 \xB7 ${secondsText(expiresInMs)}\u540E\u8FC7\u671F`));
262
+ const actions2 = doc.createElement("div");
263
+ actions2.className = "drc-actions";
264
+ actions2.appendChild(buttonOf("\u6362\u4E00\u5F20", () => void requestPairing()));
265
+ panel.appendChild(actions2);
266
+ return;
267
+ }
268
+ const note = view.kind === "unavailable" ? text("p", "drc-note", view.reason) : (() => {
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);
278
+ };
279
+ const closePanel = () => {
280
+ if (countTimer !== void 0) {
281
+ clearInterval(countTimer);
282
+ countTimer = void 0;
283
+ }
284
+ if (panel) {
285
+ panel.remove();
286
+ panel = void 0;
287
+ }
288
+ button.setAttribute("aria-expanded", "false");
289
+ };
290
+ async function requestPairing() {
291
+ if (disposed || !panel) return;
292
+ view = { kind: "loading" };
293
+ paint();
294
+ let answer;
295
+ let httpStatus = 0;
92
296
  try {
93
- ctx.sidebarRight.openResource(address);
94
- lastEpoch = epoch;
297
+ const response = await deps.fetchImpl(NEW_ROUTE, {
298
+ method: "POST",
299
+ headers: { "x-drc-pair": "1" },
300
+ credentials: "same-origin"
301
+ });
302
+ httpStatus = typeof response?.status === "number" ? response.status : 0;
303
+ answer = await response?.json?.();
95
304
  } catch (error) {
96
- report("openResource failed", error);
305
+ view = { kind: "failed", detail: error instanceof Error ? error.message : String(error) };
306
+ paint();
307
+ return;
308
+ }
309
+ if (disposed || !panel) return;
310
+ view = panelViewFor(answer, httpStatus);
311
+ if (view.kind === "qr") {
312
+ expiresInMs = view.expiresInMs;
313
+ if (countTimer === void 0) {
314
+ countTimer = setInterval(() => {
315
+ expiresInMs -= 1e3;
316
+ if (expiresInMs <= 0) void requestPairing();
317
+ else paint();
318
+ }, 1e3);
319
+ }
320
+ }
321
+ paint();
322
+ }
323
+ const openPanel = () => {
324
+ if (panel || disposed) return;
325
+ panel = doc.createElement("div");
326
+ panel.className = "drc-panel";
327
+ panel.setAttribute("role", "dialog");
328
+ panel.setAttribute("aria-label", "dsh-remote-control \u8FDE\u63A5\u4FE1\u606F");
329
+ panel.tabIndex = -1;
330
+ root.appendChild(panel);
331
+ button.setAttribute("aria-expanded", "true");
332
+ view = { kind: "info" };
333
+ paint();
334
+ try {
335
+ panel.focus();
336
+ } catch {
337
+ }
338
+ };
339
+ const inside = (candidate) => {
340
+ if (!candidate || typeof candidate !== "object") return false;
341
+ try {
342
+ return root.contains(candidate) || (panel?.contains(candidate) ?? false);
343
+ } catch {
344
+ return false;
345
+ }
346
+ };
347
+ const onPointerDown = (event) => {
348
+ if (!panel) return;
349
+ if (inside(event?.target)) return;
350
+ closePanel();
351
+ };
352
+ const onKeyDown = (event) => {
353
+ if (event.key === "Escape") closePanel();
354
+ };
355
+ async function pollStatus() {
356
+ if (disposed) return;
357
+ try {
358
+ if (doc.visibilityState === "hidden") return;
359
+ } catch {
360
+ }
361
+ try {
362
+ const response = await deps.fetchImpl(`${STATUS_ROUTE}?t=${Date.now()}`, {
363
+ headers: { accept: "application/json" },
364
+ credentials: "same-origin"
365
+ });
366
+ if (!response || response.status !== 200) return;
367
+ const body = await response.json?.();
368
+ if (!body || typeof body !== "object") return;
369
+ lastStatus = body;
370
+ write();
371
+ } catch (error) {
372
+ deps.warn?.("status poll failed");
373
+ warn("status poll failed\uFF08pill \u4F1A\u505C\u5728\u9ED8\u8BA4\u6587\u6848\u4E0A\uFF09", error);
97
374
  }
98
- } catch (error) {
99
- report("poll failed", error);
100
- } finally {
101
- inFlight = false;
102
375
  }
376
+ button.addEventListener("click", () => {
377
+ if (panel) closePanel();
378
+ else openPanel();
379
+ });
380
+ doc.addEventListener("pointerdown", onPointerDown, true);
381
+ doc.addEventListener("keydown", onKeyDown);
382
+ write();
383
+ void pollStatus();
384
+ statusTimer = setInterval(() => void pollStatus(), POLL_MS);
385
+ return () => {
386
+ if (disposed) return;
387
+ disposed = true;
388
+ if (statusTimer !== void 0) clearInterval(statusTimer);
389
+ closePanel();
390
+ doc.removeEventListener("pointerdown", onPointerDown, true);
391
+ doc.removeEventListener("keydown", onKeyDown);
392
+ button.remove();
393
+ };
103
394
  }
104
- function stop() {
105
- stopped = true;
106
- if (timer !== void 0) {
107
- clearInterval(timer);
108
- timer = void 0;
395
+ function mountPill(ctx, createElement) {
396
+ if (!createElement) {
397
+ warn("\u88C5\u8F7D\u5668\u7ED9\u4E0D\u51FA react\uFF1A\u914D\u5BF9\u90A3\u9897 pill \u4E0D\u4F1A\u51FA\u73B0");
398
+ return false;
109
399
  }
400
+ let mounted = false;
401
+ const mount = (slots) => {
402
+ if (mounted) return;
403
+ let cleanup;
404
+ const attach = (node) => {
405
+ const element = node;
406
+ if (element && typeof element.appendChild === "function" && element.ownerDocument) {
407
+ if (cleanup) return;
408
+ element.style.position = "relative";
409
+ cleanup = buildPill(element, {
410
+ fetchImpl: (input, init) => globalThis.fetch(input, init)
411
+ });
412
+ } else if (cleanup) {
413
+ cleanup();
414
+ cleanup = void 0;
415
+ }
416
+ };
417
+ const component = () => createElement("span", { ref: attach });
418
+ mounted = true;
419
+ try {
420
+ slots.inject(SLOT_NAME, () => {
421
+ try {
422
+ slots.register({ name: SLOT_NAME, id: PILL_ID, order: PILL_ORDER, inject: () => ({}) }, component);
423
+ } catch (error) {
424
+ warn("\u69FD\u4F4D\u6CE8\u518C\u5931\u8D25\uFF08\u90A3\u9897 pill \u4E0D\u51FA\u73B0\uFF09", error);
425
+ }
426
+ });
427
+ } catch (error) {
428
+ mounted = false;
429
+ warn("\u69FD\u4F4D\u6CE8\u5165\u5931\u8D25\uFF08\u90A3\u9897 pill \u4E0D\u51FA\u73B0\uFF09", error);
430
+ return;
431
+ }
432
+ try {
433
+ ctx.effect?.(() => () => {
434
+ if (cleanup) {
435
+ cleanup();
436
+ cleanup = void 0;
437
+ }
438
+ });
439
+ } catch {
440
+ }
441
+ };
442
+ let early;
110
443
  try {
111
- if (typeof document !== "undefined") document.removeEventListener("visibilitychange", onVisibility);
444
+ early = pickSlots(ctx.slots) ?? pickSlots(ctx.get?.("slots", true));
112
445
  } catch {
113
446
  }
447
+ if (early) mount(early);
448
+ try {
449
+ ctx.inject?.(["slots"], ((...args) => {
450
+ for (const candidate of [...args, ctx]) {
451
+ const found = pickSlots(candidate);
452
+ if (found) {
453
+ mount(found);
454
+ return;
455
+ }
456
+ }
457
+ }));
458
+ } catch (error) {
459
+ warn("ctx.inject \u53E3\u4E0D\u53EF\u7528\uFF1Aslots \u518D\u665A\u5230\u4E5F\u6CA1\u4EBA\u8865\u6302", error);
460
+ }
461
+ if (!mounted) {
462
+ warn("apply \u65F6\u6CA1\u63A2\u5230 slots\uFF08\u5DF2\u6302\u6CE8\u5165\u56DE\u8C03\u7B49\u5B83\u665A\u5230\uFF09\uFF1Bpill \u4E00\u76F4\u4E0D\u51FA\u73B0\u5C31\u662F\u8FD9\u4EE3\u5BBF\u4E3B\u6CA1\u8FD9\u4E2A\u670D\u52A1");
463
+ }
464
+ return mounted;
114
465
  }
115
- var current;
116
- function onVisibility() {
117
- if (current && !stopped) void poll(current);
118
- }
466
+
467
+ // client/src/main.ts
119
468
  var name = "dsh-remote-control";
120
- var inject = ["sidebarRight"];
469
+ var inject = ["slots"];
121
470
  function apply(ctx) {
122
471
  try {
123
- current = ctx;
124
- stopped = false;
125
- reported.clear();
472
+ mountPill(ctx, findCreateElement());
473
+ } catch (error) {
126
474
  try {
127
- if (typeof document !== "undefined") document.addEventListener("visibilitychange", onVisibility);
475
+ console.error("[dsh-remote-control] pill \u6302\u8F7D\u629B\u9519\uFF08\u5DF2\u541E\u6389\uFF09\uFF1A", error);
128
476
  } catch {
129
477
  }
130
- timer = setInterval(() => void poll(ctx), POLL_MS);
131
- void poll(ctx);
132
- try {
133
- ctx.effect?.(() => stop);
134
- } catch (error) {
135
- report("ctx.effect unavailable; poll timer will outlive this fiber", error);
136
- }
137
- } catch (error) {
138
- report("apply failed", error);
139
- stop();
140
478
  }
141
479
  }
142
480