@mrrisega/dsh-remote 0.6.0-beta.9 → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  // dsh-remote-web 浏览器半回归(2026-09 前名 dsh-remote-ui):面板入口迁移进「设置」页 settings.section 官方扩展点。
2
- // 覆盖:栏目注册(id/order/label)、侧边栏入口与浮动面板移除、账号区无「切换账号」、
3
- // 「关于 dsh-remote」说明卡片、退出登录/切换连接账号清理反馈线程凭据、首次安装红点引导。
2
+ // 覆盖:栏目注册(id/order/label)、侧边栏入口与浮动面板移除(不再向官方侧栏注入任何按钮/点击机器)、
3
+ // 账号区无「切换账号」、「关于 dsh-remote」说明卡片、退出登录/切换连接账号清理反馈线程凭据、首次安装红点引导。
4
4
  import assert from "node:assert/strict";
5
5
  import { readFileSync } from "node:fs";
6
6
  import test from "node:test";
@@ -98,7 +98,6 @@ function loadPlugin(opts = {}) {
98
98
  },
99
99
  querySelectorAll(sel) {
100
100
  if (sel === "button") return opts.navCells || [];
101
- if (typeof opts.navQuery === "function") return opts.navQuery(sel);
102
101
  return [];
103
102
  },
104
103
  };
@@ -158,7 +157,6 @@ function loadPlugin(opts = {}) {
158
157
  return {
159
158
  registered, metas, injects, requests, removed, states, localStorage,
160
159
  intervals,
161
- navWindow: sandbox.window,
162
160
  lastObserver: () => lastObserver,
163
161
  renderSection() { hook = 0; return registered.get("dsh-remote")({ close() {} }); },
164
162
  };
@@ -284,59 +282,50 @@ test("红点已看过(localStorage 有 key)时不注入,重启 DSH Web 不
284
282
  assert.equal(navCell.children.length, 0, "已看过时不应再注入红点");
285
283
  });
286
284
 
