Repository navigation
feat(ui): v0.6.0 Web UI responsive — sidebar collapse, dynamic viewport, narrow-viewport drawer, touch interaction (issues #99, #100) - #110
Conversation
…ge doc (issues #99, #100) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…invariant UI principle
…d, handing the terminal the full window width on demand (issue #99)
…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
left a comment
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
.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)
There was a problem hiding this comment.
裁定:成立,已修复(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 ④)已全部改正。
修复分两层:
- 隐形命中区(本 PR 内关闭):
opacity: 0不拦命中测试,两个按钮在触屏上成了永久隐形但可点的陷阱。已在基础规则后补@media (hover: none) { .wt-actions { pointer-events: none; } }(index.html:854-858),并配TestTouchInteractionAnchors块内断言。 - 功能恢复(维持暂缓):常驻显示 / 长按恢复显形属可见设计变更,维持 PRD-CHANGE §8 ④ 的产品决策暂缓,beta 渠道评估——不采纳
opacity: 1常驻显形作为本 PR 的内联修复。
感谢把「既有行为」的错误定性挖出来——这正是 sticky hover 最阴险的地方:它让一条谁都没设计的交互路径看起来像"本来就能用"。
| the base rule's var(--sidebar-width). */ | ||
| position: absolute; | ||
| left: 0; | ||
| top: 0; |
There was a problem hiding this comment.
窄屏 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)
There was a problem hiding this comment.
裁定:成立,已按你的方案修复(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 首行不被状态栏/刘海压盖」真机回归点。
Sisyphus 评审 PR #110(head
|
… 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)
对 Sisyphus 评审的逐条回复(修复已推送:
|
linletian
left a comment
There was a problem hiding this comment.
第二轮完整评审
上轮两条裁定复核:均已修复,且修复方案正确——
- 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 |
There was a problem hiding this comment.
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,第二轮)
There was a problem hiding this comment.
裁定:不成立(驳回),现有 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 一致。
Sisyphus 第二轮评审 PR #110(head
|
| 轮次 | 项 | 处置 | 本轮独立验证方式 | 结果 |
|---|---|---|---|---|
| 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 测试):
- 中档改拆分规则(
#sidebar{width:9999px}+ 无关.whatever{max-width:200px})→ FAIL(in-rule 断言命中)✅ - 宽屏补集块插入
#sidebar{position:absolute}→ FAIL(负向禁词position: absolute)✅ .dragging规则移入 hover 块 → FAIL(hover depth 0 断言 + 行号)✅- 基础规则注入
z-index→ FAIL(8 空格锚点选中正确的规则)✅;追加诱饵第三条 toggle 规则 → 计数守卫 FAIL(锚点为双保险) - CSS 末尾注入
width: 44pt→ FAIL(禁词仍工作);JS 行注释写kinds/*.js触发旧截断因子的场景 → PASS(style 切片作用域,不再有静默截断/误触发面)✅ - meta
/>→>精确定性命中:旧/>锚点在无斜杠 tag 下实测切片 65,913 字节(覆盖整个<style>及之后,断言空转),新IndexByte('>')锚点实测 88 字节(恰为标签本身)——该修复消除的是"空转切片"而非改变裁决(内容完好时两者皆绿),如实记档。 - 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(均不阻断)
index.html:2687注释 "(15 of them)" 与实际不符:全文件<dialog>元素共 10 个(8 个带 id + 2 个匿名),无动态创建。纯注释口误,不影响行为;按本仓库的字字计较惯例顺手改掉。- 新增负向禁词走的是原始文本(未剥注释):宽屏补集块的
position: absolute/translateX/inset: 0三禁词与 hover:none 块的 in-rule 断言都在含注释文本上做——将来若有人在宽屏块注释里提一句"绝对定位"这类字样会误报红(44pt 禁词已正确地限定<style>切片 + 剥注释,规则切片仍继承这一不对称)。一行修复:这些切片先过stripCSSComments。 sidebar_collapse.test.mjsB3 用例的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:305drawer 四值env(safe-area-inset-*)padding——包含块是#apppadding box,故只能由 drawer 自身内缩;盒保持 full-bleed。index.html:854-858@media (hover: none) { .wt-actions { pointer-events: none } }——opacity 不拦命中的推论的正确修法。index.html:2686-2690dialog 守卫前置——showModal()下焦点在模态内,closest("dialog")一票否决。ui_test.go:669IndexByte('>')、:880-885宽档禁词、:909-916drawer env 断言、:964-980基础规则 8 空格锚、:1080-1101中档 in-rule、:1266-1365hover: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 用注入!importantpadding 模拟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)
概述
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)多轮复核后原子提交。分期内容
#app单一.collapsed类驱动(#sidebarwidth: 0+overflow: hidden+visibility: hidden),#header左侧 chevron 开关(<</>>),localStorage持久化(mw.ui.sidebarCollapsed),Ctrl/Cmd+B快捷键(含 xterm 自行截断按键的机制核实),折叠后 xterm 按序重排。100vhfallback 经@supports渐进增强为100dvh;宽度100%取代100vw;viewport-fit=cover+ 四向env(safe-area-inset-*)padding;iOS 软键盘由visualViewport钉高补偿(不引入interactive-widget),捏合/双击缩放退出钉高。.collapsed类驱动两种形态;PR1 折叠规则限定进无缝互补的@media not all and (max-width: 768px)(堵住分数宽度死区回归缝);遮罩点按关闭走同一状态路径;z-index 分层 40>30>25 仅限窄屏;tab 栏scroll-snap;R14 窄屏首访默认关闭 drawer(显式持久化值永远优先)。setPointerCapture+pointercancel+ 重入守卫,钳制数学逐字未动,桌面鼠标路径零回归);@media (pointer: coarse)下.icon-btn/.term-ctrl-btnmin-* 44×44 CSS px(#header .icon-btn补top: 3px修正 border-box 39px 内容盒几何,保证 inset-top=0 设备命中区完整);全部 22 处:hover包进@media (hover: hover)(.dragging/:active非 hover 半支保持全局)。测试
TestSidebarCollapseAnchors/TestViewportAndHeightAnchors/TestNarrowDrawerAnchors/TestTouchInteractionAnchors(served 资产文本锚点 + 块内切片 + 包含性扫描,均经评审变异验证承重)sidebar_collapse/viewport_height/narrow_drawer/touch_interaction四个testdata/*.test.mjs,全仓node --test109/109(从真实 index.html 切片)gofmt -l internal/无输出、go test ./... -count=121 包全绿、双二进制 build OKinternal/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