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:
@@ -80,6 +80,19 @@ describe("working diff tab identity", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("workspace utility panel identity", () => {
|
||||
it.each(["files", "pull_request"] as const)(
|
||||
"normalizes and deterministically keys %s",
|
||||
(kind) => {
|
||||
const target = { kind };
|
||||
|
||||
expect(normalizeWorkspaceTabTarget(target)).toEqual(target);
|
||||
expect(buildDeterministicWorkspaceTabId(target)).toBe(kind);
|
||||
expect(workspaceTabTargetsEqual(target, target)).toBe(true);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("commit diff tab identity", () => {
|
||||
it("keys a commit diff tab by its sha", () => {
|
||||
expect(buildDeterministicWorkspaceTabId({ kind: "commit_diff", sha: "abc123" })).toBe(
|
||||
|
||||
@@ -49,6 +49,9 @@ function normalizeSimpleWorkspaceTabTarget(value: WorkspaceTabTarget): Workspace
|
||||
const browserId = trimNonEmpty(value.browserId);
|
||||
return browserId ? { kind: "browser", browserId } : null;
|
||||
}
|
||||
case "files":
|
||||
case "pull_request":
|
||||
return { kind: value.kind };
|
||||
case "setup": {
|
||||
const workspaceId = trimNonEmpty(value.workspaceId);
|
||||
return workspaceId ? { kind: "setup", workspaceId } : null;
|
||||
@@ -121,6 +124,12 @@ function secondaryWorkspaceTabTargetsEqual(
|
||||
if (left.kind === "working_diff" && right.kind === "working_diff") {
|
||||
return left.focusPath === right.focusPath && left.focusRequestId === right.focusRequestId;
|
||||
}
|
||||
if (left.kind === "files" && right.kind === "files") {
|
||||
return true;
|
||||
}
|
||||
if (left.kind === "pull_request" && right.kind === "pull_request") {
|
||||
return true;
|
||||
}
|
||||
if (left.kind === "setup" && right.kind === "setup") {
|
||||
return left.workspaceId === right.workspaceId;
|
||||
}
|
||||
@@ -188,6 +197,9 @@ export function buildDeterministicWorkspaceTabId(target: WorkspaceTabTarget): st
|
||||
if (target.kind === "working_diff") {
|
||||
return "working_diff";
|
||||
}
|
||||
if (target.kind === "files" || target.kind === "pull_request") {
|
||||
return target.kind;
|
||||
}
|
||||
return `file_${target.path}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ export type WorkspaceTabTarget =
|
||||
| { kind: "provider_subagent"; parentAgentId: string; subagentId: string }
|
||||
| { kind: "terminal"; terminalId: string }
|
||||
| { kind: "browser"; browserId: string }
|
||||
| { kind: "files" }
|
||||
| { kind: "pull_request" }
|
||||
| WorkspaceFileTabTarget
|
||||
| WorkspaceWorkingDiffTabTarget
|
||||
| { kind: "setup"; workspaceId: string }
|
||||
|
||||
Reference in New Issue
Block a user