feat(browser-tabs): tab-owned split panes — one strip, tabs contain panes#3364
feat(browser-tabs): tab-owned split panes — one strip, tabs contain panes#3364adamleithp wants to merge 6 commits into
Conversation
Local dev loop for the @posthog/quill design system: edit quill in the sibling mono, build the whole quill workspace, pack a content-hashed tarball into .local-quill, repoint the pnpm override, and reinstall — so quill changes can be tested in this app before publishing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anes Chrome-split-view model: the single title-bar strip stays; each TAB owns a PaneLayoutNode tree of panes, and panes carry the identity (canvas/task/channel/appView). Multi-pane tabs show a layout glyph on their pill; dragging tab B onto a content edge/center zone merges it into the active tab (its pill disappears); each pane of a multi-pane tab gets a hover close-X. - shared: v2 schemas + transforms (openOrFocusTab pane-dedup, setPaneTarget, mergeTabIntoTab, closePane, ensureSnapshotIntegrity, decidePaneNavigation with PUSH-only dedup), insertNodeInLayout subtree splice. - workspace-server: browser_panes table + migration 0021 (one pane backfilled per tab, heals v1-dogfood profiles), v2 repository/service/procedures with renderer-minted ids and never-empty-strip blank backfill. - ui: per-pane memory-history routers (registry + sessionStorage location persistence), AppShell chrome outside the route tree bound to the focused pane's router, PaneChrome reconciles each pane's location via setPaneTarget (history tabId-stamping is gone — tab switches are pure store mutations, browser-like), PaneTreeRenderer renders the active tab's layout, DnD merge drops. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
React Doctor found 10 issues in 4 files · 10 warnings. 10 warnings
Reviewed by React Doctor for commit |
|
Reviews (1): Last reviewed commit: "feat(browser-tabs): tab-owned split pane..." | Re-trigger Greptile |
| ALTER TABLE `browser_tabs` DROP COLUMN `dashboard_id`;--> statement-breakpoint | ||
| ALTER TABLE `browser_tabs` DROP COLUMN `task_id`;--> statement-breakpoint | ||
| ALTER TABLE `browser_tabs` DROP COLUMN `channel_id`;--> statement-breakpoint | ||
| ALTER TABLE `browser_tabs` DROP COLUMN `channel_section`;--> statement-breakpoint | ||
| ALTER TABLE `browser_tabs` DROP COLUMN `scroll_state`;--> statement-breakpoint | ||
| ALTER TABLE `browser_tabs` DROP COLUMN `app_view`; |
There was a problem hiding this comment.
Replacement Columns Removed Too Soon
This migration drops the old tab identity columns in the same step that introduces browser_panes. During a staged rollout, any still-running code that reads or writes browser_tabs.dashboard_id, task_id, channel_id, channel_section, scroll_state, or app_view can fail immediately after the migration runs instead of continuing until the replacement has fully rolled out.
Rule Used: When removing database fields, do not remove them ... (source)
Learned From
PostHog/posthog#32876
There was a problem hiding this comment.
This rule targets staged rollouts against a shared server DB; this is the desktop app's per-install local SQLite — the binary and its migrations ship together, so there is no old reader running against the migrated schema. The real re-run hazard (ledger behind schema re-executing 0021, whose backfill reads the dropped columns and whose DROP TABLE would wipe pane rows) is now handled by a migration guard in migrate.ts: the probe fails on a post-0021 schema and the migration is recorded and skipped. Leaving unresolved for a human look, but no column-retention change planned.
Tab-owned panes ships dark: posthog-code-tab-split-panes (default off) must be enabled for pill drags to arm merge drop zones or the drag-end merge branch to run. Reorder is unaffected; existing multi-pane tabs still render and close. No DEV override — off means off in dev too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-running 0021 on a post-0021 schema threw (backfill reads browser_tabs.dashboard_id, which 0021 drops) after its DROP TABLE wiped live browser_panes rows. Guarded migrations probe the pre-migration schema; a failed probe records the migration as applied and skips it, so a ledger-behind-schema DB boots intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
useSyncExternalStore lived in an anonymous default: arrow (rules-of-hooks error) and only subscribed to router registration, so focus moves between registered panes left the devtools on the stale router. Use useFocusedPaneRouter in a named component: registry + tabs-mirror subscription. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
Split panes, v2 model (supersedes the window-owned-panes approach in #3338): the single title-bar tab strip stays, and each tab owns a split-pane layout. Panes are the content units and carry the identity (canvas / task / channel section / app view); a tab's pill renders its focused pane's identity, plus a mini layout glyph of the actual pane tree when the tab holds more than one pane.
mergeTabIntoTabsplices B's whole pane subtree viainsertNodeInLayout), B's pill disappears, focus lands on B's focused pane.PaneChromereconciles every location change intosetPaneTarget, and the old historytabId-stamping machinery is deleted.Model
Migration
0021_tab_owned_panes: newbrowser_panestable, one pane backfilled per existing tab (<tabId>-pane), tab identity columns dropped,layout/focused_pane_idadded. Also heals dev profiles that dogfooded the pre-merge v1 branch (drops v1's window-ownedbrowser_panes, re-addsbrowser_windows.active_tab_id). Verified by replaying the full 0000→0021 chain over seeded v0 data and over a copy of a real v1-dogfooded dev DB.Verified live (CDP)
Strip in title bar; tab click switches pane trees with locations preserved; pill drag → right-edge and bottom-edge merges (row/column splits) with the pill disappearing and the glyph appearing; glyph reflects the real tree; hover X collapses a pane and drops the glyph; new tab lands on the new-tab page; reload restores tabs, split layout, and focus; sidebar navigation targets the focused pane and enables its back button.
Notes
pnpm typecheckgreen across the monorepo; shared suite 111 tests (transforms + layout math +decidePaneNavigation); ui suite 1471 tests green; migration replay harness green. Pre-existingbetter-sqlite3ABI mismatch keeps the sqlite-backed workspace-server tests red onmaintoo (unrelated).🤖 Generated with Claude Code