Skip to content

feat(fork): split the right panel by dragging a tab - #164

Open
orbitingflea wants to merge 2 commits into
mainfrom
feat/right-panel-split
Open

orbitingflea wants to merge 2 commits into
mainfrom
feat/right-panel-split

Conversation

@orbitingflea

Copy link
Copy Markdown
Owner

This pull request lets a user split the right panel into two panes by dragging a tab, and makes a press anywhere on a tab switch to it at once.

改了什么行为

Behaviour change: yes, a web or desktop user who drops a right-panel tab on the left or right third of the panel's content gets two side-by-side panes, each with its own tabs, and a press anywhere on a tab outside its close and mute buttons now switches to it.

  • The tab menu can split, move a tab to the other pane and unsplit.
  • New tabs open in the pane last used, and panel shortcuts act on that pane.
  • Closing or moving away a pane's last tab ends the split.
  • A narrow panel shows all tabs in one strip and keeps the split for when there is room.

Why

Users asked for a split like Windows Snap, started by dragging a tab. They also reported tab clicks that switched late or not at all. Only the title text took a click, 6 px of hand movement turned the click into a drag, and the switch waited for mouse-up.

Checks

A new browser test presses a tab's edges and presses with hand jitter. It fails on main and passes now. New tests cover the split state, the drop targets and the split view, and the repo's checks for changed files pass. In the web app at desktop and phone width, tab presses, drag to split, moving tabs, the splitter, reload and the narrow layout worked. The desktop app was not run.

How to undo

Revert the two commits. No server, wire or database change. Older code runs its migration on the stored panel version 15, which drops the split.

Design notes
  • During a drag a box shows the half the new pane will take. The centre third does nothing, and a panel with one tab offers no split. The user drags the splitter to resize, and a double-click or Enter sets it back to half. The menu's Close others, Close to the right and Close all act on the strip that opened it. A new split widens a panel narrower than 800 px as far as the panel may grow, and ending the split gives that width back unless the user resized the panel in between. The one-strip layout applies in a window 980 px wide or less and in an inline panel under 441 px. The mobile app has no right panel.
  • ThreadRightPanelState gets one optional split field with rightIds, otherShownId and ratio. surfaces stays the one list of tabs, the left pane's first. The focused pane is the one that holds activeSurfaceId, so code that reads the active tab keeps working. The stored version goes from 14 to 15 with a migration, and a persist merge repairs a damaged split at the same version. A reload keeps the split.
  • updateThreadStateMap passes every write through reconcileSplit with its kind (user, close or automatic). New tabs join the focused pane. A replacement that empties a pane, such as a folder tree that becomes a file, fills that pane. A close falls back to a neighbour in its own pane. When a pane empties, the split ends. updateSplit(ref, change) is the one layout action.
  • The proactive diff does not open while a diff or a pull request shows in either pane.
  • ChatView renders its panel through RightPanelSplitView, with one pane or two. The left pane keeps its React identity, so a split does not remount what it shows. The Pull requests page has no split but gets the new tab press.
  • One dnd-kit context spans the panel. During a tab drag a transparent layer covers the window, so code views under the pointer do not react to the drag.
  • A primary mouse or pen pointerdown activates a tab, before the 6 px drag threshold. Close, mute and rename do not activate, and middle-click and right-click do not switch. Touch and keyboard activate on click. A touch drag needs a 250 ms hold, so a swipe scrolls the strip.
  • A cancelled drag leaves the pressed tab selected. After a reload, ending a split no longer gives back the width the split took.
  • Fork code is in apps/web/src/fork/rightPanelSplit/. Upstream files: ChatView.tsx +72 −60 (mostly a re-indent), RightPanelTabs.tsx +61 −30, RightPanelTabs.browser.test.tsx +98, rightPanelStore.ts +34 −9, useRightPanelSurfaceClose.ts +14 −11, ChatRightPanel.tsx +9 −4, useChatTerminalActions.ts +8 −4, usePreviewPanelInlineSize.ts +5 −2, PreviewPanel.tsx +4, PreviewView.tsx +4 −2, PullRequestGuideTab.tsx +4 −1, RightPanelSurfaceContent.tsx +2 −1, PullRequestDetailPanel.tsx +1. Without whitespace, all upstream files come to +262 −70. README.md gets one line.

orbitingflea and others added 2 commits November 15, 2026 14:38
A click on a right-panel tab's padding or its top and bottom edges does
nothing, a click with a few pixels of hand movement is swallowed by the
tab drag sensor, and a working click switches only after mouse-up. This
browser test presses a tab's edges and its label with jitter and checks
that the tab is active before the button is released, that focus stays
where the switch put it, that touch and keyboard switch on click, and
that the close button closes without switching. It fails on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drag a right-panel tab onto the outer third of the panel content: a box
shows the half the new pane will take, and the drop splits the panel into
two side-by-side panes, each with its own tab strip, as with Windows Snap.
Tabs move between panes by drag or the tab menu (Split right, Split left,
Move to the other pane, Unsplit). A splitter resizes the panes. Closing or
moving away a pane's last tab ends the split. New tabs open in the focused
pane. A narrow panel shows all tabs in one strip and keeps the split for
later. A new split widens a narrow panel and gives the width back when it
ends.

The layout is one optional field on the panel's per-thread record, kept
valid by one reconcile step in the store's write path, so the existing
pane-blind open and close calls keep working. The logic lives in
apps/web/src/fork/rightPanelSplit/.

Tabs also switch on a primary press anywhere on the tab instead of on a
click on the title text, so edge clicks and clicks with hand movement no
longer miss, and the switch runs while the button is still held. Touch
and keyboard switch on click; a touch drag needs a short hold so a swipe
scrolls the strip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:XXL labels Nov 15, 2026
@github-actions

Copy link
Copy Markdown

This PR changes the test suite by +27 test cases (browser +8, unit +19, server 0).

New browser test files: apps/web/src/fork/rightPanelSplit/RightPanelSplitView.browser.test.tsx.
Each browser test file costs CI seconds of module loading before its first test runs; a unit test costs milliseconds.

Keep tests that would fail for a real reason, and test logic below the UI where it can be. See Testing.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant