feat: open Changes as a workspace tab (#2298)

Open the complete working comparison in a desktop workspace tab while preserving inline sidebar diffs when the tab is closed.

Working and commit comparisons share one diff panel, and workspace layout is the sole owner of live tab persistence and identity.

Refs #1520
This commit is contained in:
nikuscs
2026-07-22 20:57:18 +02:00
committed by GitHub
parent 76a5edb020
commit 512c9b31a8
61 changed files with 1581 additions and 2012 deletions
@@ -15,9 +15,8 @@ vi.mock("@react-native-async-storage/async-storage", () => {
};
});
import type { WorkspaceTab } from "@/stores/workspace-tabs-store";
import { buildWorkspaceTabPersistenceKey, type WorkspaceTab } from "@/workspace-tabs/model";
import {
buildWorkspaceTabPersistenceKey,
collectAllPanes,
collectAllTabs,
createWorkspaceLayoutStore,
@@ -27,8 +26,10 @@ import {
getFocusedBrowserId,
getTreeDepth,
insertSplit,
normalizeLayout,
removePaneFromTree,
removeTabFromTree,
stripEphemeralTabsFromLayout,
type SplitNode,
type SplitPane,
} from "@/stores/workspace-layout-store";
@@ -1128,6 +1129,73 @@ describe("workspace-layout-store actions", () => {
]);
});
it("persists working diff tabs while stripping commit diff tabs", () => {
const workspaceKey = createWorkspaceKey();
const store = workspaceLayoutStore.getState();
store.openTabFocused(workspaceKey, {
kind: "working_diff",
focusPath: "src/a.ts",
});
store.openTabFocused(workspaceKey, { kind: "commit_diff", sha: "abc123" });
const partialize = workspaceLayoutStore.persist.getOptions().partialize;
expect(partialize).toBeTypeOf("function");
if (!partialize) {
throw new Error("Workspace layout partialize function is missing");
}
const currentState = workspaceLayoutStore.getState();
const layout = stripEphemeralTabsFromLayout(currentState.layoutByWorkspace[workspaceKey]);
const persisted = partialize(currentState);
expect(persisted).toEqual({
layoutByWorkspace: { [workspaceKey]: layout },
splitSizesByWorkspace: currentState.splitSizesByWorkspace,
});
expect(layout && collectAllTabs(layout.root).map((tab) => tab.target)).toEqual([
{
kind: "working_diff",
focusPath: "src/a.ts",
},
]);
});
it("canonicalizes comparison-specific working diff tab ids from persisted layouts", () => {
const legacyTabId = "working_diff_uncommitted_0_n";
const layout = normalizeLayout({
root: {
kind: "pane",
pane: {
id: "main",
tabIds: [legacyTabId],
focusedTabId: legacyTabId,
tabs: [
{
tabId: legacyTabId,
createdAt: 1,
target: {
kind: "working_diff",
focusPath: "src/a.ts",
mode: "uncommitted",
baseRef: null,
ignoreWhitespace: false,
},
},
],
},
},
focusedPaneId: "main",
});
expect(collectAllTabs(layout.root)).toEqual([
{
tabId: "working_diff",
target: { kind: "working_diff", focusPath: "src/a.ts" },
createdAt: 1,
},
]);
});
it("resizeSplit keeps sizes normalized while enforcing the minimum proportion", () => {
useWorkspaceLayoutIds(
"eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee",