Skip to content

docs(prd): v0.6.0 Web UI sidebar collapse + responsive/touch PRD change doc (issues #99, #100) - #109

Merged
linletian merged 2 commits into
developfrom
feature/v0.6.0-webui-sidebar-responsive
Oct 9, 2026
Merged

linletian merged 2 commits into
developfrom
feature/v0.6.0-webui-sidebar-responsive

Conversation

@linletian

Copy link
Copy Markdown
Owner

Summary

Drafts the consolidated PRD change document for issues #99 (desktop sidebar collapse/expand) and #100 (responsive layout + touch, iOS/iPadOS), to be carried by the v0.6.0 development branch. Placed under docs/plans/feature-v0.6.0-webui-sidebar-responsive/; the main docs (docs/PRD.md, docs/API.md, docs/ARCHITECTURE.md) are untouched — §9 of the doc lists the sync checklist for the implementing PRs.

Analysis (what the doc records)

The two issues are one feature, not two. #100 explicitly states its narrow-screen drawer reuses #99's collapse/expand semantics (drawer shape, not width-to-zero) and recommends #99 land the wide-screen behavior first. The doc models this as a single sidebar-visibility state machine with two renderings, dispatched by a viewport breakpoint:

Notes

  • Doc-only change: no code, no protocol, no build/test impact (go test ./... untouched paths).
  • Branch carries the full v0.6.0 feature work by design (not a docs-only branch); implementation PRs will follow the §4 phasing and update the main docs per the §9 checklist.

…ge doc (issues #99, #100)

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
3. 状态刷新后保持(localStorage 往返);清掉 localStorage 后回到默认展开。
4. `Ctrl/Cmd+B` 可切换,且在 input 内输入不触发。
5. `toggleGitSection()` 的 staged/unstaged 折叠状态不受影响,两个状态互不覆盖。
6. `internal/ui/ui_test.go` 增加 DOM 测试:collapsed 类能加到 `#app` 上、localStorage 往返正确(对齐现有 UI 测试写法,见 §6)。

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.

§5.1 第 6 条与 §6 测试策略自相矛盾。

§6 已明确 ui_test.go 的既有写法是 httptest 取 served 源码后 strings.Contains 文本断言(不能执行 JS),并把「localStorage 往返的状态读写」明确分给 node testdata/*.test.mjs 线。而本条原样继承了 issue #99 的验收措辞,要求 ui_test.go 断言「localStorage 往返正确」——照字面实施,只能写出名为「往返正确」实则只查字符串的断言,与 §6 自己的分工冲突。建议拆成两条写清:ui_test.go 锚点文本断言(collapsed class 名、key 名、相对顺序)+ node 行为用例(localStorage 往返、默认值降级)。

—— Kimi Code CLI(AI 评审)

| R2 | 开关按钮:chevron 图标按钮,**放在 `#header` 左侧**(折叠态常驻可见,不给侧栏留宽度) | #99 |
| R3 | 持久化:`localStorage`,命名空间化 key `mw.ui.sidebarCollapsed`;文件首个 localStorage 用例,**默认展开**;旧浏览器 / 隐私模式失败时降级为不持久化 | #99 |
| R4 | 快捷键 `Ctrl/Cmd+B` 切换:首个全局 keydown;不在 input / textarea / contenteditable 内触发;焦点在终端 iframe 内时父页面捕获不到按键(跨 frame 限制,注释说明,不试图绕过) | #99 |
| R5 | 视口与高度:`#app` 高度 `100dvh`(`@supports` 保留 `100vh` fallback)、宽度 `100%` 取代 `100vw`;viewport meta 补 `viewport-fit=cover`;容器用 `env(safe-area-inset-*)` 处理刘海与 Home Indicator | #100 L1 |

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.

可追溯性缺口:interactive-widget 在需求层静默丢失。

§1.4 已记录 viewport meta 同时缺 viewport-fit=cover 与 interactive-widget(issue #100 原文亦两项并列),但 R5 只接了前者,interactive-widget 在 R5–R16 及 §8 非目标中均无去向。若取舍是「interactive-widget 仅 Chromium/Android 支持、iOS Safari 忽略,软键盘由 R6 的 visualViewport 覆盖,本期 iOS/iPadOS 目标不需要它」——这个判断本身是对的,但建议在 §3 显式记录该决策;否则对照 issue 阅读时会认为 R5 静默缩窄了范围。

—— Kimi Code CLI(AI 评审)

| R4 | 快捷键 `Ctrl/Cmd+B` 切换:首个全局 keydown;不在 input / textarea / contenteditable 内触发;焦点在终端 iframe 内时父页面捕获不到按键(跨 frame 限制,注释说明,不试图绕过) | #99 |
| R5 | 视口与高度:`#app` 高度 `100dvh`(`@supports` 保留 `100vh` fallback)、宽度 `100%` 取代 `100vw`;viewport meta 补 `viewport-fit=cover`;容器用 `env(safe-area-inset-*)` 处理刘海与 Home Indicator | #100 L1 |
| R6 | iOS 软键盘:监听 `visualViewport` 的 `resize` / `scroll`,键盘弹出时收窄终端可视高度,保证输入区可见 | #100 L1 |
| R7 | 断点与形态(`max-width: 768px` 为界,对应 iPad 竖屏 768pt):窄屏侧栏改覆盖式 drawer(`position: absolute` + `transform: translateX`),打开带遮罩、点遮罩关闭;宽屏维持并排;中间宽度(iPad Split View,768–1024)侧栏收窄或限上限(如 `max-width: 200px`),并排仍可用 | #100 L2 |

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.

断点边界归属歧义 +「iPad 竖屏 768pt」表述过宽。

窄屏定义为 max-width: 768px(含 768),同句中间档又写「768–1024」,768px 同时落在两档;§5.3 第 3 条同样写「中间宽度(768–1024)」。另外「对应 iPad 竖屏 768pt」仅对 9.7"/10.2" iPad 成立(mini 竖屏 744pt、10.9"/11" 为 820/834pt),§3.6 的断点依据也按 768 笼统表述。建议明确 768 归哪一档(如 max-width: 767.98px,或注明 768 归 drawer),并把断点依据改写为具体机型档。

—— Kimi Code CLI(AI 评审)

| R11 | hover 样式用 `@media (hover: hover)` 包裹,纯触屏设备无粘滞高亮 | #100 L3 |
| R12 | iframe 重排:折叠 / 展开 / 断点切换后,终端 iframe(xterm + xterm-addon-fit)与 reasonix / opencode / dsh 内嵌 iframe 不出现滚动条、错位、尺寸错误 | #99+#100 |
| R13 | 与 `toggleGitSection()` 的侧栏内折叠**状态互不覆盖**(两个正交状态) | #99 |
| R14 | 窄屏让位:窄屏(≤768px)使用 drawer 形态,不要求桌面折叠态在小屏生效 | #99+#100 |

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 的初始开合状态未规定。

单一 sidebarCollapsed 布尔态(§3.1)+ 默认展开(R3)意味着窄屏首次访问时 drawer 以「展开」形态渲染——打开即盖住 #main,用户须先点遮罩才能看到终端。R14 只说窄屏用 drawer 形态,未规定窄屏初始开合,也未写明窄屏是否与桌面共享同一个持久化 key(桌面收起过的用户到 iPad 上 drawer 默认关闭——行为合理,但应写明这是同一状态机的必然结果而非新状态)。建议补一条验收:窄屏首次加载 drawer 默认状态 + 与桌面持久化状态的共享语义。

—— Kimi Code CLI(AI 评审)


### 3.4 快捷键基础设施(首个全局 keydown)

- `document` 级 keydown,`metaKey || ctrlKey` + `key === 'b'` 判定。

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.

实施细节提醒:key === 'b' 的大小写与 repeat。

event.key 在 Caps Lock 或 Ctrl/Cmd+Shift+B 时为 'B',key === 'b' 会静默不触发,建议判定写 key.toLowerCase() === 'b';另可考虑忽略 event.repeat(长按 B 导致反复切换)。属于实施 PR 易踩的坑,文档可先写明。

—— Kimi Code CLI(AI 评审)

@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.

评审总结(docs-only PR;仅评审并评论,未做任何改动)

已逐条核实 §1.4 代码现状表对当前 develop(index.html 5562 行):15 项事实锚点全部命中、无一漂移——5562 行数、viewport meta(L5)、--sidebar-width: 240px(L9)、--term-ctrl-btn-size: 32px(L23)、仅 2 个 @media 均为 prefers-color-scheme(L79/L1208)、body,html overflow:hidden(L144–152)、#app(L158–162)、#sidebar(L165–171)、.icon-btn padding:2px(L187–194)、#main(L539–545)、.sidebar-actions(L1335–1338)、#sidebar-resize-handle→startSidebarResize(L1343/L2355–2392)、toggleGitSection(L2200/L1344–1365)、唯一 keydown = handleRenameKeydown(L2565/L2623)、localStorage 0 处、SERVER_UPGRADED_FLAG(L1742/L1797/L2113–2114)、Pointer/Touch/dvh/visualViewport/safe-area 全 0。行号漂移注记与 issue #99/#100 原文比对属实。

测试策略(§6)与仓库约定核对无误:TestWebRenderersStoppedSwitchHidesAllFrames、TestEmptyWorktreeSwitchHidesAllWebPanels、TestTerminalStatusHandling(node --test 包装、node 缺席时 skip)均存在;CHANGELOG 房规描述与 issue #95 及 TestTerminalStatusChangelogCount 守卫一致;「v0.5.1 beta 抓出 3 个真实 bug」与 CHANGELOG v0.5.1 条目相符;§9 引用的 docs/PRD.md §7「当前实现状态」存在;分支名 / 基线(develop)/ 版本定位与 PR 元数据一致。文档整体质量很高。

发现 5 个问题,已逐条行内评论:

  1. §5.1 第 6 条与 §6 测试策略矛盾(L142)——ui_test.go 做不了真正的 localStorage 往返,措辞需拆分。
  2. interactive-widget 需求层静默丢失(L65 R5)——取舍正确但未记录。
  3. R7 断点 768px 边界两档重叠 +「iPad 竖屏 768pt」过宽(L67,连带 L109/L154)。
  4. 窄屏 drawer 初始开合状态未规定(L74 R14)——默认展开意味着窄屏首访抽屉盖住终端。
  5. key === 'b' 大小写 / repeat 细节(L99 §3.4)。

另有一处小 nit(不单独开评论):R10 / §5.4 的「44pt」建议统一为 CSS px 口径(HIG 44pt 在 iOS Safari 对应 44 CSS px;若实施者照写 44pt 会得到 ~58.7px,无害但与意图不符)。

—— Kimi Code CLI(AI 评审 · 2026-10-09)

@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.

独立评审:PR #109 — v0.6.0 侧栏折叠 + 响应式/触屏 PRD 变更文档

评审方式:只读独立评审(未对仓库做任何改动)
评审日期:2026-10-09
评审基线:develop(v0.5.2 合入后,index.html = 5562 行)
署名:Sisyphus

结论

无阻断性问题,本文档可以作为 v0.6.0 分期实施的 PRD 基线。有 5 项建议在开工前修订(详见行内评论):

  1. [中] §1.4 把 interactive-widget 列为现状缺陷,但 R5/R6 与全部验收标准均未承接——缺陷陈述与需求表不闭环。
  2. [中] R7 断点 768 同时属于窄屏与中间档两个区间,边界归属未定义。
  3. [中] §3.6「Split View 768–1024」与 iPad Split View 实际宽度域(约 384–911pt,上界到不了 1024)不符,中间档的论证需要修正。
  4. [低] R7 的 drawer 方案缺定位包含块(#app 无 position)与遮罩/开关按钮层级的前提声明。
  5. [低] R8「复用 568-574 既有实现」截断了隐藏滚动条规则块(实际 568–600),§1.4 同行同样问题。

已逐条核实验证为属实的部分

  • §1.4 全部行号/事实锚点与当前 develop 一致:#app 158-162(100vh/100vw);#sidebar 165-171 + --sidebar-width 9;#main 539-545;#header HTML 1370 / CSS 548-556;.sidebar-actions HTML 1335-1338;resize handle 1343 与 startSidebarResize 2355-2392;toggleGitSection 2200;唯一 keydown 为 tab 重命名输入框(2565/2623);sessionStorage 三处 1742/1797/2113-2114;localStorage 0 处;宽度断点 0 个且两个 @media(79/1208)均为 prefers-color-scheme;pointer/touch/dvh/visualViewport/safe-area 均 0 处;body overflow hidden 144-152;.icon-btn 187-194 padding 2px;--term-ctrl-btn-size 23。§1.4 脚注的行号漂移说明(49 行)也与 issue 快照差异吻合。
  • §6 测试设施引用全部存在:ui_test.go 的 TestWebRenderersStoppedSwitchHidesAllFrames / TestEmptyWorktreeSwitchHidesAllWebPanels、terminal_status_test.go 的 TestTerminalStatusHandling、testdata/terminal_status.test.mjs(当前 68 个 test( 用例);issue #95 CHANGELOG 房规的表述与 CHANGELOG 中 #95 条目一致;「v0.5.1 beta 抓出 3 个真实 bug」与 v0.5.2 发布条目一致。
  • §9 同步清单与 §10 发布路径属实:docs/PRD.md §7「当前实现状态(与愿景差异)」存在;CHANGELOG 有 ## Unreleased;README 中英双语均在;beta 先行流程与 CHANGELOG v0.5.1 beta 流程一致。§5.5 的 CI 门与 .github/workflows/go-ci.yml 一致(CI 另跑 node --test,§6 已覆盖)。
  • 需求完整性:R1–R16 与 §4 分期映射闭合(无需求无 PR 承接、无 PR 遗漏红线);issue #99 / #100 的验收标准逐条落入 §5.1–§5.5;非目标(§8)与 issue 的范围声明一致。

— Sisyphus

| 全文件唯一 keydown:tab 重命名输入框 `handleRenameKeydown` | `index.html:2623`、`2565` |
| **localStorage:0 处**;sessionStorage 仅 `SERVER_UPGRADED_FLAG` | `index.html:1742`、`1797`、`2113-2114` |
| **宽度断点:0 个**(全文件仅 2 个 `@media`,均为 `prefers-color-scheme`) | `index.html:79`、`1208` |
| viewport meta 缺 `viewport-fit=cover` / `interactive-widget` | `index.html:5` |

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.

【发现·中】interactive-widget 被列为现状缺陷,但没有任何需求或验收标准承接,需求表不闭环。

本行说 viewport meta「缺 viewport-fit=cover / interactive-widget」,但:

  • R5(§2.1)只要求补 viewport-fit=cover,未提 interactive-widget=resizes-content;
  • R6 用 visualViewport 路线解决软键盘,与该 meta 是替代关系而非互补;
  • §5.2 验收只检查 viewport-fit=cover。

这个缺口源自 issue #100:其「背景」第 2 条点了 interactive-widget,但「方案 L1」并没有列它——本文档照抄了背景、没有对方案做显式决断。结果是 PR2 实施者无法从需求表判断它是否在范围内:补,超出 R5 字面;不补,本行的缺陷陈述永远悬空。建议二选一:(a) R5 显式加上 interactive-widget=resizes-content 并在 §5.2 补一条验收;(b) 本行改为「viewport meta 缺 viewport-fit=cover;interactive-widget 缺口由 R6 的 visualViewport 方案替代,不引入」。

— Sisyphus

| R4 | 快捷键 `Ctrl/Cmd+B` 切换:首个全局 keydown;不在 input / textarea / contenteditable 内触发;焦点在终端 iframe 内时父页面捕获不到按键(跨 frame 限制,注释说明,不试图绕过) | #99 |
| R5 | 视口与高度:`#app` 高度 `100dvh`(`@supports` 保留 `100vh` fallback)、宽度 `100%` 取代 `100vw`;viewport meta 补 `viewport-fit=cover`;容器用 `env(safe-area-inset-*)` 处理刘海与 Home Indicator | #100 L1 |
| R6 | iOS 软键盘:监听 `visualViewport` 的 `resize` / `scroll`,键盘弹出时收窄终端可视高度,保证输入区可见 | #100 L1 |
| R7 | 断点与形态(`max-width: 768px` 为界,对应 iPad 竖屏 768pt):窄屏侧栏改覆盖式 drawer(`position: absolute` + `transform: translateX`),打开带遮罩、点遮罩关闭;宽屏维持并排;中间宽度(iPad Split View,768–1024)侧栏收窄或限上限(如 `max-width: 200px`),并排仍可用 | #100 L2 |

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.

【发现·中】768 同时落在两个区间,边界归属未定义。

R7 以 max-width: 768px 划窄屏 drawer,同一条又写「中间宽度(iPad Split View,768–1024)」——按字面 768 同时属于窄屏与中间档。若 PR3 实现时把中间档写成 @media (min-width: 768px) and (max-width: 1024px),就会与 max-width: 768px 在恰好 768px 时同时命中,走哪个分支取决于 CSS 书写顺序——这是可以避免的歧义。R14 已写「窄屏(≤768px)」,建议中间档统一改为开区间(769–1024)或在本条明确「768 归窄屏 drawer」,并与 §3.6 的断点表述保持一致。

— Sisyphus

| R5 | 视口与高度:`#app` 高度 `100dvh`(`@supports` 保留 `100vh` fallback)、宽度 `100%` 取代 `100vw`;viewport meta 补 `viewport-fit=cover`;容器用 `env(safe-area-inset-*)` 处理刘海与 Home Indicator | #100 L1 |
| R6 | iOS 软键盘:监听 `visualViewport` 的 `resize` / `scroll`,键盘弹出时收窄终端可视高度,保证输入区可见 | #100 L1 |
| R7 | 断点与形态(`max-width: 768px` 为界,对应 iPad 竖屏 768pt):窄屏侧栏改覆盖式 drawer(`position: absolute` + `transform: translateX`),打开带遮罩、点遮罩关闭;宽屏维持并排;中间宽度(iPad Split View,768–1024)侧栏收窄或限上限(如 `max-width: 200px`),并排仍可用 | #100 L2 |
| R8 | 窄屏 `#tabs-container` 补 `scroll-snap-type` 让 tab 吸附(滚动条继续隐藏,复用 568-574 既有实现) | #100 L2 |

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.

【发现·低·锚点精度】「复用 568-574 既有实现」引用范围偏小。

#tabs-container 隐藏滚动条的 ::-webkit-scrollbar* 规则族实际从 568 行延伸到 600 行:568-574 只是第一条规则,574-587 是 scrollbar 主体(track/track-piece),589-590 是 thumb hover/active,594-600 是 scrollbar-button/corner。§1.4 事实表同一行的「548-574」同样在滚动条规则块中间截断。建议统一改为 568-600,避免 PR3 实施者以为只需参考前 7 行。

— Sisyphus


### 3.6 断点选取依据

`768px` = iPad 竖屏宽度;`1024px` = iPad 横屏 / Split View 上边界。中间档收窄侧栏而非直接 drawer,因为 Split View 下半屏仍可容纳收窄侧栏 + 终端。

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.

【发现·中】「Split View 768–1024」与 iPad Split View 的实际宽度域不符,据此给出的中间档论证站不住。

iPad Split View 的档位是整屏宽的 1/3 / 1/2 / 2/3:12.9" 横屏约 455 / 683 / 911pt,11" 横屏约 398 / 597 / 796pt——最大档(12.9" 的 2/3,1366pt×2/3)约 911pt,到不了 1024;而所有 1/2 档(约 384–683pt)和各机型的低档 2/3 全部 ≤768,按 R7 会进入窄屏 drawer 分支,不是「中间档收窄侧栏并排」。因此「1024px = iPad 横屏 / Split View 上边界」两处都不准确:1024 只是 9.7" iPad 的全屏横屏宽,Split View 任何档位都到不了它。

该表述源自 issue #100 方案层的原文,但其背景第 7 条自己给的宽度域是「320pt ~ 1366pt」——issue 内部就互相矛盾,本文档 §3.6 逐字继承了方案层说法而没有消解它。三档分派的设计本身没问题,问题在论证。建议改写 §3.6:中间档实际覆盖的是 iPad 全屏竖屏宽度(768–1024)到 9.7" 全屏横屏之间的过渡宽度,外加 11"/12.9" 横屏的 2/3 分屏档(796 / 911pt);≤768 的 Split View 档位明确归 drawer(R7/R14 已隐含,写明即可),避免实施者误以为「所有 Split View 都走中间档」。

— Sisyphus


### 5.3 PR3(#100 L2)

1. iPhone Safari 竖屏(375×667 / 390×844)与 iPad Safari(含 Split View 窄档)下,侧栏、tab 栏、终端区域均可达,无横向滚动条、无内容被裁掉。

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.

【发现·低】R7 的 drawer 方案缺两处落地必需的前提,PR3 实施时只能临场发挥,建议在 R7 或本节验收里钉死:

  1. 定位包含块:position: absolute 的 drawer 需要定位祖先,而 #app 当前只有 display:flex; height:100vh; width:100vw(已核实 158-162,无 position)。不显式补 #app { position: relative },drawer 会相对更外层包含块定位,脱离 UI 容器。
  2. 遮罩与开关按钮的层级:遮罩需要盖住 #main 与 tab 区,但 §3.2 明确窄屏下 #header 左侧同一个按钮就是 drawer 开关——打开态必须能点它来关闭。若遮罩全屏盖住 #header,按钮不可点。z-index 分层与遮罩作用域当前均未定义。

这两点是「一个 class 两种渲染」分派的必要组成部分,建议随 acceptance 写进 PRD,而不是留给 PR3 review。

— Sisyphus

@linletian
linletian merged commit ad50eee into develop Oct 9, 2026
4 checks passed
linletian added a commit that referenced this pull request Oct 10, 2026
…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
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