287
- // ---------- 侧栏入口「远程访问」点击流(openRemoteSettings) ----------
288
-
289
- /** 简易导航节点:带 aria-label/text/class/role 的“按钮”,记录点击并支持 data-dru-remote 标记。 */
290
- function navNode({ id = "", text = "", aria = "", cls = "", role = "", tag = "button" } = {}) {
291
- const node = {
292
- id,
293
- className: cls,
294
- textContent: text,
295
- tagName: tag,
296
- _attrs: {},
297
- _clicked: 0,
298
- getAttribute(k) { return k in this._attrs ? this._attrs[k] : null; },
299
- setAttribute(k, v) { this._attrs[k] = String(v); },
300
- click() { this._clicked++; },
301
- };
302
- if (aria) node._attrs["aria-label"] = aria;
303
- if (role) node._attrs.role = role;
304
- return node;
305
- }
285
+ test("红点只挂在设置页「远程访问」栏目导航(navCell):普通按钮即使文本含「远程访问」也不注入,不遮挡官方入口", () => {
286
+ const navCells = [];
287
+ const officialCell = makeEl("button");
288
+ officialCell.className = "VOzbGW_navCell"; // shell hashed 类名(含 navCell 子串)=设置页栏目导航
289
+ officialCell.textContent = "📱 远程访问";
290
+ const plainBtn = makeEl("button");
291
+ plainBtn.className = "some-other-btn"; // 非 navCell(官方侧栏/内容区按钮):即使含「远程访问」也不得注入
292
+ plainBtn.textContent = "远程访问";
293
+ const plugin = loadPlugin({ navCells });
306
294
 
307
- test("侧栏入口点击流:远程栏目项已存在(navCell/aria-label,含 emoji 前缀)→ 直接命中栏目,不点注入项自身", () => {
308
- const injected = navNode({ id: "dru-nav-remote", text: "📱 远程访问", cls: "navCell-clone" }); // 注入的侧栏项
309
- const settingsNav = navNode({ text: "设置", cls: "navCell-set" });
310
- const remoteCell = navNode({ aria: "远程访问", cls: "VOzbGW_navCell", tag: "button" }); // 纯图标+aria-label 的栏目项
311
- const nodes = [injected, settingsNav, remoteCell];
312
- const plugin = loadPlugin({ navQuery: () => nodes });
313
- plugin.navWindow.__dshRemoteNav.open();
314
- assert.equal(remoteCell._clicked, 1, "应命中「远程访问」栏目项(aria-label 亦可读)");
315
- assert.equal(remoteCell.getAttribute("data-dru-remote"), "1", "命中后应打 data-dru-remote 标记(加速二次命中)");
316
- assert.equal(injected._clicked, 0, "不得点击注入的侧栏项自身(防递归)");
317
- assert.equal(settingsNav._clicked, 0, "栏目已存在时无需退化点「设置」");
318
- assert.equal(plugin.intervals.filter((x) => x.ms === 150).length, 0, "直接命中后不应启动轮询");
295
+ navCells.push(officialCell, plainBtn);
296
+ plugin.lastObserver().cb([]);
297
+
298
+ assert.equal(officialCell.children.length, 1, "官方设置页「远程访问」栏目按钮应注入红点");
299
+ assert.equal(officialCell.children[0].className, "dru-reddot");
300
+ assert.equal(plainBtn.children.length, 0, "非栏目导航按钮不得注入红点(不遮挡官方按钮)");
319
301
  });
320
302
 
321
- test("侧栏入口点击流:栏目项尚未挂载 → 先点「设置」,轮询到点后再补点「远程访问」(全程不点注入项)", () => {
322
- const injected = navNode({ id: "dru-nav-remote", text: "📱 远程访问", cls: "navCell-clone" });
323
- const settingsNav = navNode({ text: "设置", cls: "navCell-set" });
324
- const remoteCell = navNode({ text: "📱 远程访问", cls: "VOzbGW_navCell" }); // 设置页打开后才挂载
325
- const nodes = [injected, settingsNav]; // 初始只有注入项 + 「设置」
326
- const plugin = loadPlugin({ navQuery: () => nodes });
327
- plugin.navWindow.__dshRemoteNav.open();
328
-
329
- assert.equal(settingsNav._clicked, 1, "栏目不可达时应点「设置」进入设置页");
330
- assert.equal(injected._clicked, 0, "不得点击注入的侧栏项自身");
331
- const poll = plugin.intervals.find((x) => x.ms === 150);
332
- assert.ok(poll, "应启动 150ms 轮询等待栏目挂载");
333
-
334
- nodes.push(remoteCell); // 设置页已打开,栏目项挂载
335
- poll.cb();
336
- assert.equal(remoteCell._clicked, 1, "轮询到点后应补点「远程访问」栏目");
337
- assert.equal(remoteCell.getAttribute("data-dru-remote"), "1", "栏目命中后应打标记");
338
- assert.equal(settingsNav._clicked, 1, "「设置」只应点一次");
339
- assert.equal(injected._clicked, 0, "轮询过程也不得点注入项自身");
303
+ // ---------- 侧栏「远程访问」快捷入口(2026-09 恢复注入:与官方「设置」共存、不遮挡) ----------
304
+ // 用户要求:左侧官方「设置」按钮旁保留「📱 远程访问」快捷按钮。实现要点(防 Bug A 复发):
305
+ // - 独立 <button id=dru-nav-remote>(绝不 cloneNode → 不继承官方事件委托/点击热区),
306
+ // 插在官方「设置」按钮之前;
307
+ // - 候选限定官方导航(class 含 navCell 或位于导航容器)+ 短文本/aria 精确匹配;
308
+ // - 命中排除自身(防递归)与“菜单/更多”语义节点;不点工作区会话行;
309
+ // - openRemoteSettings:栏目已开直接点;未开先点官方「设置」再 ≤3s 轮询补点;
310
+ // - 红点(.dru-reddot / .dru-nav-remote-dot)用同一 dsh-remote-seen-dot key,点击即收起。
311
+
312
+ test("源码约束:侧栏注入「远程访问」按钮与官方「设置」共存;独立按钮非克隆;导航点击安全", () => {
313
+ // 注入存在:入口 id / 挂载函数 / 打开函数 / 心跳保证
314
+ assert.match(SOURCE, /dru-nav-remote/);
315
+ assert.match(SOURCE, /navEnsureStart/);
316
+ assert.match(SOURCE, /openRemoteSettings/);
317
+ assert.match(SOURCE, /__dshRemoteNav/);
318
+ assert.match(SOURCE, /data-dru-remote/);
319
+ assert.match(SOURCE, /NAV_ENTRY_ID/);
320
+ // 安全设计:绝不克隆官方按钮;候选限定 navCell/导航容器 + 短文本;排除自身;排除菜单语义
321
+ assert.doesNotMatch(SOURCE, /cloneNode/);
322
+ assert.match(SOURCE, /navCell/);
323
+ assert.match(SOURCE, /NAV_TEXT_MAX/);
324
+ assert.match(SOURCE, /el\.id === NAV_ENTRY_ID/);
325
+ assert.match(SOURCE, /NAV_BAD_RE/);
326
+ // 红点仍在(官方栏目 navCell + 快捷按钮首用同 key)
327
+ assert.match(SOURCE, /dru-reddot/);
328
+ assert.match(SOURCE, /dsh-remote-seen-dot/);
340
329
  });
341
330
 
342
331
  test("源码约束:无侧边栏入口/浮动面板/切换账号;命名统一为「远程访问」;新增访问密钥/设备路由", () => {
@@ -356,13 +345,6 @@ test("源码约束:无侧边栏入口/浮动面板/切换账号;命名统一
356
345
  assert.match(SOURCE, /dsh-remote\/access-key/);
357
346
  assert.match(SOURCE, /dsh-remote\/mobile-sessions/);
358
347
  assert.match(SOURCE, /order: 30/);
359
- // 侧栏入口导航加固:aria-label/role 扫描、data-dru-remote 标记、排除注入项防递归、进设置后轮询
360
- assert.match(SOURCE, /data-dru-remote/);
361
- assert.match(SOURCE, /dru-nav-remote/);
362
- assert.match(SOURCE, /aria-label/);
363
- assert.match(SOURCE, /remoteSectionVisible/);
364
- assert.match(SOURCE, /openRemoteSettings/);
365
- assert.match(SOURCE, /__dshRemoteNav/);
366
348
  // 清理时机:退出登录成功回调内、登录账号变化时、切换自建服务时
367
349
  assert.match(SOURCE, /post\("\/dsh-remote\/logout"\)\.then\(function \(body\) \{[\s\S]*?fbClearThreads\(\);/);
368
350
  assert.match(SOURCE, /if \(prevPhone !== phone\.trim\(\)\) fbClearThreads\(\);/);