Make the workspace right pane a first-class tab host (#3287)

* feat(app): add persistent workspace side panes

Keep the common review and file-browsing workflow beside the active agent without tearing down pane contents. Workspace tabs now share one compact chip language, assistant file links route into the retained right pane, and diff/file trees share stable geometry.

* fix(app): localize pull request empty state

* test(app): update explorer pane coverage

* fix(app): preserve explorer pane workflows

* test(app): make explorer tab selection idempotent

* fix(app): persist workspace review pins

* fix(app): preserve sidebar scrim drag hook

* fix(app): restore companion pane interactions

* test(app): follow selected sidebar surface
This commit is contained in:
Mohamed Boudra
2026-08-16 18:18:32 +02:00
committed by GitHub
parent 6dcf6a4b08
commit ab274d635e
81 changed files with 2900 additions and 1051 deletions
@@ -2,13 +2,14 @@ import { describe, expect, it } from "vitest";
import type { SplitNode } from "@/stores/workspace-layout-store";
import { findAdjacentPane } from "./split-navigation";
function createPaneNode(id: string): SplitNode {
function createPaneNode(id: string, hidden = false): SplitNode {
return {
kind: "pane",
pane: {
id,
tabIds: [],
focusedTabId: null,
...(hidden ? { hidden: true } : {}),
},
};
}
@@ -80,4 +81,15 @@ describe("findAdjacentPane", () => {
expect(findAdjacentPane(root, "top", "down")).toBe("bottom-left");
expect(findAdjacentPane(root, "bottom-right", "up")).toBe("top");
});
it("skips hidden panes", () => {
const root = createGroupNode({
direction: "horizontal",
sizes: [0.3, 0.3, 0.4],
children: [createPaneNode("left"), createPaneNode("hidden", true), createPaneNode("right")],
});
expect(findAdjacentPane(root, "left", "right")).toBe("right");
expect(findAdjacentPane(root, "hidden", "right")).toBeNull();
});
});
@@ -171,6 +171,9 @@ function collectPaneBounds(
bounds: { left: number; top: number; right: number; bottom: number },
): PaneBounds[] {
if (node.kind === "pane") {
if (node.pane.hidden === true) {
return [];
}
return [
{
paneId: node.pane.id,