feat(webui): open.file.in.web — single-source 'open file' capability (webui-parity slice 12) - #49
Merged
Merged
Conversation
The slice 02 preview component had no entry point — neither the file tree (空间目录) nor the turn summary (轮次总结) reached it. Slice 12 wires both through ONE shared action so a click in either place lands on the same right-hand preview surface (file-preview-pane). Action: lib/open-file.ts owns the single-source 'open.file.in.web' contract — openFileInWeb(path) is the only way to open a file, both entry points call it, persistence (localStorage 'webui:open-file:path') makes a refresh reopen the same file. The companion webapp unit test pins the contract at three levels: module behaviour, source-tree single-source (grep the webapp for callers), and the page-level panel-open discipline. Entry 1 (file tree): panels.tsx FilesPanel renders FileRow as a clickable button (testid files-tree-file-open) that calls openFileInWeb; the hover-only 'copy absolute path' button is unchanged. Entry 2 (turn summary): chat.tsx ActivityGroup → ToolCard renders block.toolPaths (the server's '@ /path' body lines) as clickable buttons (testid tool-card-path) that call the page-level onOpenFile handler. The handler sets the path AND auto-opens the files panel when it is closed, so the preview the user triggered is the one they see. Honest limitation: the slice-12 wiring is in place for the turn-summary entry, but the current session transcripts in the dev DB don't carry the server's 'locations' data, so the per-file chips aren't visible on those specific sessions. The wiring is correct and a session whose tool updates include locations will render clickable path chips as designed. Persistence key: webui:open-file:path (localStorage). Slice 07 owns the cross-restart durability migration; this slice only adds the per-file 'refresh reopens same file' guarantee. Gates: webapp typecheck 0; test:webapp 406/406 pass; webapp build ✓; source-inventory regenerated.
CI's webapp:typecheck rejected the test with TS2403/TS2322: the disabled-storage case redeclared the ambient 'window' global with a structural type narrower than the DOM lib's Window & typeof globalThis. Install the stub with Object.defineProperty on globalThis instead, which keeps the same behaviour under test (storage disabled must not throw at import time) without shadowing the ambient declaration. webapp:typecheck (CI entry point): clean.
Conflict resolution threaded slice 12's onOpenFile through slice 07's ScrollRestoredChat wrapper so the turn-summary file chips survive the merge: ChatProps keeps both slices' optional props, page.tsx keeps both import sets, and ScrollRestoredChat forwards onOpenFile to the inner Chat. webapp:typecheck clean · test:webapp 464/464 · check:source 4621
fengzhi09
force-pushed
the
feat/open-file-in-web
branch
from
September 27, 2026 12:13
48f682c to
cb91e2e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Slice 12 closes the gap the slice-02 acceptance flagged: the user requires 「文件预览功能是 空间目录/轮次总结都可以触发的能力,open.file.in.web」, and measurement showed 0/2 entry points and no single-source action — the viewer existed but nothing could open it.
webapp/lib/open-file.tsowns the whole capability:openFileInWeb(path)/closeOpenFile()/getOpenFilePath()/subscribeOpenFile(listener), persisting the last path underwebui:open-file:path(thewebui:namespace slice 01 established).FileRowbecomes a<button data-testid="files-tree-file-open">. The hover 「复制绝对路径」 control is kept as its own target so copying never opens the preview by accident.ToolCardrendersblock.toolPathsasdata-testid="tool-card-path"chips, threaded via a newonOpenFileprop throughActivityGroup; the page-level handler also opens the files panel so the preview is visible even when the right panel was closed.Acceptance (independent agent) — PASS-WITH-CONCERNS
panels.tsx(tree) andpage.tsx(chat handler);chat.tsximports nothing and takes a prop. Independent grep confirms no second open path exists.tool_update.locations→applyToolUpdatewrites@ ${p}→transcript.ts#TOOL_PATH_LINE→block.toolPaths→ chip.Disclosed limitation (verified accurate, not waived): the current dev database has zero
→ toolheaders and zero@ pathbody lines, so for those existing sessions no chips render. This is a data dependency, not dead code — the path is proven above.Merge-order note: this branch edits
chat.tsxandpage.tsx, which slices 06 and 07 also touch. The acceptance mapped the overlapping hunks (chat.tsx import block; ToolCard body for 12↔06; page.tsx imports and the handler block for 12↔07). Recommended landing order: 07 → 06 → 12, threadingonOpenFilethrough 06's ToolCard during its rebase.Gates
test:webapp406/406 · typecheck ✓ · build ✓ ·check:source✓ (4602 files)