feat(code): route file/diff opens to the Code surface (cave-ohcj)#3735
Merged
Conversation
File-open events (cave:open-project-file, cave:open-file-diff, cave:browse-project-files) now land on the Code surface instead of Chat's inline code rail — from every mode, including chat itself. - pending-code-rail-open.ts → pending-code-open.ts: PendingCodeOpen carries the raising chat session (sessionId) so CodeView can select that session's workbench. - workspace.tsx: the bridge enqueues unconditionally (no more chat-mode skip), attaches activeChatSessionId, and switches to code mode; the pending open is handed to CodeView, not ChatSurface. - code-view.tsx: consumes pendingOpen — picks the raising session (or a browse root's newest session; degrades gracefully when none matches), lands on the Sessions tab, and forwards the target to the workbench. A manual rail switch drops any pending focus so it can't replay into an unrelated workbench. - code-workbench.tsx: openTarget lands on the Diff or Files tab with the path focused (SessionChangesInner focusPath; new focusPath/ focusNonce on CodeWorkbenchFiles). - use-workspace-rail-controller.ts: the chat rail no longer listens for the global open events; it keeps only its surface-internal cave:changes-open affordance. The rail itself stays (slimming is the follow-up half of cave-ohcj). - Pins flipped: code-surface-mode, workspace-chat-handoff, tool-target-file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Routes file/diff/browse open events (cave:open-project-file, cave:open-file-diff, cave:browse-project-files) to the dedicated Code surface so opens land in the correct session workbench (instead of Chat’s inline code rail), and updates the contract tests/pins to enforce the new behavior.
Changes:
- Introduces
PendingCodeOpen(with optional raisingsessionId) and updates Workspace to enqueue opens and switch intomode="code". - Adds Code surface consumption: CodeView resolves the target session (by
sessionIdor browse root), and CodeWorkbench/Files tab focus the appropriate diff/file viafocusPath+nonce. - Removes the chat rail controller’s global file/diff/browse event listeners; retains only surface-internal
cave:changes-open.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/lib/use-workspace-rail-controller.ts | Stops consuming global file/diff/browse open events; keeps only cave:changes-open handling for the rail. |
| src/lib/tool-target-file.test.ts | Updates pin assertions to require Code-surface routing and to forbid rail controller global listeners. |
| src/lib/pending-code-open.ts | Defines PendingCodeOpen and adds optional sessionId for session-aware routing. |
| src/components/workspace.tsx | Bridges open events into pendingCodeOpen and switches to mode="code"; passes pending open into CodeView. |
| src/components/workspace-rail.tsx | Updates rail focus type import to PendingCodeOpen. |
| src/components/workspace-chat-handoff.test.ts | Updates contract tests to enforce Code-surface routing and CodeView/Workbench handling. |
| src/components/code-workbench.tsx | Accepts openTarget and routes to Diff/Files tab + forwards focus props. |
| src/components/code-workbench-files.tsx | Adds focusPath/focusNonce and applies external focus by selecting/opening the path. |
| src/components/code-view.tsx | Consumes pendingOpen, resolves target session (by sessionId/root), and passes a scoped openTarget to the resolved workbench. |
| src/components/code-surface-mode.test.ts | Updates pin to assert file-open events route to Code surface (and adjusts Diff tab mount assertion). |
| src/components/chat-surface.tsx | Removes pending code-rail open props/effect; Chat no longer participates in global file/diff routing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+118
to
+136
| useEffect(() => { | ||
| if (!pendingOpen) return; | ||
| const byId = pendingOpen.sessionId | ||
| ? groups.flatMap((g) => g.sessions).find((row) => row.id === pendingOpen.sessionId) | ||
| : undefined; | ||
| const root = pendingOpen.kind === "files" ? pendingOpen.root : undefined; | ||
| const trim = (p: string) => p.replace(/\/+$/, ""); | ||
| const byRoot = | ||
| !byId && root | ||
| ? groups.flatMap((g) => g.sessions).find((row) => trim(codeSessionWorkRoot(row)) === trim(root)) | ||
| : undefined; | ||
| const target = byId ?? byRoot; | ||
| setTopTab("sessions"); | ||
| if (target) setSelectedId(target.id); | ||
| // Root browse with no matching session: there is no workbench to focus — | ||
| // land on the surface and leave the rail/selection as-is. | ||
| setWorkbenchTarget(root && !target ? null : { open: pendingOpen, sessionId: target?.id ?? null }); | ||
| onPendingOpenHandled?.(); | ||
| }, [groups, onPendingOpenHandled, pendingOpen]); |
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.
First half of cave-ohcj (phase 2b of the Code surface series; follows #3732).
What
File-open events —
cave:open-project-file,cave:open-file-diff,cave:browse-project-files— now land on the Code surface instead of Chat's inline code rail, from every mode including chat itself. Clicking a file chip or diff link in a transcript jumps to that session's workbench with the file focused in the Files tab (or its diff expanded in the Diff tab).How
pending-code-rail-open.ts→pending-code-open.ts:PendingCodeOpencarries the raising chat session (sessionId).activeChatSessionIdvia ref mirror, switches tocodemode; pending open handed to CodeView.pendingOpen— selects the raising session, or a browse root's newest session (codeSessionWorkRootmatch); degrades to the surface with no stale focus when nothing matches. Manual rail switches drop pending focus so it can't replay into an unrelated workbench.openTargetlands on Diff/Files tab;focusPath/focusNonceforwarded toSessionChangesInner(existing contract) andCodeWorkbenchFiles(new props).cave:changes-open. The rail itself stays — slimming is the second half of cave-ohcj.Verification
pnpm typecheck✓, eslint ✓