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:
@@ -31,9 +31,9 @@ export const MIN_EXPLORER_SIDEBAR_WIDTH = 280;
|
||||
// Upper bound is intentionally generous; desktop resizing enforces a min-chat-width constraint.
|
||||
export const MAX_EXPLORER_SIDEBAR_WIDTH = 2000;
|
||||
|
||||
export const DEFAULT_EXPLORER_FILES_SPLIT_RATIO = 0.38;
|
||||
export const MIN_EXPLORER_FILES_SPLIT_RATIO = 0.2;
|
||||
export const MAX_EXPLORER_FILES_SPLIT_RATIO = 0.8;
|
||||
export const DEFAULT_TREE_RAIL_WIDTH = 320;
|
||||
export const MIN_TREE_RAIL_WIDTH = 200;
|
||||
export const MAX_TREE_RAIL_WIDTH = 600;
|
||||
|
||||
export interface PanelVisibilityState {
|
||||
isAgentListOpen: boolean;
|
||||
@@ -70,8 +70,8 @@ export function clampExplorerWidth(width: number): number {
|
||||
return clampNumber(width, MIN_EXPLORER_SIDEBAR_WIDTH, MAX_EXPLORER_SIDEBAR_WIDTH);
|
||||
}
|
||||
|
||||
export function clampExplorerFilesSplitRatio(ratio: number): number {
|
||||
return clampNumber(ratio, MIN_EXPLORER_FILES_SPLIT_RATIO, MAX_EXPLORER_FILES_SPLIT_RATIO);
|
||||
export function clampTreeRailWidth(width: number): number {
|
||||
return clampNumber(width, MIN_TREE_RAIL_WIDTH, MAX_TREE_RAIL_WIDTH);
|
||||
}
|
||||
|
||||
export function selectPanelVisibility(
|
||||
@@ -190,6 +190,7 @@ export const PanelPersistedStateSchema = z.strictObject({
|
||||
explorerSortOption: z.enum(["name", "modified", "size"]).optional(),
|
||||
explorerShowHiddenFiles: z.boolean().optional(),
|
||||
explorerFilesSplitRatio: z.number().optional(),
|
||||
treeRailWidth: z.number().optional(),
|
||||
});
|
||||
|
||||
type MigratablePanelState = z.infer<typeof PanelPersistedStateSchema>;
|
||||
@@ -198,11 +199,6 @@ function migratePanelV2Explorer(state: MigratablePanelState, isWeb: boolean): vo
|
||||
if (isWeb && typeof state.explorerWidth === "number" && state.explorerWidth === 400) {
|
||||
state.explorerWidth = DEFAULT_EXPLORER_SIDEBAR_WIDTH;
|
||||
}
|
||||
if (typeof state.explorerFilesSplitRatio !== "number") {
|
||||
state.explorerFilesSplitRatio = DEFAULT_EXPLORER_FILES_SPLIT_RATIO;
|
||||
} else {
|
||||
state.explorerFilesSplitRatio = clampExplorerFilesSplitRatio(state.explorerFilesSplitRatio);
|
||||
}
|
||||
}
|
||||
|
||||
function migratePanelV3Explorer(state: MigratablePanelState, isWeb: boolean): void {
|
||||
@@ -253,6 +249,15 @@ function migratePanelDesktopFocusMode(state: MigratablePanelState): void {
|
||||
}
|
||||
}
|
||||
|
||||
function migrateTreeRailWidth(state: MigratablePanelState, version: number): void {
|
||||
if (version < 13 || typeof state.treeRailWidth !== "number") {
|
||||
delete state.explorerFilesSplitRatio;
|
||||
state.treeRailWidth = DEFAULT_TREE_RAIL_WIDTH;
|
||||
return;
|
||||
}
|
||||
state.treeRailWidth = clampTreeRailWidth(state.treeRailWidth);
|
||||
}
|
||||
|
||||
export function migratePanelState(
|
||||
persistedState: unknown,
|
||||
version: number,
|
||||
@@ -302,6 +307,7 @@ export function migratePanelState(
|
||||
if (typeof state.explorerShowHiddenFiles !== "boolean") {
|
||||
state.explorerShowHiddenFiles = true;
|
||||
}
|
||||
migrateTreeRailWidth(state, version);
|
||||
if (version < 12) {
|
||||
// Compact panel position is transient UI state. Cold starts always begin
|
||||
// at content, regardless of what an older version persisted.
|
||||
|
||||
Reference in New Issue
Block a user