feat(web): split-view divider controls, active-pane opening, and pane swap - #170
Merged
Conversation
… swap The split-view pane controls lived in ChatView's titlebar overlay and covered the git actions; sidebar and palette picks always retargeted the primary route, and clicking the secondary pane's thread while the primary showed a draft folded the whole split. Secondary-pane controls now sit in a hover cluster on the resize divider (swap / switch / close), and Open split view moved into the chat header's action row. Sidebar clicks and cmd+K picks open into the active pane; a thread already on screen just gets its pane focused, so a pick can never fold the split. New threadPane.swap command (default mod+shift+\) trades the two panes' threads under a store latch that keeps the duplicate-thread guard quiet mid-swap. Sidebar rows mark the two open threads with pane glyphs.
… controls Review round (sol + opus on PR #170): - The swap latch now expires through a store-owned timer; without one a stalled navigation left it (and every swap affordance) stuck, since the TTL was only read on re-renders. Mid-swap secondary picks keep the latch so the late route arrival is still recognized; abort already yields to the newer pick. - mod+shift+\ never matched: Shift+Backslash types "|" and the resolver had no Backslash code alias. Added, with a regression test; both new behaviors were mutation-tested. - Enter/Space row activation and archived-row opens now route through the active-pane helper like clicks; its primary branch delegates to the upstream navigateToThread instead of copying it. - openThreadInActivePane returns the executed plan and the navigation promise, so palette picks surface rejections again; post-swap focus reclaim moved into swapThreadPanes so shortcut, palette, and divider paths behave alike; the overlay focus intent moved out of the store into the palette call sites so sidebar picks stop leaving stray intents. - Divider: drag overhang biased off the primary pane's scrollbar lane, reveal delayed to stop crossing flashes, cluster pinned visible on coarse pointers, group label added, pane ring lowered below it, dead no-drag removed, disabled swap button's tooltip made reachable. - Pane glyphs ride slim rows too; stale-named modules renamed (PaneControls, SplitPaneMarker).
…icks Verification round follow-ups: a swap whose latch expired could still see its navigation settle later and abort, toast over, or steal focus from a newer swap — abortPaneSwap now takes the owning latch and stale continuations no-op. Picking the in-flight swap's destination thread from the sidebar or palette now focuses the primary pane (the route is already heading there) instead of reopening it in the secondary, which folded the split on arrival.
pandec
added a commit
that referenced
this pull request
Aug 23, 2026
PR #170 landed on origin/dev mid-sync, touching ChatView.tsx and Sidebar.tsx. The merge was conflict-free and holds up on inspection: the slim-row split-pane marker sits after the provider icon and composes with the pin indicator inserted after the title, and #170 already assumed a settled thread can occupy a pane, which is exactly what pingdotgg#7969 makes common. vp check, typecheck, focused split-view/sidebar/keybinding tests, and the full suite all rerun and pass, except the documented PortScanner pair.
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.
Follow-ups to the two-pane split view (#163) after real use.
Problems. The secondary pane's switch/close buttons rendered in ChatView's absolute titlebar overlay, which only reserves 64px, so they covered the git actions. Sidebar clicks and ⌘K picks always retargeted the primary route, so the secondary pane's thread was effectively pinned, and clicking its thread while the primary showed a draft tripped the duplicate-thread guard and folded the whole split. There was also no way to swap the two panes, and the sidebar gave no hint which threads were open in the split.
Fixes.
planThreadOpen: the active pane receives the pick, and a thread already on screen just gets its pane focused instead of navigating, which is what fixes the split-folding bug. The palette snapshots its owner pane at open time because its own focus traffic moves the live value. Mod-click and shift-range selection are untouched.threadPane.swapcommand (default mod+shift+, ⌘K "Swap split threads", divider button). Swap sets the secondary to the old primary and navigates to the old secondary under a 5s store latch that the duplicate guard consults; expiry or rejection rolls back and toasts, and the same physical side stays active afterwards. Disabled while the primary shows a draft.Verification. 35 focused tests across
threadSplitStore.test.ts,threadOpenTarget.test.ts,splitPaletteItems.test.ts(the latch guard was mutation-tested);vp run typecheck15/15;vp check0 errors.Implemented by Claude Fable 5 in Claude Code.