You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Clicking a file in the Changes sidebar opens a split pane even when Changes is set to open in a new tab #9039
I set Settings → Behavior → Changes open target to "New tab". The Changes button in the top bar respects it and opens the diff in its own tab. Clicking a specific file in the right sidebar's Changes list does not: it opens the diff as a split pane next to the agent session, so I move it to its own tab by hand every time.
Settings → Files → Sidebar file rows doesn't help either. "Open in tab" also opens a split, and "Open in new tab" opens a new tab for every file I click.
Expected: with "New tab" set, clicking a file in the Changes list opens the diff in a Changes tab, or reuses the one already open.
Steps to reproduce
Settings → Behavior → Changes open target: "New tab".
Open a workspace with one agent session in the active tab and no diff open.
In the right sidebar's Changes tab, click a changed file.
The diff opens as a split pane next to the session instead of in a new tab.
The setting is ignored on this path. Clicking a file in the Changes sidebar goes to openDiffPane, and that function never reads "Changes open target". Only the top-bar Changes button reads it.
Summary
With Settings → Behavior → Changes open target set to "New tab", the top-bar Changes button opens or reuses a diff tab as expected. A plain click on a file in the right sidebar's Changes list does not: when no diff pane exists yet, it opens a split next to the active session. The "Sidebar file rows" setting can't work around this. Its "pane" action becomes a diff with openInNewTab = false, which ends up as the same split. Its "newTab" action becomes diffNewTab, which always adds a new tab and never reuses one. The report matches the code.
Affected code
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useWorkspacePaneOpeners/useWorkspacePaneOpeners.ts:216: openChangesPane (top-bar button) passes useSettings.getState().changesOpenTarget to openChangesPaneInStore. This is the only place the setting is read.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/utils/openChangesPaneInStore/openChangesPaneInStore.ts:40-47: for "tab", it focuses the first diff pane in tab order, or calls addTab if there is none.
useWorkspacePaneOpeners.ts:68-136: openDiffPane, which handles the sidebar file click. It has three branches:
openInNewTab → addTab every time (L85-99).
Otherwise, reuse the first diff pane in any tab (L101-122).
If there is none, state.openPane(...) (L123-133), which splits the active tab's center area.
.../WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/ChangesTreeView/ChangesTreeView.tsx:251-260 (Pierre onSelect, always false) and :280-289 (onSelectDiff from the click policy).
apps/desktop/src/renderer/lib/clickPolicy/policies/changesSidebarFilePolicy.ts:20-28: maps the "Sidebar file rows" policy (useSidebarFilePolicy) to intents. newTab → diffNewTab; a plain click with pane → diff.
The setting was added only to the "open Changes" entry point (openChangesPaneInStore). The per-file path (openDiffPane) was not updated. In the reported case there is no diff pane yet, so the reuse loop finds nothing and the code falls through to state.openPane(...) at useWorkspacePaneOpeners.ts:123. That creates a split, whatever the setting says. If a diff tab is already open, the reuse loop at L101-122 already does what the user expects. So the bug only shows on the first file click.
The "Sidebar file rows" setting is a separate click policy. It has no option meaning "reuse the Changes tab", so it can't fix this. Its "Open in new tab" calls addTab unconditionally (L85-99), which is why every click makes a new tab.
Reproduction
Settings → Behavior → Changes open target: "New tab". Leave "Sidebar file rows" at its default.
Open a workspace whose active tab holds only an agent session, with no diff pane in any tab.
Click a changed file in the right sidebar's Changes list. The diff opens as a split in the active tab.
Compare with the top-bar Changes button from the same state. It opens a dedicated tab.
Sketch for a bun:test: build a WorkspaceStore with one tab holding a terminal or chat pane. Run the no-existing-diff branch of openDiffPane (probably easiest after pulling it out into a pure openDiffPaneInStore(store, args, target) helper, like openChangesPaneInStore). Assert that with target = "tab" the store ends with 2 tabs and the new diff pane alone in the active tab. Today you'd get 1 tab with 2 panes.
Fix direction
In openDiffPane, read useSettings.getState().changesOpenTarget. When it is "tab" and the reuse loop finds no diff pane, call state.addTab({ panes: [diffPane] }) instead of state.openPane(...). This matches openChangesPaneInStore. The best option is to move the logic into a shared store helper so the two entry points can't drift apart again.
Optional, product decision: under "tab", the sidebar's diffNewTab intent could also reuse the existing Changes tab instead of always adding one. That may break users who want one tab per file on purpose, so it's best decided separately.
Smaller mismatch in "pane" mode: openChangesPaneInStore only reuses a diff pane in the active tab (L27-36). openDiffPane reuses the first diff pane in any tab. A shared helper should settle which one is right.
Activity
github-actions commentedon Oct 29, 2026
The setting is ignored on this path. Clicking a file in the Changes sidebar goes to
openDiffPane, and that function never reads "Changes open target". Only the top-bar Changes button reads it.Summary
With Settings → Behavior → Changes open target set to "New tab", the top-bar Changes button opens or reuses a diff tab as expected. A plain click on a file in the right sidebar's Changes list does not: when no diff pane exists yet, it opens a split next to the active session. The "Sidebar file rows" setting can't work around this. Its "pane" action becomes a diff with
openInNewTab = false, which ends up as the same split. Its "newTab" action becomesdiffNewTab, which always adds a new tab and never reuses one. The report matches the code.Affected code
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useWorkspacePaneOpeners/useWorkspacePaneOpeners.ts:216:openChangesPane(top-bar button) passesuseSettings.getState().changesOpenTargettoopenChangesPaneInStore. This is the only place the setting is read.apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/utils/openChangesPaneInStore/openChangesPaneInStore.ts:40-47: for"tab", it focuses the firstdiffpane in tab order, or callsaddTabif there is none.useWorkspacePaneOpeners.ts:68-136:openDiffPane, which handles the sidebar file click. It has three branches:openInNewTab→addTabevery time (L85-99).diffpane in any tab (L101-122).state.openPane(...)(L123-133), which splits the active tab's center area.apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx:673:onSelectDiffFile={openDiffPane}..../WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/ChangesTreeView/ChangesTreeView.tsx:251-260(PierreonSelect, alwaysfalse) and:280-289(onSelectDifffrom the click policy).apps/desktop/src/renderer/lib/clickPolicy/policies/changesSidebarFilePolicy.ts:20-28: maps the "Sidebar file rows" policy (useSidebarFilePolicy) to intents.newTab→diffNewTab; a plain click withpane→diff.apps/desktop/src/renderer/lib/clickPolicy/usePierreChangesSidebarRowClickPolicy.ts:66-67:diffNewTab→onSelectDiff(path, true),diff→onSelectDiff(path, false).Suspected root cause
The setting was added only to the "open Changes" entry point (
openChangesPaneInStore). The per-file path (openDiffPane) was not updated. In the reported case there is no diff pane yet, so the reuse loop finds nothing and the code falls through tostate.openPane(...)atuseWorkspacePaneOpeners.ts:123. That creates a split, whatever the setting says. If a diff tab is already open, the reuse loop at L101-122 already does what the user expects. So the bug only shows on the first file click.The "Sidebar file rows" setting is a separate click policy. It has no option meaning "reuse the Changes tab", so it can't fix this. Its "Open in new tab" calls
addTabunconditionally (L85-99), which is why every click makes a new tab.Reproduction
Sketch for a
bun:test: build aWorkspaceStorewith one tab holding a terminal or chat pane. Run the no-existing-diff branch ofopenDiffPane(probably easiest after pulling it out into a pureopenDiffPaneInStore(store, args, target)helper, likeopenChangesPaneInStore). Assert that withtarget = "tab"the store ends with 2 tabs and the new diff pane alone in the active tab. Today you'd get 1 tab with 2 panes.Fix direction
openDiffPane, readuseSettings.getState().changesOpenTarget. When it is"tab"and the reuse loop finds no diff pane, callstate.addTab({ panes: [diffPane] })instead ofstate.openPane(...). This matchesopenChangesPaneInStore. The best option is to move the logic into a shared store helper so the two entry points can't drift apart again."tab", the sidebar'sdiffNewTabintent could also reuse the existing Changes tab instead of always adding one. That may break users who want one tab per file on purpose, so it's best decided separately."pane"mode:openChangesPaneInStoreonly reuses a diff pane in the active tab (L27-36).openDiffPanereuses the first diff pane in any tab. A shared helper should settle which one is right.