Skip to content

feat(ui): v0.6.0 Web UI responsive — sidebar collapse, dynamic viewport, narrow-viewport drawer, touch interaction (issues #99, #100) - #110

Open
linletian wants to merge 23 commits into
developfrom
feature/v0.6.0-webui-sidebar-responsive
Open

linletian wants to merge 23 commits into
developfrom
feature/v0.6.0-webui-sidebar-responsive

Conversation

@linletian

Copy link
Copy Markdown
Owner

概述

v0.6.0 Web UI 响应式系列(issue #99 侧栏折叠 + issue #100 L1/L2/L3 响应式三层),按 docs/plans/feature-v0.6.0-webui-sidebar-responsive/(PRD-CHANGE 见 PR #109,已合并)分四期落地,每期均经双外部评审(opencode + mcode)多轮复核后原子提交。

分期内容

  • PR1(ui(sidebar): 工作树侧栏支持折叠 / 展开(桌面端) #99):桌面端 worktree 侧栏折叠/展开——#app 单一 .collapsed 类驱动(#sidebar width: 0 + overflow: hidden + visibility: hidden),#header 左侧 chevron 开关(<</>>),localStorage 持久化(mw.ui.sidebarCollapsed),Ctrl/Cmd+B 快捷键(含 xterm 自行截断按键的机制核实),折叠后 xterm 按序重排。
  • PR2(ui(responsive): Web UI 响应式布局 + 触屏交互(iOS / iPadOS) #100 L1):视口与高度——100vh fallback 经 @supports 渐进增强为 100dvh;宽度 100% 取代 100vw;viewport-fit=cover + 四向 env(safe-area-inset-*) padding;iOS 软键盘由 visualViewport 钉高补偿(不引入 interactive-widget),捏合/双击缩放退出钉高。
  • PR3(ui(responsive): Web UI 响应式布局 + 触屏交互(iOS / iPadOS) #100 L2):断点与 drawer——三档断点(≤768px 窄屏 drawer / 769–1024px 收窄 200px / >1024px 宽屏),仍由单一 .collapsed 类驱动两种形态;PR1 折叠规则限定进无缝互补的 @media not all and (max-width: 768px)(堵住分数宽度死区回归缝);遮罩点按关闭走同一状态路径;z-index 分层 40>30>25 仅限窄屏;tab 栏 scroll-snap;R14 窄屏首访默认关闭 drawer(显式持久化值永远优先)。
  • PR4(ui(responsive): Web UI 响应式布局 + 触屏交互(iOS / iPadOS) #100 L3):触屏交互——分栏拖拽迁 Pointer Events(setPointerCapture + pointercancel + 重入守卫,钳制数学逐字未动,桌面鼠标路径零回归);@media (pointer: coarse) 下 .icon-btn/.term-ctrl-btn min-* 44×44 CSS px(#header .icon-btn 补 top: 3px 修正 border-box 39px 内容盒几何,保证 inset-top=0 设备命中区完整);全部 22 处 :hover 包进 @media (hover: hover)(.dragging/:active 非 hover 半支保持全局)。

测试

  • Go 锚点断言:TestSidebarCollapseAnchors / TestViewportAndHeightAnchors / TestNarrowDrawerAnchors / TestTouchInteractionAnchors(served 资产文本锚点 + 块内切片 + 包含性扫描,均经评审变异验证承重)
  • node 行为测试:新增 sidebar_collapse / viewport_height / narrow_drawer / touch_interaction 四个 testdata/*.test.mjs,全仓 node --test 109/109(从真实 index.html 切片)
  • 门禁:gofmt -l internal/ 无输出、go test ./... -count=1 21 包全绿、双二进制 build OK
  • R15 红线守住:生产代码仅 internal/ui/static/index.html,无后端/协议变更

评审与仲裁记录

每期双评审的裁定、驳回项与计划文档修订均原子提交为 docs(prd): arbitration from … 提交;PR4 评审实证一次"评审 agent 限额死亡留下未还原变异"事件,据此在 §6 立了评审/合并过程房规(现场生成 diff、评审后逐字节核对、门禁在最后一次改动后跑)。

遗留(不阻塞合并)

§6 人工清单项(真机窄屏形态、软键盘、触屏拖拽手感、44px 命中区抽查含终端左上角 4.5px 压盖带点击归属)与 §8 四项暂缓(.tab/modal 族 44px 化、4px 拖拽条带触控加宽、drawer 宽度上限、.wt-action-btn 触屏不可达恢复方案)按计划在 beta 渠道收敛。

Closes #99
Refs #100

linletian and others added 19 commits October 9, 2026 21:21
…ge doc (issues #99, #100)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…lete only, sidebar git Staged/Unstaged sections (issues #99, #100)
…items as manual checks, pin down the changelog-count phrase semantics (issues #99, #100)
…ot an iframe; R4/R16 isolation rides on the input guard hitting the helper textarea (issues #99, #100)
…6 mechanism, UA [hidden] rule is HTML-namespaced, PR3 must scope the collapsed rule off the drawer (issues #99, #100)
…d, handing the terminal the full window width on demand (issue #99)
…-keyboard and collapse+keyboard items; standing rule that every §5.x acceptance item maps to a checklist or automated pin (issues #99, #100)
…the iOS soft keyboard, keeping the terminal input visible while typing (issue #100)
…768px the sidebar becomes an off-canvas drawer with a tap-to-close mask over the terminal, and at 769–1024px it narrows to a 200px cap, so iPhones and iPad Split View get a usable layout instead of a crushed terminal (issue #100)
…mplement closes the fractional-width dead zone (inert-toggle regression), drawer width cap recorded as deferred, acceptance items annotated with test/checklist mapping back to §5.1, pointer:coarse gate applies from PR4, checklist gains middle-tier / shared-key cross-device / real-device narrow items (issues #99, #100)
…and terminal controls at 44px, and use the UI without sticky hover states (issue #100)
…'s literal targets with the hit-area clip mechanism recorded (top: 3px after the r2 39px-content-box geometry correction) plus sidebar-actions/chevron consequences, §8 deferred ledger now four items including .wt-action-btn touch-unreachability as a functional consequence, R9 row split into standing touch-action vs drag-scoped select/callout, §6 gains review/merge house rules (live git-diff input, post-review byte-identity, gates after the last change), the scan-helper input-assumption rule, and the 4.5px click-ownership checklist spot-check (issues #99, #100)

@linletian linletian left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review(2 处)

生产代码仅 internal/ui/static/index.html,已逐条核对断点/级联/命名空间/包含块语义;门禁本地复跑通过(go test ./internal/ui/... 绿,node --test 109/109 绿)。position: relative 的既有元素审计、not all and (max-width: 768px) 无缝互补、xterm 自行截断按键三条论断均复核无误。以下两处是实际缺陷。

— Mavis(自动化 code review)

transition: opacity 0.2s;
}

@media (hover: hover) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.wt-actions 在触屏设备上永久不可见 —— 本次 R11 新引入的回归

.wt-item:hover .wt-actions { opacity: 1 } 被包进 @media (hover: hover),但基础规则 .wt-actions { opacity: 0 } 没有配对的 @media (hover: none) 兜底(全文件 hover: none 出现 0 次)。

  • 触发:任何上报 hover: none 的设备(iOS Safari / Android Chrome)——正是 PR3/PR4 的目标机型。
  • 改动前:点按 worktree 行会 latch :hover,"Open Terminal" / "Open Finder" 显形可点;改动后该规则整块不命中,.wt-actions 永远停在 opacity: 0。又因 opacity: 0 不拦截指针事件,两个按钮变成看不见但点得动的命中区。
  • 附带:CHANGELOG 与 docs/PRD.md 中".wt-action-btn 触屏不可达为既有行为、非 PR4 新增"的定性不成立——改动前这条显形规则对触屏同样生效,正是本次包裹导致触屏显形消失。

建议方向:基础规则一并按指针能力分流,如补 @media (hover: none) { .wt-actions { opacity: 1 } },或增加 :focus-within 显形路径。

— Mavis(自动化 code review)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

裁定:成立,已修复(c626a9a)。

你指出的事实链我们复核无误:.wt-item 行带 onclick="selectWorktree(...)"(:3160/:3183),改动前 iOS 点按会 latch :hover 使 .wt-actions 显形——这条显形路径在触屏上真实存在,R11 的 @media (hover: hover) 包裹把它移除了。因此我方 CHANGELOG/PRD 原先「触屏不可达为既有行为、非 PR4 新增」的定性错误,不可达是 R11 的直接后果,三处文档(CHANGELOG、docs/PRD.md、PRD-CHANGE §5.4-2 与 §8 ④)已全部改正。

修复分两层:

  1. 隐形命中区(本 PR 内关闭):opacity: 0 不拦命中测试,两个按钮在触屏上成了永久隐形但可点的陷阱。已在基础规则后补 @media (hover: none) { .wt-actions { pointer-events: none; } }(index.html:854-858),并配 TestTouchInteractionAnchors 块内断言。
  2. 功能恢复(维持暂缓):常驻显示 / 长按恢复显形属可见设计变更,维持 PRD-CHANGE §8 ④ 的产品决策暂缓,beta 渠道评估——不采纳 opacity: 1 常驻显形作为本 PR 的内联修复。

感谢把「既有行为」的错误定性挖出来——这正是 sticky hover 最阴险的地方:它让一条谁都没设计的交互路径看起来像"本来就能用"。

the base rule's var(--sidebar-width). */
position: absolute;
left: 0;
top: 0;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

窄屏 drawer 铺满刘海 / Home Indicator,#app 的 safe-area padding 对它无效

top: 0; bottom: 0 的定位包含块是 #app 的 padding box(PR2 刚加的 env(safe-area-inset-*) padding 同在包含块内),故 drawer 顶/底边直接落在安全区上,padding 并未内缩其内容。

  • 触发:刘海 iPhone 竖屏(≤768px、drawer 打开)——env(safe-area-inset-top) ≈ 47px,抽屉顶部 .sidebar-title(Worktrees + 新建 / 导入两个 .icon-btn)正落在状态栏 / 刘海带内;bottom: 0 同理让 sidebar 底部内容压到 Home Indicator 下。
  • 与 CHANGELOG "Home Indicator 不再遮挡内容" 的表述不符:该结论对 drawer 不成立。

建议方向:盒子保持全出血、内缩内容即可,如在窄屏块内给 #sidebar 补 padding: env(safe-area-inset-*)(mask 的 inset: 0 全出血无需改动)。

— Mavis(自动化 code review)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

裁定:成立,已按你的方案修复(c626a9a)。(与 Sisyphus 的 B2 同源,两位独立命中。)

机制复核无误:绝对定位 drawer 的包含块是 #app 的 padding box,top: 0 / bottom: 0 自 padding box 边缘起算,#app 的 safe-area padding 管不到它——刘海机竖屏下 .sidebar-title(New/Import 按钮,44px)整体落入状态栏带,直接违背 §5.3-1 的「可达」验收。

采纳你的全出血 + 内容内缩方案(优于 top/bottom 偏移:横屏时左右 inset 一并覆盖,drawer 背景延伸到屏幕边缘也符合 iOS 惯例):

  • 窄屏块 #sidebar 补 padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px)(index.html:305),全局 border-box 下 drawer 宽度仍 = var(--sidebar-width);
  • mask 的 inset: 0 按你的建议保持全出血不动(遮罩无内容,且须点任意处可关);
  • TestNarrowDrawerAnchors 补块内 env() padding 断言;
  • CHANGELOG PR3 条目与 docs/PRD.md 如实注明「刘海与 Home Indicator 不再遮挡内容」对 drawer 曾不成立;§6 人工清单新增「drawer 首行不被状态栏/刘海压盖」真机回归点。

@linletian

Copy link
Copy Markdown
Owner Author

Sisyphus 评审 PR #110(head 19173524,base develop@ad50eee)

结论先行:四期实现的质量与自洽性很高——我对真实页面做了无头浏览器行为实证(按键语义、指针拖拽、R14 默认值、断点三档、遮罩层叠、vendored xterm 的 R16 机制全部复现吻合),门禁全绿(node --test 'internal/ui/testdata/*.test.mjs' 109/109、go test ./... 全包 ok、gofmt 干净、node 用例 0 skip)。但有 1 个合并机械性拦截(与 develop 冲突)与 1 个目标机可复现的功能缺陷(窄屏 drawer/mask 逃逸 safe-area),另有 1 个可复现小缺陷与一批测试/文档精修项。建议解冲突并修 safe-area 后再合。


一、Findings(按 LoopX five-block 合同,findings first)

B1(阻塞合并·机械)与 develop 冲突,唯一冲突文件是 PRD-CHANGE.md

  • 位置/事实:gh pr view 110 → mergeStateStatus=DIRTY、mergeable=CONFLICTING;git merge-tree origin/develop HEAD 显示唯一冲突为 docs/plans/feature-v0.6.0-webui-sidebar-responsive/PRD-CHANGE.md(add/add:develop 227 行 ← docs(prd): v0.6.0 Web UI sidebar collapse + responsive/touch PRD change doc (issues #99, #100) #109 vs 本分支 241 行)。生产代码与测试无冲突。
  • 触发:任何直接 merge 都在此文件冲突停下。
  • 最小修复:merge/rebase develop 后人工合并该文档(develop 版基准 + 本分支后续仲裁编辑)。
  • 回归测试:冲突解决后 gh pr view 应回 MERGEABLE,门禁在最后一次改动后重跑(PR 房规 §6 自订)。

B2(阻塞·功能,目标机可复现)窄屏 drawer/mask 逃逸 env(safe-area-inset-*)

  • 位置:internal/ui/static/index.html:279-292(@media (max-width: 768px) 内 #sidebar { position: absolute; left: 0; top: 0; bottom: 0; z-index: 30 })、:302-308(mask inset: 0)。
  • 触发:刘海 iPhone 竖屏——PR2 刚引入 viewport-fit=cover,#app 以 padding 消费四个安全区插入值。
  • 代码路径:padding 只内缩普通流子元素(#main 实测 inset 47/34);drawer/mask 是 #app(position: relative)的绝对定位子元素,包含块是 #app 的 padding box,top:0 / bottom:0 自 padding box 边缘起算,两条安全区条带被整体覆盖。
  • 实测证据(/tmp 只读脚手架:served 静态资产字节级副本 + 无头 Chrome 143,注入 47px/34px 模拟插入):safeArea.sidebar.top=0, bottomGap=0;safeArea.main.top=47, bottomGap=34;drawer 标题行(.sidebar-title,高 44px)整体落在 0..47 状态栏带内——标题文字与 New/Import worktree 按钮在刘海机竖屏位于系统状态栏之下,不可见/不可点。
  • 后果:直接违背 PRD-CHANGE §5.3-1「iPhone 竖屏窄屏可达」验收,而 §6 人工清单无对应条目(既有「真机窄屏形态」未覆盖此几何)。
  • 最小修复:drawer 改 top: env(safe-area-inset-top); bottom: env(safe-area-inset-bottom);(mask 的 inset: 0 同理或保持全出血但至少 drawer 内容避开)。
  • 回归测试:Go 锚点断言加一条 pin(drawer 规则含 env(safe-area-inset-top)),或 §6 清单补「真机窄屏 drawer 首行是否被状态栏压盖」。

B3(不阻塞·可复现小缺陷)Ctrl/Cmd+B 守卫漏 select 与 button

  • 位置:index.html:2661-2664 的 target.closest("input, textarea, [contenteditable]")。
  • 触发:<dialog> 打开(openModal() → showModal() 焦点落首个可聚焦元素):确仔细态 dialog 首元素是 button;modal-create-wt 的 <select>(wtBaseSelect/llmProtocol/tagSelect)获焦同理。
  • 实测:焦点在 button(modal-wt-dirty)/ select(wtBaseSelect)时派发 Ctrl+B,app.classList 翻转实测 true——侧栏在模态后方盲切并持久化。代码注释称守卫职责是保护 modal 表单字段,但表单字段本就含 <select>,dialog 按钮也未被覆盖(与 R16 注释的「保护 modal 表单字段」自述不符)。
  • 最小修复:守卫选择器补 select, button(或 event.target.closest("dialog") 时直接 return)。
  • 回归测试:sidebar_collapse.test.mjs 补 select/button 宿主 guard 用例。

N1(不阻塞·测试承重力)ui_test.go 锚点存在「断言比注释弱」的六处

本轮逐条做了内存变异验证(未触碰仓库):

  1. metaEnd := strings.Index(bodyText[metaStart:], "/>")(viewport meta 切片):把 <meta … /> 写成合法 > 形式后切片蔓延全文 63,846 字节,三条 viewport 断言与 interactive-widget 禁词仍全绿——「锚在标签自身」的注释落空。修复:IndexByte(…, '>')。
  2. 中档 Contains(mid, "#sidebar {") && Contains(mid, "max-width: 200px;") 非同规则:换成 #sidebar{width:9999px} .whatever{max-width:200px} 依然绿。修复:按 drawer 同款 in-rule 切片。
  3. 宽屏补集块无负向断言:向 @media not all and (max-width: 768px) 插入 #sidebar{position:absolute} 全绿(桌面排版会全宽崩坏)。修复:禁词 ["position: absolute","translateX","inset: 0","display: block"]。
  4. .dragging / :active「保持全局」只查存在不查位置:把 .dragging 规则移进 hover 块(真实破坏触屏拖拽的 selection guard)测试仍全绿。修复:用 hover 扫描的 depth 栈断言其 hover 深度为 0。
  5. LastIndex(bodyText, "#sidebar-toggle-btn {") 定位基础规则:窄屏提升规则一旦移到文件末尾(常见整理)即误报红。修复:Index(bodyText, "\n #sidebar-toggle-btn {\n")。
  6. 44pt 禁词 stripCSSComments(bodyText) 扫全文,kinds/*.js JS 注释里的 /* 被误判为 CSS 注释起点——实测该守卫在全文 66.7% 处静默截断(当前两处 44pt 均在截断点之前故仍生效)。PRD-CHANGE §6「44pt 禁词扫描目标域是 CSS」与代码输入不符。修复:只传 <style> 切片。

N2(不阻塞·文档精度)

  1. docs/PRD.md:59 同段自相矛盾:前句「key 缺失 / 解析失败 / 抛异常一律降级为展开」是 R14 之前的旧语义,同段后文已正确表述为「R14 默认值按视口分派」;CHANGELOG.md:13 PR1 条目同样残留旧句「degrades to "expanded, not persisted"」,且「(PR3+ land later)」在四期同发的 PR 内已失效。
  2. PLAN.md:105「拖拽时 handle touch-action: none」与已合代码(常驻,index.html:520)及 R9 仲裁结论相悖。
  3. PRD-CHANGE 头「状态:草案」 vs PRD.md「已实现」 vs PLAN M5/M6 未完成——三处阶段状态不一致。
  4. 四个新 CHANGELOG 场景数(16/7/9/9,当前全部准确)系手工维护,无 issue test(ui): anchor TestTerminalStatusChangelogCount to the right CHANGELOG entry instead of the first match #95 式导出守卫;terminal_status_test.go 的计数规则未覆盖这四份新文件。
  5. R16「terminal keystrokes never reach this listener / 对终端是死路径」过强:cancel 并非无条件——本仓实证 Ctrl+B(win/mac)确实被 xterm 取消且 \u0002 入 PTY,但 Ctrl+Shift+B / Ctrl+Alt+B / macOS Cmd+B 不被取消。行为无虞(textarea 守卫兜底),但该措辞会诱导后人删守卫,建议收窄为「xterm 实际取消的按键到不了监听器」。

二、五段评审(LoopX review_template)

动机

240px 侧栏(worktree 列表 + git 面板)在 agent TUI 需要全屏时挤压 xterm 可见列,此前唯一手段是永久改布局;小修复(加按钮即可折叠)不够,因为折叠态若引入第二状态变量会与 git 折叠状态机互相污染。PR 以单一 .collapsed 类 + localStorage 单一真源解决,并按 #100 补齐移动端三层:100vh→100dvh、safe-area、iOS 软键盘钉高、≤768/769–1024/>1024 三档 drawer/收窄/并排、Pointer Events + coarse 44px + hover 门控。before/after 观察点:窄屏首访终端被 drawer 覆盖 → 默认关闭;桌面折叠后 xterm 需手动 refresh → class 后一个 rAF 内自动重排。

改动思路

入口四处,全部收敛到同一状态路径:toggleSidebarCollapse() / closeSidebarDrawer() / Ctrl+B 的 handleSidebarToggleKeydown / 首载 readSidebarCollapsed(),唯一权威状态是 sidebarCollapsed bool + #app.collapsed 类;R14 的默认值按视口分派只发生在首载(defaultSidebarCollapsedByViewport(),index.html:2544),显式持久化值恒优先。渲染形态由断点内的 CSS 决定而非 JS:折叠零宽规则用 not all and (max-width: 768px) 与窄屏块无缝互补,闭掉 (768,769) 分数死区(桌面 200% 缩放下 768.5 CSS px 可达,PR3 若用 min-width:769px 会引入视觉惰性 toggle 回归)。高度由 applyVisualViewportHeight(index.html:2691)独立钉制,与折叠类写不同的两个 inline/类槽位,互不相交。拖拽从 mouse 迁 Pointer Events(startSidebarResize/stopSidebarResize,3017/3067),capture + re-entry guard + clamp 数学逐字未动。

具体改动(关键代码讲解)

  • handleSidebarToggleKeydown(index.html:2647):repeat/altKey(AltGr) 拒入,key.toLowerCase()==='b' 兼容 Caps/Shift,target 命中 input/textarea/[contenteditable] 放行;命中路径 preventDefault() 防止浏览器书签栏。修正项 B3 在此。
  • readSidebarCollapsed/defaultSidebarCollapsedByViewport(2524/2544):localStorage 首次使用,读/写双向 try/catch,缺 key / 解析失败 / 抛异常一律走视口默认值,不持久化。实证:宽屏 seed=true → collapsed;窄屏无 seed → collapsed(R14);窄屏 seed=false → 显式值胜出走 drawer。
  • applyVisualViewportHeight(2691)+ reapplyPinnedVisualViewportHeight(2735):vv.scale≠1 退出钉高;哨兵 documentElement.clientHeight(非 innerHeight,避开经典滚动条槽);scroll 仅在已钉高时重钉,纯平移不引入钉高;无 visualViewport 零操作。
  • 三档 CSS(index.html:250-364):not all and (max-width:768px) 限定桌面零宽折叠(四声明逐字保留)+ visibility:hidden 移出 Tab 序;窄档 drawer position:absolute + translateX(-100%) + mask(display:none 全局 / 窄档且展开时 block)+ 40/30/25 层叠且按钮提升仅限窄档;中档 max-width:200px 与 width 异属性组合 min(0,200px)=0。B2 缺陷在此区间。
  • Pointer 拖拽(3017-3084):setPointerCapture try/catch NotFoundError、pointercancel 同 stop、重入守卫防基线劫持;实测 pointerdown/move/up 全链有效(+40px → sidebar-top-panel 416px,class 增删正确),旧 mousedown 路径不再驱动。

对主干的风险

  • 生产代码面收敛良好(R15 红线成立:仅 index.html,无后端/协议变更),#app 加 position:relative 的包含块影响经审计为零(#connection-overlay fixed、#status-bar/#terminal-controls/#empty-state 各有更近 relative 祖先、xterm host 挂 #terminal-container——逐条复核属实)。
  • 主要风险不是回归而是验收盲区:B2 说明 safe-area 的验收只覆盖了流式子元素,绝对定位新形态未覆盖——同类新增绝对定位元素时会再踩;N1 说明若干"结构性断言"实际是字符串包含,未来重构可能以绿测试携带真回归。
  • 已知并显式接受项(§8 ledger 四项)核对属实,确未提前实现:.tab/通用按钮/modal 族 44px、4px 条带加宽、drawer 宽度上限、.wt-action-btn 触屏不可达(.wt-item:hover .wt-actions 在 hover 块内,纯触屏保持 opacity:0)。
  • 回滚成本低:生产面单文件,按 commit 分期 revert 即可;冲突文件纯文档不影响回滚判断。

我的整体评价

verdict: COMMENT(不建议当前 head 直接合并——需先解 B1 冲突、修 B2)。证据:head 19173524 与本地 HEAD 字节一致、结论出具前再次复核远端 head 未变;门禁复现(109/109 需 glob/显式文件形式——node --test internal/ui/testdata/ 目录形参数在 Node v24.19.0 报 MODULE_NOT_FOUND,是调用形式问题而非仓库缺陷;CI 用的 glob 形式通过)。22 处 hover 全包裹、z-index 40/30/25、四声明逐字保留、\u0002 机制均由我与两个并行只读核查(测试承重性 + 文档声称一致性)双向确认。重审所需:解冲突后的新 head + B2 修复 + B3 的最小守卫修补(一行)。


附:验证方法(只读,未改仓库任何文件)

  • /tmp 脚手架:复制 served 静态资产字节级副本(含 vendor xterm),本地 HTTP 起服,无头 Chrome 143 真实渲染;所有注入(localStorage 预置、模拟 safe-area padding、connection-overlay 屏蔽、事件派发)只作用于 /tmp 副本。
  • 实测矩阵:宽 1280 / 中 900 / 窄 375 三档 × {无 key, seed=true, seed=false};Ctrl/Cmd+B × {body, input, Ctrl+Alt, repeat, Caps/Shift, Ctrl+V};pointer 拖拽 +40px;mask 点击关闭并持久化;窄档 drawer 开启时 toggle 按钮命中与可点。
  • R16 实证:合成 keyCode=66 的 Ctrl+B 派发到 xterm helper textarea → document 处理器 docSaw=0、defaultPrevented=true、PTY 收到 "\u0002"(与 vendored 源码路径 evaluateKeyboardEvent → triggerDataEvent → cancel(e,!0) 一致)。
  • 并行只读核查:①测试承重性与健壮性(含门禁复现与内存变异验证);②文档声称 vs 代码一致性(§5/§8/CHANGELOG 房规逐条映射)。

—— Sisyphus(评审仅做只读分析,未向本仓库写入任何改动)

…ui-sidebar-responsive — PRD-CHANGE.md add/add resolved by taking the branch version: develop's blob (PR #109 squash) is byte-identical to this branch's ancestor 6b12485, so ours is a strict superset and no content is lost
… content from the safe areas (full-bleed box + env() padding, the absolutely-positioned drawer's containing block is #app's padding box), Ctrl/Cmd+B no longer blind-toggles the sidebar while a modal dialog hosts the focus, and the .wt-actions buttons lose their invisible-but-clickable hit area on pure touch via (hover: none) pointer-events: none; test anchors hardened (meta-tag slice, in-rule mid-tier assertion, negative words in the wide complement block, hover-depth-0 for the global halves, first-occurrence toggle anchor, style-slice-scoped 44pt scan) and the changelog scenario counts gain an issue #95-style guard across the four new testdata files (issues #99, #100)
…plemented four phases, R9 wording aligned in PLAN (standing touch-action vs drag-scoped select/callout), .wt-action-btn touch-unreachability recharacterized as PR4-new (the sticky-hover reveal path R11 removed) with the pointer-events trap closure recorded, §6 checklist gains the drawer safe-area regression spot, the scan-helper rule updated for the style-slice-scoped 44pt scan, and the R16 xterm-cancel claims narrowed to data-producing keys after re-verifying the vendored source (issues #99, #100)
@linletian

Copy link
Copy Markdown
Owner Author

对 Sisyphus 评审的逐条回复(修复已推送:dd5319d merge 解冲突 → c626a9a 代码修复 → e60c56c 文档仲裁)

先致谢:无头浏览器实证矩阵(三档 × 三种 seed、R16 按键路径、safe-area 注入)的质量非常高,B2 正是靠这套实证才暴露的——它同时说明 PR4 立的「UA 显隐断言原则」房规仍然是对的:属性层正确 ≠ 渲染层正确。

B1(与 develop 冲突)— 已解决(dd5319d)。 merge origin/develop;唯一冲突文件 PRD-CHANGE.md 按证据解:develop 侧 blob(PR #109 squash)与本分支祖先提交 6b12485 逐字节相同,本分支版本是其严格超集(含 PR1–PR4 全部仲裁编辑),故整体取本分支版本,零信息丢失。mergeStateStatus 已回 MERGEABLE。

B2(drawer/mask 逃逸 safe-area)— 裁定成立,已修复。 与 Mavis 的 inline 同源(两位独立命中)。采纳 Mavis 的全出血 + 内容内缩方案而非 top/bottom 偏移——理由:① top/bottom 偏移只覆盖竖屏上下 inset,横屏左右 inset(drawer 在左侧时压刘海)漏掉;② drawer 背景延伸到屏幕边缘是 iOS 惯例,视觉完整;③ 与 PR2 #app 的 padding 消费策略同构。落点:窄屏块 #sidebar 补四值 env(safe-area-inset-*) padding(index.html:305,border-box 保宽度);mask inset: 0 保持全出血(无内容、点任意处可关);TestNarrowDrawerAnchors 补块内断言;§5.3-1 验收与 §6 人工清单各补一条对应项(遵守自订的验收-清单对应规则)。

B3(Ctrl/Cmd+B 守卫漏 select/button)— 裁定成立,采纳 closest("dialog") 方案(比补枚举更彻底)。 理由:补 select, button 只覆盖当前控件清单,dialog 祖先判定覆盖 modal 前台的全部现有与未来宿主,且语义更准——modal 前台时侧栏在后方不可见,盲切无反馈本身就是问题。现有 input/textarea/contenteditable 守卫保留作防御深度。sidebar_collapse.test.mjs 补 dialog 内 button/select 宿主用例(计数 16→17,CHANGELOG 措辞与新增守卫同步)。

N1(六处测试承重)— 全部采纳,逐一修复(均在 ui_test.go)。 ① meta 切片 IndexByte('>');② 中档改 in-rule 切片;③ 宽屏补集块补负向禁词(position: absolute/translateX/inset: 0,已先核实块内现为零命中);④ .dragging/:active 复用 hover 扫描 depth 栈断言深度 0;⑤ #sidebar-toggle-btn 改首个 8 空格精确锚;⑥ 44pt 禁词改传 <style> 切片——⑥与 PR4 r3 mcode 报的 stripCSSComments 静默截断同根(双侧独立复算 50.3%/66.7% 互证),当时我们立了房规未改代码,这次连代码一起加固,§6 房规文本已同步更新。

N2(五条文档精度)— 全部采纳。 ① CHANGELOG:13 与 docs/PRD.md:59 的 R14 前旧语义(「一律降级为展开」)改为视口分派默认值,「(PR3+ land later)」失效句删除;② PLAN.md:105 的 R9 措辞与 PRD-CHANGE R9 行仲裁终稿对齐(touch-action 常驻 vs user-select 拖拽态);③ PRD-CHANGE 头部状态「草案」→「已实施」(M5 合并未发生,里程碑表不虚构);④ 新增 TestResponsiveLayerChangelogCounts 守卫,四个新 testdata 文件的 CHANGELOG 场景数措辞与 grep -c '^test(' 逐一绑定(issue #95 房规扩展);⑤ R16「无条件 cancel / 死路径」措辞收窄——我们按你的提示复读 vendored xterm.js 核实:evaluateKeyboardEvent 对 Ctrl+B 产 \x02,_keyDown 在发送数据后(及 Tab/Enter/Esc 的 cancel 标志路径)调 cancel(e,!0),Ctrl+Shift+B / Ctrl+Alt+B / macOS Cmd+B 不产生数据不被取消。CHANGELOG、docs/PRD.md、PRD-CHANGE(§1 现状表、R4 行、§4 守卫小节)与 index.html 内 R16 注释五处统一改为「产生数据的按键被 xterm 取消、到不了父监听器;不产生数据的组合不被取消,父文档守卫兜底」。

对你整体评价两点回应:「主要风险是验收盲区而非回归」——同意,B2 的落档(§5.3-1 注解 + §6 清单项)就是把盲区转成条目;「N1 说明若干结构性断言实为字符串包含」——六处已修,且你建议的变异验证方法(内存变异、不触仓库)本身就是评审纪律的好示范,与我们 §6 房规②(评审后工作树逐字节核对)方向一致。

门禁(最后一次改动之后实跑,房规③):gofmt -l internal/ 无输出;go test ./... -count=1 21 包全 ok;node --test internal/ui/testdata/*.test.mjs 110/110(109 + B3 新增 1 例)。

@linletian linletian left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第二轮完整评审

上轮两条裁定复核:均已修复,且修复方案正确——

  • B2 drawer 安全区:窄屏块 #sidebar 补四值 env() padding 成立。绝对定位 drawer 的包含块是 #app 的 padding box(padding 区在内),top:0/bottom:0 自 #app 边框盒内缘起算,确会跨越安全区条带;盒子全出血 + 内容内缩的方案正确,border-box 下宽度仍为 var(--sidebar-width),mask 保持 inset: 0 不内缩亦正确。
  • M1 .wt-actions:@media (hover: none) { pointer-events: none } 精确关掉隐形命中区(opacity 继承不影响子元素命中,.wt-action-btn 未自设 pointer-events),且不触碰桌面 hover 路径。§8 ④ 的功能恢复维持暂缓,裁定合理。
  • B3 modal 防盲切:target.closest("dialog") 守卫成立——原生 <dialog> + showModal() 下焦点锁在 dialog 内,15 个 modal 的 select/button 均被覆盖;<dialog> 在 top layer,不受新 z-index 40 影响。

门禁本地复跑:gofmt -l internal/ 无输出;go test ./internal/ui/... -count=1 绿;node --test 110/110 绿(较上轮 +1)。

新发现 1 条(承重机制与 vendored 源码不符,见下)。

— Mavis(自动化 code review)

//
// R16, stated against the real mechanism (verified in the vendored
// source, static/vendor/xterm/xterm.js): xterm's _keyDown runs
// evaluateKeyboardEvent and then cancel(e, !0) — unconditional

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R16 承重机制与 vendored 源码不符:Ctrl+B 并没有被 xterm 截断

  • evaluateKeyboardEvent 的 default: 支对纯 Ctrl+字母(keyCode 66,无 shift/alt/meta)走 else:o.key = String.fromCharCode(66-64) = \u0002,而 o.cancel 保持 false——「发数据」与「置 cancel 标记」在 xterm 里是两个独立字段。
  • _keyDown 仅在 i.cancel 为真(或 type 2/3)时调 cancel(e,!0),而 textarea 上的监听器是 (e=>this._keyDown(e)),丢弃返回值。

故 Ctrl+B 的 keydown 既未 preventDefault 也未 stopPropagation,照常冒泡到本监听器;真正兜住「终端内 Ctrl+B 不折叠侧栏」的是下方 input, textarea, [contenteditable] 守卫——xterm 的 helper 元素正是 document.createElement("textarea")(.xterm-helper-textarea)。

影响:当前行为恰好正确,但「这些按键死在 xterm 内、到不了父文档监听器」的说法已扩散五处——本注释与 :2696、CHANGELOG.md:13、docs/PRD.md:59、PRD-CHANGE.md:78(明写「父文档监听器在终端场景不被调用」)及 §6 人工清单,以及 sidebar_collapse.test.mjs:338-344(明写该守卫「NOT a model of the terminal path」)。照此叙述清理(删掉 textarea 守卫)会让终端内 Ctrl+B 直接折叠侧栏。附带:终端路径在守卫处提前 return,event.preventDefault() 同样不执行,故「避免 Ctrl+B 唤出浏览器书签栏」的意图在终端聚焦时并不成立。

建议方向:R16 叙述改为「Ctrl+B 在 xterm 中只置 key 不置 cancel,事件照常冒泡,承重的是 textarea 守卫」,上述五处 + 测试注释同步修订。

— Mavis(自动化 code review,第二轮)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

裁定:不成立(驳回),现有 R16 叙述与 vendored 源码一致,无需改动。 你抓的两个子点里有一个是真的,但关键推断漏了 _keyDown 数据发射路径尾部的 cancel 调用。逐层给证据(minified bundle 原文):

① 你说对的部分:evaluateKeyboardEvent 对纯 Ctrl+字母确实只置 key 不置 cancel——末支 else e.keyCode>=65&&e.keyCode<=90 ? o.key=String.fromCharCode(e.keyCode-64) : …,o.cancel 保持默认 false。这点与我们一轮修复时核实并写入文档的表述一致(「evaluateKeyboardEvent 对 Ctrl+B 产 \x02 但不置 cancel 标志」)。

② 但 _keyDown 的取消不只走 i.cancel 支。bundle 原文(cond 链展开):

i.cancel && this.cancel(e,!0),                    // ← 你看到的支,Ctrl+B 不命中
!i.key || !!(e.key && !e.ctrlKey && !e.altKey && !e.metaKey && …) || (
    …,
    this._onKey.fire({key:i.key, domEvent:e}),
    this._showCursor(),
    this.coreService.triggerDataEvent(i.key,!0),   // ← \^B 在此入 PTY
    !this.optionsService.rawOptions.screenReaderMode || e.altKey || e.ctrlKey
        ? this.cancel(e,!0)                        // ← 数据发完后这里取消 DOM 事件
        : void(this._keyDownHandled=!0))

Ctrl+B 走数据发射路径:triggerDataEvent(\x02) 之后,因 e.ctrlKey 为真(三元条件右侧成立),落到 this.cancel(e,!0)。所以「_keyDown 仅在 i.cancel 为真(或 type 2/3)时调 cancel」不成立——数据路径末尾还有一次。

③「监听器丢弃返回值」是误读:cancel 不是纯函数,bundle 原文 cancel(e,t){if(this.options.cancelEvents||t) return e.preventDefault(),e.stopPropagation(),!1}——t=!0 时 preventDefault + stopPropagation 是调用本身的副作用,与 (e=>this._keyDown(e)) 是否使用返回值无关。

④ 与实测互证:Sisyphus 一轮在本仓做的活体派发(合成 keyCode=66 Ctrl+B → helper textarea)测得 defaultPrevented=true、document 处理器 docSaw=0、PTY 收到 \^B——与上述源码路径(发数据→cancel)精确吻合。

⑤ 附带点也不成立:「终端聚焦时 preventDefault 不执行、书签栏会唤出」——正因 xterm 自己 preventDefault 了,浏览器默认动作(书签栏)在终端聚焦时根本不会触发;我方守卫的 event.preventDefault() 注释覆盖的是事件确实能到达监听器的非终端路径(如焦点在 body)。

现行叙述为何不动:e60c56c 后的五处统一表述是「产生数据的按键(如 Ctrl+B → \x02)由 xterm 在发送数据后自行 cancel(e,true),这些按键到不了父文档监听器;不产生数据的组合(Ctrl+Shift+B / Ctrl+Alt+B / macOS Cmd+B)不被取消,父文档守卫是其兜底」——与 ②③ 的机制逐字吻合,且已经把你担心的「删守卫」方向堵死(守卫被明确定位为不产生数据组合的承重兜底,文档多处强调保留)。sidebar_collapse.test.mjs:338-344 的「NOT a model of the terminal path」在该机制下同样准确:终端路径根本到不了守卫,textarea 用例是对非终端 textarea 宿主与 xterm 行为假设变化的防御性覆盖。

若你有与 ④ 相反的实测(例如在你的环境里 Ctrl+B 于终端聚焦时真的冒泡到了 document),欢迎贴出来——那将指向 bundle 版本差异或浏览器差异,值得再开一轮。静态阅读层面,现有叙述与 vendored bundle 一致。

@linletian

Copy link
Copy Markdown
Owner Author

Sisyphus 第二轮评审 PR #110(head e60c56c,base develop@ad50eee,出具前复核远端 head 未变)

结论先行:第一轮全部发现(B1/B2/B3/N1×6/N2×5)均已修复并经本独立复核证实;另有两个超范围加固(.wt-actions 命中区封堵、CHANGELOG 计数守卫)。无阻断项,仅存 3 处 nit。本轮同样全程只读——仓库工作树零改动,所有实验(含 9 个变异)都在 /tmp 的克隆/脚手架上进行。

一、第一轮 findings 的处置复核

轮次 项 处置 本轮独立验证方式 结果
B1 develop 冲突 dd5319d merge 解 blob 三角验证 ✅ 证实
B2 drawer/mask 逃逸 safe-area 全出血盒 + 四值 env() 内容内缩(index.html:305) 无头 Chrome 注入模拟 inset 实测 ✅ 修复生效
B3 Ctrl/Cmd+B 守卫漏 select/button closest("dialog") 守卫前置(index.html:2688-2690) 焦点元素派发冒泡实测 + mjs 17 例 ✅ 修复生效
N1 六处测试承重 逐项加固 9 个变异逐一复测 ✅ 8/9 证实承重,1 项精确定性
N2 五条文档精度 逐条订正 文本比对 + 新守卫实跑 ✅ 全部落实

B1 证实(不只是"看状态"):origin/develop 与分支祖先 6b12485 的 PRD-CHANGE.md blob 哈希同为 e001df8a(227 行,逐字节相同);HEAD 该文件为 d6a134a(241 行,含 PR1–PR4 全部仲裁编辑 + 本轮修订)——"ours 为严格超集、零信息丢失"的合并声明属实,develop..HEAD 文件集仍为原 19 个,无其他 develop 内容混入。

B2 证实(几何实测,模拟 47px/34px 刘海 inset 覆盖 drawer padding):drawer 盒 top=0 / bottomGap=0 保持全出血,.sidebar-title 顶边 0 → 47(内容被内缩出状态栏带),注入左 inset 47px 时标题左边 0 → 47(横屏刘海侧同覆盖)。mask 未加 padding(全出血保持,任意位置点按可关)。与 New/Import 按钮不再被压盖的验收一致;§5.3-1 注解与 §6 人工清单均已补对应条目。方案评审:选"全出血 + 内容内缩"而非 top/bottom 偏移是对的——横屏左右 inset 只有内容内缩能覆盖,且与 PR2 #app 的 padding 策略同构;mask 无内容不加 padding 的理由(点任意处关闭)也成立。

B3 证实,并主动披露一个我第一轮的方法缺陷:本轮我把 Ctrl+B 事件派发到焦点元素上让其冒泡——dialog 内 button 焦点、select 焦点均 toggled=false(body 对照仍 true)。需要坦白:第一轮我的实证把事件 dispatch 在 document 上(target=document),那种派发根本不可能命中 dialog 守卫,所以"实测 true"不能作为 B3 的行为证据;B3 的真实性是靠代码阅读成立的(旧守卫选择器确实不匹配 button/select),结论无误但证据链有瑕疵。本轮以正确派发路径补正,且 closest("dialog") 方案优于补枚举——覆盖 dialog 内全部现有与未来宿主。

N1 变异复测(/tmp 克隆内逐条变异后跑对应 Go 测试):

  1. 中档改拆分规则(#sidebar{width:9999px} + 无关 .whatever{max-width:200px})→ FAIL(in-rule 断言命中)✅
  2. 宽屏补集块插入 #sidebar{position:absolute} → FAIL(负向禁词 position: absolute)✅
  3. .dragging 规则移入 hover 块 → FAIL(hover depth 0 断言 + 行号)✅
  4. 基础规则注入 z-index → FAIL(8 空格锚点选中正确的规则)✅;追加诱饵第三条 toggle 规则 → 计数守卫 FAIL(锚点为双保险)
  5. CSS 末尾注入 width: 44pt → FAIL(禁词仍工作);JS 行注释写 kinds/*.js 触发旧截断因子的场景 → PASS(style 切片作用域,不再有静默截断/误触发面)✅
  6. meta />→> 精确定性命中:旧 /> 锚点在无斜杠 tag 下实测切片 65,913 字节(覆盖整个 <style> 及之后,断言空转),新 IndexByte('>') 锚点实测 88 字节(恰为标签本身)——该修复消除的是"空转切片"而非改变裁决(内容完好时两者皆绿),如实记档。
  7. CHANGELOG 把 "seventeen" 改回 "sixteen" → FAIL(新 TestResponsiveLayerChangelogCounts 守卫)✅

N2 文档复核:PRD.md:59「一律降级为展开」与 R14 视口分派的自相矛盾已消除(同段现一致表述为视口分派默认值);PLAN.md:105 已改为「常驻 + 拖拽态」的仲裁措辞;PRD-CHANGE 头「已实施」、M5/M6 未虚构;CHANGELOG PR1 条目旧句("degrades to 'expanded, not persisted'"、"(PR3+ land later)")已清;R16 措辞五处统一收窄。特别肯定 §8 ④ 的重新定性——「不可达是 PR4 新增的功能性后果」比原来的「既有行为」更诚实,且如实记录了 (hover: none) 只封堵命中区、不恢复显形(产品决策仍挂起)。

M1 超范围加固(.wt-actions 命中区)复核:@media (hover: none) { .wt-actions { pointer-events: none; } }(index.html:854-858)+ in-rule 锚点。语义核查:opacity:0 确实不拦命中测试,纯触屏上两个不可见按钮可被误点(开终端/Finder)——这个坑第一轮我只当作"已接受的后果"记录,作者直接堵了,属于超出评审要求的正确加固。hybrid 设备(触屏笔记本)主指针为鼠标 → hover:hover 匹配 → 该块不生效,无误伤。

二、门禁与实证(全部亲跑)

项 结果
gofmt -l internal/ 空(exit 0)
go test ./... -count=1 21 个测试包全 ok + 3 no-test-files,0 FAIL
node --test 'internal/ui/testdata/*.test.mjs' 110/110(109 + B3 新增 1 例,0 skip)
CHANGELOG 计数 vs grep -c '^test(' 17 / 7 / 9 / 9 四项逐一相符,且有守卫绑定
窄屏默认(R14)/ 显式 seed 双向 / 中档 200px / 宽屏持久化 实测全部符合
drawer 全出血 + 内容内缩 / 横屏左 inset / mask 全出血 实测符合
dialog 内 button·select 焦点 Ctrl+B 不切换;body 仍切换;input 守卫仍拦 实测符合
mask 点按关闭 + 持久化;toggle 在遮罩上可点(命中测试命中 chevron) 实测符合
pointer 拖拽(+40px → 416px、class 增删、pointerup 清理) 实测符合
R16 机制复测 Ctrl+B 终端内:xterm 取消(preventDefault=true)、document 处理器 docSaw=0、PTY 收 \u0002;Ctrl+Shift+B 终端内:不被取消、冒泡触发切换——与新版五处文档描述逐条吻合

三、残余 nit(均不阻断)

  1. index.html:2687 注释 "(15 of them)" 与实际不符:全文件 <dialog> 元素共 10 个(8 个带 id + 2 个匿名),无动态创建。纯注释口误,不影响行为;按本仓库的字字计较惯例顺手改掉。
  2. 新增负向禁词走的是原始文本(未剥注释):宽屏补集块的 position: absolute/translateX/inset: 0 三禁词与 hover:none 块的 in-rule 断言都在含注释文本上做——将来若有人在宽屏块注释里提一句"绝对定位"这类字样会误报红(44pt 禁词已正确地限定 <style> 切片 + 剥注释,规则切片仍继承这一不对称)。一行修复:这些切片先过 stripCSSComments。
  3. sidebar_collapse.test.mjs B3 用例的 for (const kind of ["button","select"]) 循环体两种 kind 用同一 stub,按钮/select 的差异未真正参数化(语义覆盖靠 dialogHost stub 已达成,纯代码整洁)。

四、第二轮五段复核(LoopX review_template)

动机:未变——审查确认与原评审一致(侧栏挤压 xterm 可见列、四期响应式落地)。新增的两个加固各自动机成立:B2 是对"验收盲区"(绝对定位新形态未覆盖于既有验收)的补课;.wt-actions 封堵源于 R11 改动的连带后果诚实化。

改动思路:三个修复均遵循最小侵入且与既有架构同构——B2 复用 PR2 #app 的 env() padding 消费策略(同一个宣告模式、同一套 border-box 语义)而非引入偏移机制;B3 用 dialog 祖先判定取代控件枚举(覆盖未来宿主,语义就是"前台模态优先");hover:none 块用媒体查询而非 JS(与 R11 同手段)。测试加固全部走 in-rule/负向/深度断言,与文件既有锚点风格一致。

具体改动(本轮新增的行为承重符号):

  • index.html:305 drawer 四值 env(safe-area-inset-*) padding——包含块是 #app padding box,故只能由 drawer 自身内缩;盒保持 full-bleed。
  • index.html:854-858 @media (hover: none) { .wt-actions { pointer-events: none } }——opacity 不拦命中的推论的正确修法。
  • index.html:2686-2690 dialog 守卫前置——showModal() 下焦点在模态内,closest("dialog") 一票否决。
  • ui_test.go:669 IndexByte('>')、:880-885 宽档禁词、:909-916 drawer env 断言、:964-980 基础规则 8 空格锚、:1080-1101 中档 in-rule、:1266-1365 hover:none 块 + depth-0 扫描、style 切片 44pt(:1283-1290);terminal_status_test.go 新增 TestResponsiveLayerChangelogCounts。

对主干的风险:生产代码面仍为单文件 index.html(R15 红线保持),本轮 +85 行 CSS/JS 均为新增规则/守卫,无删除性改动;三个修复都只收窄条件(内容内缩/守卫否决/命中封堵),桌面与宽屏路径实测零变化。剩余风险集中在文档-代码同步的维护性(nit 1-2 同类)。

我的整体评价:verdict: COMMENT,无阻断项,可合并。理由:B1/B2/B3/N1/N2 全部 closes 且各带独立复证;门禁全绿且 head 与远端一致(e60c56c,MERGEABLE/CLEAN);两个超范围加固方向正确。重审无需补充证据;三条 nit 可随缘或后续 PR 处理。特别致谢两点方法论贡献:①回复中对 B2 的方案比选(全出血+内容内缩 vs 偏移)给出了横屏场景的决定性理由;②第一轮 B3 的证据瑕疵由作者以正确路径(mjs 焦点宿主用例)闭环,我这边也以修正后的探针派发方式补正了实证。

附:本轮评审方法(只读)

  • 仓库工作树 git status 零改动;差异分析基于 git diff 1917352..e60c56c;9 个变异实验在 /tmp 的仓库克隆(cp -a)中进行,每次变异后 git checkout 还原。
  • 无头 Chrome 143 脚手架:/tmp 复制 served 静态资产 + 本地 HTTP;safe-area 用注入 !important padding 模拟 env() 解析值;B3 用焦点元素派发冒泡(修正第一轮 target=document 缺陷);R16 用真实 vendored xterm + keyCode 补全的合成事件复测。

—— Sisyphus(第二轮评审,仅只读分析,未向本仓库写入任何改动)

…nd responsive tiers — self-contained mock replicating the shipped mechanics (chevron/Ctrl+Cmd+B toggle with localStorage persistence, three viewport tiers with the narrow drawer and mask, R14 viewport-dispatched default, Pointer Events split drag with capture and clamp, hover gating with the (hover: none) hit-trap closure, coarse 44px targets, dialog-front guard) (issues #99, #100)
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