Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a61ed82
docs(prd): v0.6.0 Web UI sidebar collapse + responsive/touch PRD chan…
linletian Oct 9, 2026
6b12485
docs(prd): address PR #109 review — interactive-widget decision, 768p…
linletian Oct 9, 2026
76abfcf
docs(prd): v0.6.0 dev plan — design-draft gate, main-doc sync, PR1-PR…
linletian Oct 9, 2026
2a4a18d
docs(prd): v0.6.0 phase-0 design draft — sidebar states pencil mockup…
linletian Oct 9, 2026
4de8bc1
docs(prd): design draft review fixes — header actions Restart/Stop/De…
linletian Oct 9, 2026
05b1dd8
docs(prd): design hierarchy fix — sidebar stays a full-height peer of…
linletian Oct 9, 2026
1f0ae3f
docs(architecture): record sidebar/#main peer layout hierarchy as an …
linletian Oct 9, 2026
6ea97da
docs(prd): arbitration edits from PR1 dual review — mark §5.1 visual …
linletian Oct 9, 2026
70741de
docs(prd): arbitration — the pty terminal is a same-document xterm, n…
linletian Oct 9, 2026
2bf930e
docs(prd): arbitration round 3 — xterm stopPropagation is the real R1…
linletian Oct 9, 2026
9fc312f
docs(prd): manual checklist gains the chevron two-state visibility ch…
linletian Oct 9, 2026
48990f0
feat(ui): the desktop worktree sidebar can now be collapsed / expande…
linletian Oct 9, 2026
6a09232
docs(prd): arbitration from PR2 review — manual checklist gains pinch…
linletian Oct 9, 2026
160114c
docs(prd): arbitration from PR2 round 2 — manual checklist gains soft…
linletian Oct 9, 2026
801ca69
feat(ui): the Web UI now tracks the dynamic viewport and compensates …
linletian Oct 9, 2026
349d2c3
feat(ui): the Web UI now adapts to narrow and middle viewports — at ≤…
linletian Oct 10, 2026
7df0001
docs(prd): arbitration from PR3 review — gapless not-all-and media co…
linletian Oct 10, 2026
34e9f03
feat(ui): touch screens can now drag the sidebar split, tap the icon …
linletian Oct 10, 2026
1917352
docs(prd): arbitration from PR4 review — §5.4-2 scope narrowed to R10…
linletian Oct 10, 2026
dd5319d
Merge remote-tracking branch 'origin/develop' into feature/v0.6.0-web…
linletian Oct 10, 2026
c626a9a
fix(ui): PR #110 review follow-ups — the narrow drawer now insets its…
linletian Oct 10, 2026
e60c56c
docs(prd): PR #110 review arbitration — status header reflects the im…
linletian Oct 10, 2026
4ba0d2d
docs(prd): interactive preview page for the v0.6.0 sidebar collapse a…
linletian Oct 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ It does **not** analyze project code or prevent concurrent write conflicts insid

### 3.3 Main workspace (sidebar)

> **Layout hierarchy(UI 设计原则,不可变)**:`#app` 之下的顶层划分是两个**同级**区域——全高的 `#sidebar`(主仓库 + worktree 列表 + git 变更面板)与 `#main` 主工作区;`#header`(实例 tab 栏 + 实例操作按钮)位于 `#main` **之内**,实例 tab 跟随 worktree 列表的选择切换。产品层级是 worktree 在上、实例 tab 在下。任何 UI 变更(新增按钮、侧栏折叠、响应式 drawer 等)都**不得**把 tab 栏提升为压住侧栏的全宽顶栏,也不得让侧栏在视觉上从属于 tab 栏——那会把 worktree 列表降级为实例 tab 的子内容。本条已两次在评审中踩坑(最近一次:v0.6.0 侧栏折叠设计稿初版把开关按钮画进全宽顶栏);正确做法以该例为鉴:开关按钮放 `#header` 左侧(即 `#main` 顶栏左侧),侧栏保持全高。窄屏 drawer 不违反本条——drawer 是侧栏的覆盖式渲染形态,保留自己的全高顶栏。

The sidebar shows a pinned **Main Workspace** item at the top (purple accent), followed by a "Worktrees" divider and the managed worktree list.

- **Main repo**: `GET /api/main` returns `{name, branch, github_url}`. The branch is live — queried via `git rev-parse --abbrev-ref HEAD` (via `gitx.CurrentBranch`). Returns empty string for `branch` on detached HEAD (e.g., CI shallow clones), otherwise returns the current branch name.
Expand Down
1 change: 1 addition & 0 deletions docs/PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
- 输出回放中按模式脱敏主流 AI key(如 `sk-***`)。

