dsh-mobile-ui 0.2.4 → 0.2.6
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/client.js +150 -2
- package/package.json +1 -1
- package/tests/live-probe.py +65 -1
package/client.js
CHANGED
|
@@ -272,8 +272,56 @@ window.__ModuleLoader__.load({
|
|
|
272
272
|
可用、靠左时左溢出;fixed 与容器位置无关(实测两种场景都在视口内)。
|
|
273
273
|
⚠️ hash 前缀(QsffPG_ / _1nxmc_)会随 dsh 升级漂移、失效是静默的(退回溢出),
|
|
274
274
|
升级后跑 tests/live-probe.py 复核(已覆盖后台任务菜单)。 */
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
/* ⚠️ popover 的 top 必须落在**触发按钮下方**:菜单弹出后若盖住按钮本身,
|
|
276
|
+
用户再点一下(想关掉/重开)时点击落在菜单上 —— 体感就是「点了没反应」
|
|
277
|
+
(2026-09-14 用户报「三个点按钮点击没反应」:实测命中率只有 2/6,
|
|
278
|
+
菜单 [8,76,374,42] 正好压在 tabs 行的按钮 80..106 上)。
|
|
279
|
+
· QsffPG:触发按钮在 header 第二行 42..70 ⇒ top 74
|
|
280
|
+
· _1nxmc_(「更多操作」):触发按钮在 tabs 行 80..106 ⇒ top 122 */
|
|
281
|
+
" body.dsh-mobile-ui [class*=QsffPG_menu],body.dsh-mobile-ui [role=menu][class*=_list_1nxmc_]{position:fixed !important;left:8px !important;right:8px !important;width:auto !important;min-width:0 !important;max-width:none !important;max-height:calc(100vh - 150px) !important;overflow:auto !important;top:calc(var(--dsh-mobile-popover-top,74px) + env(safe-area-inset-top,0px)) !important}",
|
|
282
|
+
" body.dsh-mobile-ui [role=menu][class*=_list_1nxmc_]{--dsh-mobile-popover-top:122px}",
|
|
283
|
+
/* ── 手机端 header 瘦身 + 正文占比(2026-09-14,用户反馈「标题栏三行有点乱 / 正文显得窄」)──
|
|
284
|
+
实测 390px:header 138px(titleRow 三行)+ 输入区 272px ⇒ 正文只剩 434px(占屏 51%)。
|
|
285
|
+
三行里第三行是「终端打开 / 选择打开方式 / 更多操作 / 打开右侧边栏」——前两个点了是在
|
|
286
|
+
**服务器上**开终端(手机上什么也看不到)、右侧边栏早被本插件隐藏(点了空白),
|
|
287
|
+
只有「更多操作」(下载 Session 日志)有用。处理:
|
|
288
|
+
① 整行隐藏(行空掉后 header 少 32px);
|
|
289
|
+
② 「更多操作」改由**插件注入到 tabs 行空白处的按钮代理**(点击转发给上游按钮,
|
|
290
|
+
上游按钮仍留在 DOM 里只是不可见,程序化 click 照常触发 React 逻辑);
|
|
291
|
+
③ tabs 行右侧本来空着 246px,顺带放「折叠输入框」开关。
|
|
292
|
+
隐藏后 titleRow 只剩 titleCluster 一行组(crumbs 行 + headerActions 行),header ≈ 105px。 */
|
|
293
|
+
/* ⚠️ headerUtilities 用「移出文档流但保留坐标」而不是 display:none:
|
|
294
|
+
上游「更多操作」按钮的菜单位置由它自己的 getBoundingClientRect 算出,display:none 时
|
|
295
|
+
该矩形全 0 ⇒ 菜单渲染成 0×0 根本看不见(2026-09-14 实测踩到)。
|
|
296
|
+
改成 absolute + opacity:0 + pointer-events:none:不占布局高度(header 照样瘦身)、
|
|
297
|
+
矩形照常可测、人也点不到(点击由 tabs 行的代理按钮转发)。 */
|
|
298
|
+
" body.dsh-mobile-ui [class*=wSkVaW_headerUtilities]{position:absolute !important;top:74px !important;left:40px !important;opacity:0 !important;pointer-events:none !important}",
|
|
299
|
+
" body.dsh-mobile-ui [class*=wSkVaW_headerCorner]{display:none !important}",
|
|
300
|
+
/* 抽屉入口(原右上角悬浮汉堡 top:48/right:12,42px)在 header 从 138 降到 107 后
|
|
301
|
+
正好压在第二行(后台任务)与 tabs 行上 —— vision 复核截图时抓到。
|
|
302
|
+
改为并入 tabs 行工具组(#dsh-mobile-tab-tools 第三个按钮,点击仍走 openDrawer()),
|
|
303
|
+
原悬浮按钮恒定隐藏。 */
|
|
304
|
+
/* ⚠️ 原悬浮汉堡只在「有 tabs 行的会话页」隐藏 —— 首页(hero)没有 tabs 行,
|
|
305
|
+
若一并隐藏就**没有任何抽屉入口**了(2026-09-14 实测踩到:首页点不开会话列表)。
|
|
306
|
+
:has() 判断 tabs 行是否存在;首页仍走 JS 原有的三/四态显隐逻辑。 */
|
|
307
|
+
" body.dsh-mobile-ui:has([class*=wSkVaW_tabs]) #dsh-mobile-menu-btn{display:none !important}",
|
|
308
|
+
/* 正文与 tabs 行的间距(折叠输入区后正文直接顶到 tabs 下方,实测贴得过近) */
|
|
309
|
+
" body.dsh-mobile-ui.dsh-mobile-composer-hidden [class*=scrollBody]{padding-bottom:calc(14px + env(safe-area-inset-bottom,0px)) !important}",
|
|
310
|
+
/* 折叠态的可发现性:内容末尾给一句提示(视觉模型复核时指出折叠后底部大片留白、
|
|
311
|
+
用户可能不知道从哪恢复;提示跟随内容,长会话滚到底才看到,不打扰阅读) */
|
|
312
|
+
" body.dsh-mobile-ui.dsh-mobile-composer-hidden [class*=scrollBody]::after{content:'输入框已折叠 · 点标签行右侧 ▤ 图标展开';display:block;text-align:center;padding:14px 0 4px;font-size:11px;line-height:1.4;color:var(--dsw-alias-label-caption,rgba(255,255,255,.35))}",
|
|
313
|
+
/* tabs 行做成「标签靠左 / 工具靠右」 */
|
|
314
|
+
" body.dsh-mobile-ui [class*=wSkVaW_tabs]{align-items:center !important;min-height:34px !important}",
|
|
315
|
+
" body.dsh-mobile-ui #dsh-mobile-tab-tools{margin-left:auto !important;display:inline-flex !important;align-items:center !important;gap:8px !important;flex:0 0 auto !important}",
|
|
316
|
+
" body.dsh-mobile-ui #dsh-mobile-tab-tools button{width:36px !important;height:34px !important;min-width:36px !important;display:inline-flex !important;align-items:center !important;justify-content:center !important;padding:0 !important;border:none !important;border-radius:8px !important;background:transparent !important;color:var(--dsw-alias-label-secondary,rgba(255,255,255,.7)) !important;cursor:pointer !important}",
|
|
317
|
+
" body.dsh-mobile-ui #dsh-mobile-tab-tools button:active{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08)) !important}",
|
|
318
|
+
" body.dsh-mobile-ui #dsh-mobile-tab-tools svg{width:18px !important;height:18px !important;fill:none !important;stroke:currentColor !important;stroke-width:1.8 !important;stroke-linecap:round !important;stroke-linejoin:round !important}",
|
|
319
|
+
/* ── 折叠输入区(阅读模式):省下 composerSeat 的 ~272px,全部让给正文 ──
|
|
320
|
+
触发方式:tabs 行的键盘按钮(状态存 localStorage,刷新后保持)。
|
|
321
|
+
⚠️ 提问卡片(ask_user_question)就渲染在 composerSeat 里 ⇒ 折叠状态下若检测到卡片,
|
|
322
|
+
JS 会强制展开(见 sync 里的 qaOpen 分支),否则用户看不到问题。 */
|
|
323
|
+
" body.dsh-mobile-ui.dsh-mobile-composer-hidden [class*=composerSeat],body.dsh-mobile-ui.dsh-mobile-composer-hidden [class*=composerStack],body.dsh-mobile-ui.dsh-mobile-composer-hidden [class*=uV2eYG_root]{display:none !important}",
|
|
324
|
+
" body.dsh-mobile-ui #dsh-mobile-tab-tools button.dsh-mobile-on{background:var(--dsw-specific-selector,rgba(255,255,255,.12)) !important;color:var(--dsw-alias-label-primary,rgba(255,255,255,.9)) !important}",
|
|
277
325
|
/* 输入框聚焦反馈 */
|
|
278
326
|
" body.dsh-mobile-ui textarea:focus,body.dsh-mobile-ui [class*=input]:focus-within{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-brand-primary,rgba(79,124,255,.45))}",
|
|
279
327
|
/* 消息流操作按钮(复制/反馈/分享)触摸目标提升。
|
|
@@ -475,6 +523,102 @@ window.__ModuleLoader__.load({
|
|
|
475
523
|
document.body.appendChild(tabbar);
|
|
476
524
|
}
|
|
477
525
|
|
|
526
|
+
/* ── tabs 行工具按钮(2026-09-14)──────────────────────────────────
|
|
527
|
+
header 第三行在手机上是死重(见 CSS 段注释:终端/打开方式点了是在服务器开终端,
|
|
528
|
+
右侧边栏早被本插件隐藏),整行已用 CSS 隐藏;这里把唯一有用的「更多操作」代理过来
|
|
529
|
+
(点击转发给上游按钮 —— 它仍在 DOM 里只是不可见,程序化 click 照常触发 React 逻辑),
|
|
530
|
+
并新增「折叠输入区」开关(阅读模式,省 ~272px 全部让给正文)。
|
|
531
|
+
⚠️ 注入的节点会被 React 重渲染清掉 ⇒ 在 sync() 里保持 ensure(幂等)。
|
|
532
|
+
⚠️ 提问卡片就住在 composerSeat 里 ⇒ 折叠状态下一旦检测到卡片必须强制展开,
|
|
533
|
+
否则用户看不到问题(applyComposerHidden 里用 qaOpen 兜住)。 */
|
|
534
|
+
var COMPOSER_HIDDEN_KEY = "dsh-mobile-composer-hidden";
|
|
535
|
+
function composerHidden() {
|
|
536
|
+
try { return window.localStorage.getItem(COMPOSER_HIDDEN_KEY) === "1"; }
|
|
537
|
+
catch (e) { return false; }
|
|
538
|
+
}
|
|
539
|
+
function applyComposerHidden() {
|
|
540
|
+
try {
|
|
541
|
+
var hide = composerHidden() && !qaOpen;
|
|
542
|
+
document.body.classList.toggle("dsh-mobile-composer-hidden", !!hide);
|
|
543
|
+
var btn = document.getElementById("dsh-mobile-fold-composer");
|
|
544
|
+
if (btn) {
|
|
545
|
+
btn.classList.toggle("dsh-mobile-on", !!hide);
|
|
546
|
+
btn.setAttribute("aria-label", hide ? "显示输入框" : "隐藏输入框");
|
|
547
|
+
}
|
|
548
|
+
} catch (e) { /* 静默降级 */ }
|
|
549
|
+
}
|
|
550
|
+
function setComposerHidden(on) {
|
|
551
|
+
try { window.localStorage.setItem(COMPOSER_HIDDEN_KEY, on ? "1" : "0"); } catch (e) { /* 隐私模式 */ }
|
|
552
|
+
applyComposerHidden();
|
|
553
|
+
}
|
|
554
|
+
function ensureTabTools() {
|
|
555
|
+
try {
|
|
556
|
+
var tabs = document.querySelector("[class*=wSkVaW_tabs]");
|
|
557
|
+
if (!tabs) return;
|
|
558
|
+
if (!document.getElementById("dsh-mobile-tab-tools")) {
|
|
559
|
+
var box = document.createElement("div");
|
|
560
|
+
box.id = "dsh-mobile-tab-tools";
|
|
561
|
+
/* 「更多操作」代理:转发给上游按钮(含「下载 Session 日志」) */
|
|
562
|
+
var more = document.createElement("button");
|
|
563
|
+
more.type = "button";
|
|
564
|
+
more.id = "dsh-mobile-more-proxy";
|
|
565
|
+
more.setAttribute("aria-label", "更多操作");
|
|
566
|
+
more.innerHTML = "<svg viewBox='0 0 24 24'><circle cx='5' cy='12' r='1.7'/><circle cx='12' cy='12' r='1.7'/><circle cx='19' cy='12' r='1.7'/></svg>";
|
|
567
|
+
/* ⚠️ 竞态:上游的「点外面就关」监听的是 **pointerdown**(document 冒泡阶段),
|
|
568
|
+
而我们的转发在 **click**(按钮上)。如果菜单本来开着,一次 tap 会:
|
|
569
|
+
pointerdown → 上游关掉菜单 → click → 我们又叫开
|
|
570
|
+
净效果「点了没反应」(2026-09-14 用户报「三个点按钮点击没反应」,
|
|
571
|
+
实测命中率 2/6)。修法:在按钮上的 pointerdown(比 document 早)记下当前是否开着,
|
|
572
|
+
开着就什么都不做 —— 交给上游关掉,真正的 toggle。 */
|
|
573
|
+
more.addEventListener("pointerdown", function () {
|
|
574
|
+
try {
|
|
575
|
+
more.__menuWasOpen = !!document.querySelector("[role=menu][class*=_list_1nxmc_]");
|
|
576
|
+
} catch (err) { more.__menuWasOpen = false; }
|
|
577
|
+
}, true);
|
|
578
|
+
more.addEventListener("click", function (e) {
|
|
579
|
+
try {
|
|
580
|
+
e.preventDefault();
|
|
581
|
+
e.stopPropagation();
|
|
582
|
+
if (more.__menuWasOpen) { more.__menuWasOpen = false; return; }
|
|
583
|
+
var up = document.querySelector("button[class*=nL4_yW_moreButton]");
|
|
584
|
+
if (up) up.click();
|
|
585
|
+
} catch (err) { /* 静默 */ }
|
|
586
|
+
});
|
|
587
|
+
/* 折叠/展开输入区 */
|
|
588
|
+
var fold = document.createElement("button");
|
|
589
|
+
fold.type = "button";
|
|
590
|
+
fold.id = "dsh-mobile-fold-composer";
|
|
591
|
+
fold.setAttribute("aria-label", "隐藏输入框");
|
|
592
|
+
fold.innerHTML = "<svg viewBox='0 0 24 24'><rect x='2.5' y='6' width='19' height='12' rx='2.5'/><path d='M6.5 10h11M6.5 14h6'/></svg>";
|
|
593
|
+
fold.addEventListener("click", function (e) {
|
|
594
|
+
try {
|
|
595
|
+
e.preventDefault();
|
|
596
|
+
e.stopPropagation();
|
|
597
|
+
setComposerHidden(!composerHidden());
|
|
598
|
+
} catch (err) { /* 静默 */ }
|
|
599
|
+
});
|
|
600
|
+
/* 抽屉入口(取代右上角悬浮汉堡,见 CSS 段注释) */
|
|
601
|
+
var menuBtn = document.createElement("button");
|
|
602
|
+
menuBtn.type = "button";
|
|
603
|
+
menuBtn.id = "dsh-mobile-tab-menu";
|
|
604
|
+
menuBtn.setAttribute("aria-label", "菜单");
|
|
605
|
+
menuBtn.innerHTML = "<svg viewBox='0 0 24 24'><path d='M4 7h16M4 12h16M4 17h10'/></svg>";
|
|
606
|
+
menuBtn.addEventListener("click", function (e) {
|
|
607
|
+
try {
|
|
608
|
+
e.preventDefault();
|
|
609
|
+
e.stopPropagation();
|
|
610
|
+
openDrawer();
|
|
611
|
+
} catch (err) { /* 静默 */ }
|
|
612
|
+
});
|
|
613
|
+
box.appendChild(more);
|
|
614
|
+
box.appendChild(fold);
|
|
615
|
+
box.appendChild(menuBtn);
|
|
616
|
+
tabs.appendChild(box);
|
|
617
|
+
}
|
|
618
|
+
applyComposerHidden();
|
|
619
|
+
} catch (e) { /* 静默降级 */ }
|
|
620
|
+
}
|
|
621
|
+
|
|
478
622
|
function setTabActive(key) {
|
|
479
623
|
try {
|
|
480
624
|
if (!tabbar) return;
|
|
@@ -575,6 +719,8 @@ window.__ModuleLoader__.load({
|
|
|
575
719
|
tabbar.style.display = (drawerOpen || settingsOpen || qaOpen || popOpen)
|
|
576
720
|
? "none" : "";
|
|
577
721
|
}
|
|
722
|
+
/* tabs 行工具按钮(更多操作代理 + 折叠输入区)—— React 重渲染后重建 */
|
|
723
|
+
ensureTabTools();
|
|
578
724
|
} else {
|
|
579
725
|
document.body.classList.remove("dsh-mobile-ui");
|
|
580
726
|
qaOpen = false;
|
|
@@ -598,6 +744,8 @@ window.__ModuleLoader__.load({
|
|
|
598
744
|
mq.addListener(sync);
|
|
599
745
|
}
|
|
600
746
|
|
|
747
|
+
/* 折叠状态在进入页面时就生效(localStorage 恢复),不等 React 渲染完 */
|
|
748
|
+
applyComposerHidden();
|
|
601
749
|
/* 首次同步 + 等待 React 渲染出 frame 后重试 */
|
|
602
750
|
sync();
|
|
603
751
|
var tries = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mobile-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Mobile UI for the DeepSeek Harness Web GUI: responsive layout, overlay session drawer, 44px touch targets, safe-area support, reading enhancements (≤768px, zero desktop impact)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
package/tests/live-probe.py
CHANGED
|
@@ -152,7 +152,10 @@ def run(args) -> int:
|
|
|
152
152
|
# 上游按桌面宽度做左/右对齐 —— 后台任务菜单 right 溢出 207px、「更多操作」菜单 left 溢出 90px)
|
|
153
153
|
for name, trig_sel, menu_sel in (
|
|
154
154
|
("后台任务菜单", "button[class*=QsffPG_trigger]", "[class*=QsffPG_menu]"),
|
|
155
|
-
|
|
155
|
+
# 「更多操作」的入口是插件注入的**代理按钮**:上游按钮已被移出可点层
|
|
156
|
+
# (position:absolute; opacity:0; pointer-events:none —— 否则它会算出 0×0 的菜单,
|
|
157
|
+
# 见 client.js 里 headerUtilities 段注释),点它才算走用户的真实路径
|
|
158
|
+
("更多操作菜单", "#dsh-mobile-more-proxy", "[role=menu][class*=_list_1nxmc_]"),
|
|
156
159
|
):
|
|
157
160
|
pos = page.evaluate(
|
|
158
161
|
"""(s) => { const b = document.querySelector(s); if (!b) return null;
|
|
@@ -171,6 +174,67 @@ def run(args) -> int:
|
|
|
171
174
|
page.keyboard.press("Escape")
|
|
172
175
|
page.wait_for_timeout(800)
|
|
173
176
|
|
|
177
|
+
# ③c header 瘦身 + 折叠输入区(2026-09-14:用户反馈「标题栏三行有点乱 / 正文显得窄」)
|
|
178
|
+
lay = page.evaluate("""() => {
|
|
179
|
+
const hdr = document.querySelector('header'); const hb = hdr ? hdr.getBoundingClientRect().bottom : 0;
|
|
180
|
+
const seat = document.querySelector('[class*=composerSeat]');
|
|
181
|
+
const seatVis = seat && getComputedStyle(seat).display !== 'none' && seat.getBoundingClientRect().height > 0;
|
|
182
|
+
const bottom = seatVis ? seat.getBoundingClientRect().top : window.innerHeight;
|
|
183
|
+
const tools = [...document.querySelectorAll('#dsh-mobile-tab-tools button')].map(b => b.id);
|
|
184
|
+
const hamburger = document.getElementById('dsh-mobile-menu-btn');
|
|
185
|
+
return { headerH: Math.round(hb), readPct: Math.round((bottom - hb) / window.innerHeight * 100),
|
|
186
|
+
tools: tools, hamburgerHidden: hamburger ? getComputedStyle(hamburger).display === 'none' : null }; }""")
|
|
187
|
+
print(f" [布局] header={lay['headerH']}px 正文占比={lay['readPct']}% 工具={lay['tools']}")
|
|
188
|
+
# 行数回涨(三行 header ⇒ 138px)或工具组缺失都算回归
|
|
189
|
+
if lay["headerH"] > 120:
|
|
190
|
+
fails.append(f"header 又变高了({lay['headerH']}px > 120px,是不是行数回涨了?)")
|
|
191
|
+
if len(lay["tools"]) < 3:
|
|
192
|
+
fails.append(f"tabs 行工具组不全:{lay['tools']}(应含 more-proxy / fold-composer / tab-menu)")
|
|
193
|
+
if lay.get("hamburgerHidden") is not True:
|
|
194
|
+
fails.append("会话页的原悬浮汉堡没隐藏(会压住第二行/tabs 行)")
|
|
195
|
+
# 折叠切换:正文占比应显著上升,且按钮状态跟着翻
|
|
196
|
+
fold = page.evaluate("""() => { const b = document.getElementById('dsh-mobile-fold-composer'); if (!b) return null;
|
|
197
|
+
const r = b.getBoundingClientRect(); return [r.x + r.width / 2, r.y + r.height / 2]; }""")
|
|
198
|
+
if fold:
|
|
199
|
+
page.touchscreen.tap(fold[0], fold[1])
|
|
200
|
+
page.wait_for_timeout(1200)
|
|
201
|
+
folded = page.evaluate("""() => {
|
|
202
|
+
const seat = document.querySelector('[class*=composerSeat]');
|
|
203
|
+
const seatVis = seat && getComputedStyle(seat).display !== 'none' && seat.getBoundingClientRect().height > 0;
|
|
204
|
+
const hb = document.querySelector('header').getBoundingClientRect().bottom;
|
|
205
|
+
const bottom = seatVis ? seat.getBoundingClientRect().top : window.innerHeight;
|
|
206
|
+
return { hidden: document.body.classList.contains('dsh-mobile-composer-hidden'),
|
|
207
|
+
readPct: Math.round((bottom - hb) / window.innerHeight * 100) }; }""")
|
|
208
|
+
print(f" [折叠] hidden={folded['hidden']} 正文占比={folded['readPct']}%")
|
|
209
|
+
if not folded["hidden"] or folded["readPct"] < lay["readPct"] + 15:
|
|
210
|
+
fails.append(f"折叠输入区没生效({lay['readPct']}% → {folded['readPct']}%)")
|
|
211
|
+
page.touchscreen.tap(fold[0], fold[1]) # 复原,别影响后续断言
|
|
212
|
+
page.wait_for_timeout(1000)
|
|
213
|
+
back = page.evaluate("() => document.body.classList.contains('dsh-mobile-composer-hidden')")
|
|
214
|
+
if back:
|
|
215
|
+
fails.append("再次点击没能恢复输入区")
|
|
216
|
+
|
|
217
|
+
# ③d 工具按钮必须真的能用:⋯ 代理按钮开→关一次就翻状态
|
|
218
|
+
# (曾因「上游 pointerdown 先关 / 我们的 click 又开」的竞态,命中率只有 2/6 ——
|
|
219
|
+
# 用户报「三个点按钮点击没反应」;折叠按钮也曾因 30×26 目标过小被报「不灵敏」)
|
|
220
|
+
menus_js = "() => document.querySelectorAll('[role=menu][class*=_list_1nxmc_]').length"
|
|
221
|
+
mp = page.evaluate("""() => { const b = document.getElementById('dsh-mobile-more-proxy'); if (!b) return null;
|
|
222
|
+
const r = b.getBoundingClientRect(); return [r.x + r.width / 2, r.y + r.height / 2]; }""")
|
|
223
|
+
if mp:
|
|
224
|
+
seq = []
|
|
225
|
+
for _ in range(2):
|
|
226
|
+
page.touchscreen.tap(mp[0], mp[1])
|
|
227
|
+
page.wait_for_timeout(1000)
|
|
228
|
+
seq.append(page.evaluate(menus_js))
|
|
229
|
+
print(f" [⋯ 代理按钮] 开合序列={seq}(期望 [1,0])")
|
|
230
|
+
if seq != [1, 0]:
|
|
231
|
+
fails.append(f"「更多操作」代理按钮不能开合:{seq}(上游 pointerdown 与转发 click 的竞态又回来了?)")
|
|
232
|
+
# 按钮尺寸:触摸目标不得小于 32px(插件规范 44,实测 30×26 时用户报"不灵敏")
|
|
233
|
+
size = page.evaluate("""() => { const b = document.getElementById('dsh-mobile-more-proxy');
|
|
234
|
+
if (!b) return null; const r = b.getBoundingClientRect(); return [Math.round(r.width), Math.round(r.height)]; }""")
|
|
235
|
+
if size and (size[0] < 32 or size[1] < 30):
|
|
236
|
+
fails.append(f"工具按钮触摸目标过小:{size}")
|
|
237
|
+
|
|
174
238
|
# ④ 菜单行可进入子代理(面包屑应变成两段)
|
|
175
239
|
page.touchscreen.tap(sw["x"], sw["y"])
|
|
176
240
|
page.wait_for_timeout(800)
|