feat: Latch Code-Surface Folder at First Open - #586
Conversation
The code lens re-derives its folder from the active pane's cwd on every SSE tick, so switching panes in the terminal makes the embedded editor vanish or lose in-flight state. Latch the folder once at first open (localStorage, per-window) so only the editor's own File > Open Folder navigation moves it afterward.
There was a problem hiding this comment.
Pull request overview
This PR makes the embedded code surface stop tracking the terminal’s active pane by introducing a per-window “latched” code folder. The latch is seeded once (first time the code surface renders) and afterward only follows code-server’s own navigation (File > Open Folder), stabilizing the editor’s workspace and in-flight state across pane switches.
Changes:
- Add a per-window localStorage-backed code-folder latch (read/write helpers + unit tests).
- Plumb a latched “effective window” through availability/layout/rendering, and update
CodeSurfaceto keep iframesrcfixed per mount generation while reporting editor-driven folder navigations onload. - Add Playwright e2e coverage for pane-switch persistence and update specs/memory docs to reflect the latch semantics.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| fab/changes/260813-if5d-latch-code-surface-folder/plan.md | Generated implementation plan and acceptance criteria for the latch feature. |
| fab/changes/260813-if5d-latch-code-surface-folder/intake.md | Intake rationale, non-goals, and detailed behavior/spec requirements. |
| fab/changes/260813-if5d-latch-code-surface-folder/.status.yaml | Pipeline/status metadata for the change. |
| fab/changes/260813-if5d-latch-code-surface-folder/.history.jsonl | Pipeline history log for the change. |
| docs/specs/window-views.md | Updates view registry “code” availability semantics to latch-or-derivable. |
| docs/specs/right-panel.md | Updates right-panel spec to document latch seeding + follow-the-editor rules. |
| docs/memory/run-kit/ui-patterns.md | Documents the latch behavior as a UI pattern and how it affects code-surface rendering. |
| docs/memory/run-kit/index.md | Updates memory index description to include the latch concept. |
| app/frontend/tests/e2e/code-folder-latch.spec.ts | New e2e test validating code tile persistence when active pane leaves repo and across reload. |
| app/frontend/tests/e2e/code-folder-latch.spec.md | Companion spec document describing e2e scope/limits and scenarios. |
| app/frontend/src/lib/code-folder-latch.ts | New latch storage module (keying + try/catch localStorage wrappers). |
| app/frontend/src/lib/code-folder-latch.test.ts | Unit tests for latch keying, round-trips, empty handling, and storage failures. |
| app/frontend/src/components/surface-layout.tsx | Threads onCodeFolderNavigated through to CodeSurface and clarifies code tile meta semantics. |
| app/frontend/src/components/surface-layout.test.tsx | Tests that code tile header/body reflect the (latched) folder and passthrough works. |
| app/frontend/src/components/code-surface.tsx | Holds iframe src stable per mount generation and reports editor-driven folder changes on load. |
| app/frontend/src/components/code-surface.test.tsx | Tests src fixity, reachability remount behavior, and follow-the-editor reporting. |
| app/frontend/src/app.tsx | Implements latch read/seed/follow wiring and substitutes a latched “effective window” across consumers. |
| app/frontend/src/app.test.tsx | Adds unit coverage for the withLatchedCodeFolder substitution seam. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const [latchEpoch, setLatchEpoch] = useState(0); | ||
| const latchedCodeFolder = useMemo( | ||
| () => (windowParam ? readLatchedCodeFolder(server, windowParam) : undefined), | ||
| [server, windowParam, latchEpoch], | ||
| ); | ||
| const latchCodeFolder = useCallback((folder: string) => { | ||
| if (!windowParam || folder.length === 0) return; | ||
| writeLatchedCodeFolder(server, windowParam, folder); | ||
| setLatchEpoch((n) => n + 1); | ||
| }, [server, windowParam]); |
There was a problem hiding this comment.
Skipped — the cited rule does not exist. Plan R1 specifies the opposite explicitly: "GIVEN localStorage is unavailable (jsdom without storage, private mode, quota) / WHEN read or write is called / THEN read returns undefined and write is a silent no-op — no throw reaches the caller." Acceptance criterion A-013's phrase "degrade to per-session behavior" is defined by its own parenthetical — "(read undefined, write noop)" — meaning unpersisted, not in-memory-latched. docs/memory/run-kit/ui-patterns.md states it directly: "Absent, empty, and storage-unavailable all read alike as 'not latched'", and the intake scopes the module to a "storage-unavailable noop".
Falling back to the live derived gitRoot when storage is unavailable is the designed degradation — the window behaves exactly as it did before the latch existed. Adding an in-memory fallback would introduce a second source of truth, contradicting the app.tsx invariant that "localStorage stays the source of truth and is read AT RENDER".
pnpm 9 (pinned in ci.yml/release.yml) rejects a settings-only pnpm-workspace.yaml with "packages field missing or empty"; pnpm 10+ accepts it. packages: ['.'] satisfies pnpm 9 without changing install layout — the lockfile is byte-identical under both versions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Meta
if5dexcludes
fab/,docs/· generated by fab-kit v2.20.1Pipeline: intake ✓ → apply ✓ → review ✓ → hydrate ✓ → ship → review-pr
Summary
The code lens re-derives its folder (
gitRoot) from the active pane's cwd on every SSE tick, so switching panes in the terminal — or having two panes in different repos — makes the embedded editor vanish or lose in-flight state (open tabs, dirty buffers, undo stack). This change latches the folder once at the first-ever open of the code surface (per-window, localStorage), so only the editor's own File > Open Folder navigation moves it afterward; the terminal never does.Changes
code-folder-latch.ts)hasCode= latch exists OR gitRoot derivabletileMetafollow the latchdocs/specs/right-panel.md).spec.mdcompanion)