## 7. 当前实现状态(与愿景差异)
- **已实现:桌面端 worktree 侧栏折叠 / 展开(v0.6.0,来源 issue #99;计划与分期见 `docs/plans/feature-v0.6.0-webui-sidebar-responsive/`)**:折叠态为 `#app` 上的单一 `.collapsed` CSS 类(`#sidebar` `width: 0` + `overflow: hidden` + `visibility: hidden`(后者把折叠态侧栏按钮移出 Tab 焦点序列,`width:0` 只裁剪不移出),`#main` 因 `flex: 1` 自动吃满,JS 不做任何宽度计算);开关为 `#header` 左侧(实例 tab 之前)常驻的双尖括号 chevron 按钮(`<<` 展开态 / `>>` 折叠态,`toggleAttribute("hidden")` 翻转,且因 UA 的 `[hidden]` 提示规则仅限 HTML 命名空间,配套显式消费规则 `#sidebar-toggle-btn svg[hidden] { display: none !important; }`;按钮带 `aria-label` / `aria-controls` / `aria-expanded`),`#sidebar` 与 `#main` 的同级层级不变;状态持久化于 `localStorage` key `mw.ui.sidebarCollapsed`(宽屏默认展开,key 缺失 / 解析失败 / 抛异常一律降级为展开且不持久化);`Ctrl/Cmd+B` 经首个全局 keydown 切换(忽略 `event.repeat`,拒绝 `event.altKey`(AltGr 布局),`key.toLowerCase() === 'b'` 兼容 Caps Lock / Ctrl+Shift+B,input / textarea / `[contenteditable]` 内不触发且放行事件本身——守卫的真实职责是保护不自行截断的输入宿主即 modal 表单字段)。R16 的真实机制(终稿,经 vendored 源码核实):终端不是 iframe(pty 渲染器把 xterm 画在同文档 div),且 xterm 在 `xterm-helper-textarea` 上自行截断按键——vendored `xterm.js` 的 `_keyDown` 调 `cancel(e, !0)` 无条件 preventDefault + stopPropagation——父文档监听器的 textarea 分支对终端是死路径,终端按键根本到不了折叠开关,Ctrl+B 以 `\u0002` 原样到达 TUI;跨 frame 隔离仅对 opencode / reasonix / dsh 的 web UI iframe 成立(其按键确实到不了父文档,不做 postMessage 绕过);折叠 / 展开后按固定顺序(先切 class、下一动画帧再 fit)重排活动 xterm(xterm-addon-fit),无滚动条 / 错位;与 `toggleGitSection()` 的 staged / unstaged 折叠状态完全正交(行为用例双向驱动验证)。触屏交互已落地(PR4,issue #100 L3,见下)。**视口与高度(PR2,issue #100 L1)**:`#app` 高度基准 `100vh`(旧 Safari fallback,声明在前)经 `@supports (height: 100dvh)` 渐进增强为 `100dvh`(跟随移动端地址栏收展、零 JS);宽度 `100%` 取代 `100vw`(`vw` 计入滚动条槽导致横向溢出);viewport meta 补 `viewport-fit=cover`,`#app` 以 padding 消费全部四个 `env(safe-area-inset-*)`(刘海与 Home Indicator,border-box 内缩子元素,桌面端四值全 0 零变化)。iOS 软键盘:不引入 `interactive-widget=resizes-content`(仅 Chromium/Android,§3.5/§8 显式排除),改由 `window.visualViewport` 的 `resize`/`scroll` 监听把 `#app` 内联高度钉在可视底边(`offsetTop + height`,布局坐标系),遮挡判定哨兵用 `document.documentElement.clientHeight`(`vv.height` 与 clientHeight 均排除滚动条,而 `window.innerHeight` 不排除经典滚动条槽——CSSOM View 两处规范事实);捏合 / 双击缩放(`vv.scale ≠ 1`,此时 `vv.height` 随缩放收缩并触发 resize)显式退出钉高并清除既有钉高;`scroll` 仅在已钉高时重钉,纯平移不引入钉高(body 不滚,平移移出顶部的内容不可达,不应把布局钉死到该几何);遮挡消失即清除内联高度交还 CSS 级联;高度收窄经 flex 列下传、终端宿主既有 ResizeObserver 触发 xterm 重排,保证输入区在键盘上方;无 `visualViewport` 的环境(旧浏览器)优雅降级为无操作。与 PR1 折叠同场验证(R12):折叠管 `.collapsed` 类、本层管内联高度,`#app` 上两处不相交的状态写入同时生效不跳动。**断点与 drawer 形态(PR3,issue #100 L2)**:三档断点——窄屏 `≤768px`(drawer)、中间档 `769–1024px`(收窄侧栏并排,768 归窄屏,两档不重叠)、宽屏 `>1024px`;仍由 PR1 的单一 `.collapsed` 类驱动两种渲染形态(§3.1 单一状态机,无第二个状态变量),PR1 的零宽折叠规则整体限定进窄屏块的补集 `@media not all and (max-width: 768px)`(`all` 匹配全部媒体类型、取反后恰为 width > 768px 含分数宽度,与 `(max-width: 768px)` 无缝互补——不能用 `min-width: 769px`:媒体查询接受分数宽度,(768, 769) 死区(如桌面 200% 缩放 1537px 物理宽 = 768.5 CSS px)下两条查询都不命中,折叠规则失效、toggle 按钮视觉惰性(class 与 aria 照翻但渲染不变),这是 PR3 相对 PR1 全局规则会引入的回归缝;选择器与四声明逐字保留)。窄屏下 `#sidebar` 改 `position: absolute` 覆盖于 `#main` 之上(`#app` 补 `position: relative` 作定位包含块,已核实对既有 absolute/fixed 元素——connection overlay 为 fixed、其余各有更近的 positioned 祖先——零影响),`#main` 保持全宽在下方;关闭态 `transform: translateX(-100%)` 移出画布 + `visibility: hidden` 把抽屉按钮移出 Tab 焦点序,打开态继承基础规则的 `var(--sidebar-width)` 宽度与初始 `visibility: visible`——§3.1 的「不可见抽屉」陷阱由折叠规则的断点限定关闭而非覆盖修补;无 transition / 动画(明确非目标,与桌面折叠同为瞬时翻转)。遮罩 `<div id="sidebar-mask">` 为 `#app` 子元素、全局 `display: none`,仅窄屏且 drawer 打开(`#app:not(.collapsed)`)时 `display: block` + `position: absolute; inset: 0` + `var(--modal-backdrop)` 底色,盖住 `#main` 与 tab 区、点遮罩走 `closeSidebarDrawer()`(`applySidebarCollapsed(true)` + 持久化,与开关按钮 / `Ctrl/Cmd+B` 同一条单一状态路径);z-index 分层按钮 40 > drawer 30 > 遮罩 25 > `#main` 内容(含 terminal-controls 20),全部低于模态层 9999,且按钮的 position/z-index 提升仅限窄屏媒体查询内(桌面 connection-overlay 覆盖关系不变)。窄屏 tab 栏补 `scroll-snap-type: x proximity` + `.tab { scroll-snap-align: start }`(`proximity` 只在停驻接近吸附点时吸附,小幅度滑动不橡皮筋;`start` 把半露 tab 的左缘对齐滚动口拉全),隐藏滚动条规则族原样复用不动。R14 默认值按视口分派:key 缺失或解析失败 / 抛异常时 `defaultSidebarCollapsedByViewport()` 经 `window.matchMedia("(max-width: 768px)")` 分派(窄屏首访 drawer 默认关闭不遮挡终端;无 matchMedia 或 matchMedia 抛异常一律降级宽屏默认展开);显式持久化值永远优先、从不咨询视口——桌面收起过的用户窄屏 drawer 亦默认关闭,这是共享 key 的自然结果而非特判(§5.3-5);默认值仅在首次加载分派,会话中跨越 768px 不重算(`.collapsed` 类按当前断点自然呈现两种形态)。**触屏交互(PR4,issue #100 L3)**:侧栏分栏拖拽从 mouse 事件迁到 Pointer Events——handle 内联 `onpointerdown="startSidebarResize(event)"`,`startSidebarResize` 对 handle 调 `setPointerCapture(e.pointerId)`(capture 把该指针后续事件重定向到 handle,拖出窗口不丢事件;调用 try/catch 守卫 NotFoundError——pointerId 非活动指针(合成事件 / 时序边缘)时抛,而非「无 capture 支持的引擎」——抛出时降级为 document 监听器路径),document 级 `pointermove`/`pointerup`/`pointercancel` 监听,`pointercancel` 同 stop 处理(iOS 在 UA 接管手势时触发);函数首行重入守卫 `if (isResizingSidebar) return;`(拖拽中第二指针 pointerdown 不重置基线、不重复 capture——DOM 规范对相同 type/callback/capture 去重,真实危害是基线劫持与双指针争用而非监听器泄漏);钳制数学一字未动(minTop 120 / minBottom 100,`Math.max(minTop, Math.min(sidebarContentHeight - minBottom, newTopHeight))`);`e.preventDefault()` 保留。`touch-action: none` 常驻 handle(手势起始即求值,拖到 pointerdown 才设对已晚;4px 条带下无可滚内容,常驻无损失);`user-select: none` / `-webkit-touch-callout: none` 按 `.dragging` 拖拽态作用域(PRD「拖拽时」字面),原 `:hover, .dragging` 组合拆分——`.dragging` 半支全局、`:hover` 半支入 hover 媒体块。触控目标:`@media (pointer: coarse)` 下 `.icon-btn` 与 `.term-ctrl-btn` 得 `min-width: 44px; min-height: 44px`(min-* 非固定尺寸,used = max(content, min-*);`.icon-btn` 基础规则零 width/height 声明的护栏入测试;`--term-ctrl-btn-size` 变量驱动基础值不变;44 写 px 不写 pt);40px `#header`(border-box,减去 1px border-bottom 后内容盒 39px)中 44px `.icon-btn` 盒居中即 [-2.5, 41.5],触屏上溢出虽不可见(`.icon-btn` 无边框无底色、R11 使 hover 底色不渲染)但不可见≠可命中——`body, html` 的 `overflow: hidden` 会裁掉 `env(safe-area-inset-top)=0` 设备(iPad / Android / 无刘海机 / 桌面触屏)header 顶边(即视口 y=0)之上的 2.5px,裁剪区不参与命中测试(违反 ≥44 字面;刘海机不受影响),故 coarse 块内 `#header .icon-btn` 规则 `top: 3px` 把盒下移到 [0.5, 44.5] 完整落入视口、越过 header 底缘的约 4.5px 以 `z-index: 40` 压过 `#workspace` 顶缘保证命中(特异性 (1,1,0) 高于窄屏块 `#sidebar-toggle-btn` 的 (1,0,0),故 z-index 取与窄屏规则相同的 40,窄屏+coarse 同场时 drawer 分层逐字节不变;glyph 视觉近乎居中);`.icon-btn` 的另一使用点 `.sidebar-actions`(新建 / 导入按钮)同受 44px 地板影响,侧栏标题行高触屏下约 43→68px,属 R10 本意的接受后果。hover:全文 22 处 `:hover` 全部包进 `@media (hover: hover)`(原位包裹保持级联顺序;scroll-bar thumb 的 `:active` 半支与 handle 的 `.dragging` 半支作为非 hover 交互态全局保留;dark-scheme 内的 remote-access hover 覆盖将 hover 查询嵌套进颜色查询内;不支持 hover 媒体的古老浏览器整块丢弃 = 无 hover 样式,可接受降级)。§5.4-2 范围:R10 的字面范围是 `.icon-btn` + `--term-ctrl-btn-size`,其余可见控件审计后维持原尺寸并附理由(worktree 行 / git section 头 / git change 行 = 全宽行,一维远超 44;`.wt-action-btn` 为 hover 门控的既有形态,R11 后触屏不可达为既有行为非 PR4 新增;`.tab` / `.new-tab-btn` / modal 与 `#instance-actions` 通用按钮 / `.modal-close-btn` / `#status-bar` 带不透明盒,44px 化会在 40px header 或 dialog 内可见溢出,留作产品决策;4px 拖拽条带属 R9 手势范畴)。
- **Reasonix web chat 实例(MVP 完结,含需求修订 2026-08-12)**:`kind=reasonix` 实例在 worktree 内运行 `reasonix serve` 子进程,其 web 聊天界面经反代 `/rx/<id>/` 以 iframe 嵌入实例标签页(创建实例时选择 *Reasonix* 标签页,仅需名称——启动命令固定为 reasonix serve;tag 的 env/preStart 仍可经 API/CLI 的 tag_id 注入,command 始终忽略)。
- **语义基线(需求修订 2026-08-12,见 §2 Goal 7 / §5 关键规则)**:实例 = 在该 worktree 项目里打开 reasonix 的 web UI。项目间隔离由 reasonix 自身按 cwd 组织(`~/.reasonix/projects/<slug>/sessions`);同一项目的全部历史会话(含终端直接跑 reasonix CLI/TUI 产生的)在实例侧边栏**可见、可切换**,与终端行为一致。myworktree 只负责 worktree/实例生命周期与 `/rx/<id>/` 反代,不介入 agent 的会话/项目/分支语义。
- **实现状态(2026-08-12 已按新基线实现)**:已取消 `REASONIX_HOME` 隔离与固定 `--resume` 会话文件,serve 直接使用 `~/.reasonix`(与终端运行一致),会话按项目由 reasonix 自身组织、同项目历史会话(含终端产生的)在实例侧边栏可见可切换,`#56` 期望满足。`#49`「Restart = 全新会话」语义保留(重启开新会话,历史仍在共享会话池中可切换)。
Expand Down
Loading