What problem does this solve?
This is the foundation track of #500 (in-app browser), split out per the discussion there. It supersedes #536, whose three commits (content-agnostic split-tree leaf, focus lifecycle and pane drag-and-drop lifted above the leaf) solved the view seam and carry over as the base of this work.
As established in the #500 thread, #536 alone leaves a second surface kind as a drop-in replacement for GhosttySurfaceView rather than a first-class citizen: the tab/split/lifecycle layer still speaks a terminal-shaped vocabulary. Terminal-only commands and events (blocking scripts, binding actions, scrollback search, agent hooks) live in the shared client enums beside neutral lifecycle verbs; per-worktree state interleaves generic tree/tab/focus management with terminal specifics (Ghostty view map, zmx sessions, script tracking); and the persisted layout leaf is terminal-only. A browser pane would only "work" by quietly no-oping terminal concepts, and every additional kind would thread more kind-checks through the shared switches.
@sbertix asked for this refactor before any browser work proceeds: "refactoring existing reducer with agnostic actions, improving state management (especially efficiency-wise) [...] so that whatever we build next (browser, file viewer, etc) can truly stand its ground and not just serve as a drop-in replacement of <GhosttySurfaceView>."
Proposed solution
Make the tab/split/lifecycle layer speak surface-neutral verbs, with terminal-only concepts behind a per-kind seam -- across commands/events, per-worktree state, layout persistence, and the reducer -- and rename the load-bearing types to match what they now are. Ships no user-visible change: terminal behavior stays identical, and the PR is reviewable purely as "terminal behavior unchanged + is this the right seam."
Acceptance bar, as agreed in #500:
- Agnostic actions -- the shared layer never enumerates kinds; kind-specific behavior sits behind one arm per kind ("at least far enough so
leaf != terminal (U browser)").
- No state multiplication -- the per-kind layer must not regress performance or grow what's held per worktree for kinds that aren't present.
- Third surface is virtually free -- a new kind touches only its own module plus a registration case at each dispatch point, minus the surface-specific logic and UI. Zero edits inside existing arms.
The real acceptance test for the seam is the follow-on browser POC, which must add its kind by touching only its own module plus the registration cases.
Alternatives considered
Supacode version
current main
Are you planning to build this yourself?
Before submitting
What problem does this solve?
This is the foundation track of #500 (in-app browser), split out per the discussion there. It supersedes #536, whose three commits (content-agnostic split-tree leaf, focus lifecycle and pane drag-and-drop lifted above the leaf) solved the view seam and carry over as the base of this work.
As established in the #500 thread, #536 alone leaves a second surface kind as a drop-in replacement for
GhosttySurfaceViewrather than a first-class citizen: the tab/split/lifecycle layer still speaks a terminal-shaped vocabulary. Terminal-only commands and events (blocking scripts, binding actions, scrollback search, agent hooks) live in the shared client enums beside neutral lifecycle verbs; per-worktree state interleaves generic tree/tab/focus management with terminal specifics (Ghostty view map, zmx sessions, script tracking); and the persisted layout leaf is terminal-only. A browser pane would only "work" by quietly no-oping terminal concepts, and every additional kind would thread more kind-checks through the shared switches.@sbertix asked for this refactor before any browser work proceeds: "refactoring existing reducer with agnostic actions, improving state management (especially efficiency-wise) [...] so that whatever we build next (browser, file viewer, etc) can truly stand its ground and not just serve as a drop-in replacement of
<GhosttySurfaceView>."Proposed solution
Make the tab/split/lifecycle layer speak surface-neutral verbs, with terminal-only concepts behind a per-kind seam -- across commands/events, per-worktree state, layout persistence, and the reducer -- and rename the load-bearing types to match what they now are. Ships no user-visible change: terminal behavior stays identical, and the PR is reviewable purely as "terminal behavior unchanged + is this the right seam."
Acceptance bar, as agreed in #500:
leaf != terminal (U browser)").The real acceptance test for the seam is the follow-on browser POC, which must add its kind by touching only its own module plus the registration cases.
Alternatives considered
Supacode version
current main
Are you planning to build this yourself?
ready.Before submitting
ready.