Skip to content

fix(menu): close the reduced-motion media query that swallowed the su… - #974

Open
m4ku1337 wants to merge 1 commit into
omdsh-dev:devfrom
m4ku1337:fix/submenu-flip-top-level
Open

m4ku1337 wants to merge 1 commit into
omdsh-dev:devfrom
m4ku1337:fix/submenu-flip-top-level

Conversation

@m4ku1337

Copy link
Copy Markdown

Fixes #955.

现象与根因

src/client/layout.css 第 77 行的 @media (prefers-reduced-motion: reduce) 没有闭合。从第 78 行到文件末尾都在它里面——而这一段里只有两类规则:媒体查询本来就该包住的那条中心列规则,以及消费 menu-flip.ts 写在 <body> 上的 data-dsh-sidebar-submenu 的六条规则(把二级菜单翻回视口内的翻转规则 + 视觉牵引)。默认主题下这六条一条都不匹配,于是右侧栏场景的「打开方式」二级菜单照宿主原始几何向右展开、越过窗口右缘;开着「减少动态效果」的用户反而是唯一能用上翻转的人。

回归来源(同一套块扫描逐版本实测):

版本 { / } 六条规则的嵌套深度
36fbc5a^(v0.24.1 之前) 13 / 13 0(顶层)
36fbc5a(2026-09-28) 7 / 6 规则被整段删除(该提交 0 插入 / 61 删除,删掉的那段以那个 } 开头、直到文件末尾)
b011be1(同一天,其后) 13 / 12 1(嵌套;该提交 +60 行把规则加了回来)
v0.24.1 / v0.25.0 / v0.26.0 / v0.26.1(dev 上的 0.27.0 同) 13 / 12 1
本分支 13 / 13 0

也就是说:这个机制自 v0.24.1 发布起就没生效过。

改动

  • src/client/layout.css:补回那个 }(+1 行,规则文本零改动;修复后与 36fbc5a^ 逐字节相同,7219 字节)。
  • tests/css-block-scope.spec.ts(新增):钉住让这件事一直隐身的那条性质。tests/menu-flip.spec.tsx 用的是 expect(css).toContain(...)——字符串嵌套与否都命中,所以从 v0.24.1 到 v0.26.1、再到 dev 一直是绿的。新 spec 读块结构:
    • src/ 下每个样式表都必须闭合它打开的块(多一个 } 也报);
    • 六条翻转/牵引选择器必须开在深度 0;
    • 缩小到只约束「减少动态效果」那个块自己的子块列表(因此将来在同一文件里新增别的 @media 不会误伤);
    • 牵引规则必须声明在翻转覆盖之前(layout.css 自己写明这条顺序是承重的);
    • 选择器比较先做归一化(空白折叠 + 属性值引号去掉),所以重排版或 [role=menu] 不会造成假红;扫描器会整段吃掉引号字符串与 url(...)(含未加引号的),字符串或 data URI 里的括号不会误判。

验证

  • 反例先行:把 src/client/layout.css 切回 origin/dev、保留新 spec → 2 failed:
    src\client\layout.css: 1 unclosed block(s) 与 body[data-dsh-sidebar-submenu] div[role=menu] div[role=menu] is nested instead of top level: expected 1 to be +0。
  • 变异实验(都会红):括号配平但挪错位置;往媒体查询里塞一条无关规则;把 ~="left" 改名成 ~="leftx"(点名缺失的选择器)。
  • 构建产物:pnpm build 后 lib/client.js 内联的那份 layout.css 共 7360 字节,未闭合块 0、多余右括号 0,六条规则全部在深度 0。
  • 门禁:pnpm typecheck、pnpm lint 无输出;tests/css-block-scope.spec.ts + tests/menu-flip.spec.tsx + tests/layout-css.spec.ts 共 14 passed。
  • 全量套件(本机 Windows):218 文件 / 2652 用例里 5 条失败。其中 git-worktree(30s 超时)与 plugin-meta(tar 列表带 CR)在干净的 origin/dev 上同样失败;另 3 条属于 tests/fs-operations.spec.ts 在全量并行下的超时级联(共用同一个孤儿 .tmp),单独运行该文件 29/29 通过。本次改动只碰 layout.css 与一个新 spec 文件,触及不到这条路径。plugin-mount 车道(真实 DSH + Playwright)本地未跑。

独立复核

三个互不共享上下文、各自 clone + 安装 + 实测的子代理(详细报告见下方评论):① 仅凭源码复现 + diff 审计 → PASS;② 对抗性证伪新 spec → PASS(上面几条加固正是它的发现);③ 完整 CI 门禁并与 dev 基线逐条归因 → 本机 pnpm test 在分支与 dev 上都不全绿(环境性失败),没有一条能归到本次改动。

AI 辅助声明:定位、补丁、测试与上述独立复核均由 AI 助手按用户指令完成,报告中的证据都是原始命令输出。

…bmenu flip (omdsh-dev#955)

layout.css opens @media (prefers-reduced-motion: reduce) at line 77 and never
closes it. 36fbc5a deleted the run of 61 lines that started with that brace and
ran to the end of the file (0 insertions, 61 deletions), and b011be1 added the
context-menu submenu rules back below the still-open block (60 insertions). The
file has been one brace short ever since: 13 '{' against 12 '}'.

Everything from line 78 on is therefore a descendant of the media query. The
only rules in that range are the media query's intended child — the center
column's 'transition: none' — and the six rules that consume the
data-dsh-sidebar-submenu attribute menu-flip.ts publishes on <body>: the flip
that keeps a nested submenu inside the viewport, and the visual tether (parent
row highlight, 4px gap, 4px hover corridor). In the default theme none of them
matched, so our "open with" submenu grew past the right edge of a right-docked
workbench; reduce-motion users were the only ones who ever got the flip (omdsh-dev#955).

Restoring the brace puts all six back at top level. No rule text changes: the
result is byte-identical to the pre-regression revision 36fbc5a^ (7219 bytes).

tests/css-block-scope.spec.ts pins the property that hid this: menu-flip.spec.tsx
asserts with toContain, which matches selector text wherever it sits, so the
nesting stayed green in every released version from v0.24.1 through v0.26.1, and
on dev (0.27.0) where it still is. The new spec reads block structure instead:
every stylesheet under src/ has to close what it opens, and the six flip/tether
selectors have to open at depth 0. Run against the historical revisions, the spec is red on every
revision that shipped the nesting — v0.24.1 through v0.26.1 and dev — where it
reports "1 unclosed block(s)" and names the flip rules as nested. 36fbc5a is the
commit that lost both the brace and the rules, and it deleted menu-flip.ts with
them, so the spec cannot even load there; with that module stubbed back in, both
assertions fail on its CSS. On 36fbc5a^ and on this branch the spec passes.

Verified on this branch: pnpm exec vitest run tests/css-block-scope.spec.ts
tests/menu-flip.spec.tsx -> 11 passed; pnpm typecheck, pnpm lint clean.
@m4ku1337

Copy link
Copy Markdown
Author

独立复核摘要(三个互不共享上下文的子代理,各自独立 clone + pnpm install + 实跑)

① 复现与审计(不看作者结论,仅凭源码)— PASS

  • 自写括号栈扫描器:origin/dev 的 src/client/layout.css = { 13 / } 12,未闭合的是第 77 行第 41 列的 @media (prefers-reduced-motion: reduce) {;第 78–141 行全在其内。逐块枚举:被吞掉的只有那六条 data-dsh-sidebar-submenu 规则(外加媒体查询本来就该包住的那条中心列规则)。
  • 全仓 git grep data-dsh-sidebar-submenu:发布方只有 src/client/menu-flip.ts:23,消费方只有 layout.css:110/117/121/126/131/137——六条即全部。
  • 修复后与 36fbc5a^ 逐字节相同(7219 字节)。git show --numstat 36fbc5a = 0 61,hunk 为 @@ -81,61 +80,0 @@;b011be1 为 @@ -80,0 +81,60 @@。
  • 它对提交信息逐条判真伪,抓出两处措辞问题(36fbc5a 那次改动里 menu-flip.ts 也一并被删,因此"两条断言在 36fbc5a 全红"只对旧版 spec 成立;v0.27.0 是 dev 上的版本号而非 tag)。第二处已在 c0e6851 的提交信息里改正(树未变,git diff 701e8b3 c0e6851 为空)。

② 对抗性证伪新 spec — PASS(两轮)

  • 第一轮:dev 的 CSS + 新 spec → 2 failed;dev 上跑旧 spec(menu-flip.spec.tsx + layout-css.spec.ts)→ 12 passed,即旧断言确实抓不到。构造并实测「配平但挪错括号」(两种)、「往媒体查询里塞无关规则」、「改名 ~="leftx"」、「把牵引挪到翻转之后」等变体;其中"配平"那两种只有深度断言能拦——只数括号不够。
  • 它给出的六处脆弱点全部落进了本分支:选择器归一化(空白折叠 + 属性值引号去掉)、属性常量改为从 menu-flip.ts import、路径改用 import.meta.dirname、媒体查询断言收窄为只约束它自己的直接子块、多余右括号也计入、未加引号的 url() 整段吃掉;另加一条级联顺序断言。
  • 第二轮(701e8b3)逐项复测:6a 多行选择器、6b [role=menu]、6c 一致改名属性、7 未加引号的 url() 全部由假红/漏报变为正确;3a/3b-A/3b-B 与新增的顺序变体仍然红;新增的合法 @media 不再误伤。
  • 仍存的边界(诚实列出,均比 [Bug] 右键「在应用中打开」二级子菜单溢出到视口右缘之外:子菜单翻转规则被一个未闭合的 @media 包住而全部失效(0.26.0 复现,36fbc5a 起回归) #955 那个缺陷更窄):只断言六个被钉住的选择器,删除 layout.css 里其它规则不会被发现(配平 ≠ 内容);规则内部再嵌套(CSS nesting)不受约束;两处 ::before 只在"存在且顶层"上被钉住,未参与顺序断言;失败信息里的路径在 Windows 上带反斜杠(仅影响文案)。

③ 完整门禁 + dev 基线对照

  • 分支:pnpm typecheck exit 0、pnpm lint exit 0、pnpm test 5 failed / 2641 passed;同机同命令在 origin/dev 上 2 failed / 2642 passed。
  • 归因:git-worktree(30s 超时)与 plugin-meta(tar -tzf 列表带 CR)在 dev 上同样失败;tests/fs-operations.spec.ts 的 3 条是全量并行下的一次超时级联(共用同一个孤儿 .tmp),该文件单独跑 29/29 通过。
  • 它没跑到的两项,由作者补上:pnpm build 后 lib/client.js 内联的那份 layout.css 7360 字节、0 未闭合、0 多余右括号、六条规则深度 0;plugin-mount 车道(真实 DSH + Playwright)本地未跑,留给 CI。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant