feat(dashboard): right dock resizes wider (max 1280) + Files goes two-pane when the dock is wide
- RIGHT_DOCK_MAX_WIDTH 720 to 1280 (all clamps/persist/aria funnel through it); CSS --right-dock-max-width raised to calc(space-2xl * 40), keeping min(100%, ...). - The dock now threads its live width into the overflow view render props; the Files entry deterministically forces the LEFT|RIGHT two-pane split when the dock width >= 640 (no longer relying on the flaky @container query that left it stacked top/bottom). Narrow dock keeps the stacked single-panel nav; the expand pop-out stays always two-pane. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,11 @@ The right dock OVERLAYS the page content (floats over the right edge) instead of
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: min(100%, var(--right-dock-min-width, calc(var(--space-2xl) * 8)));
|
||||
max-width: min(100%, var(--right-dock-max-width, calc(var(--space-2xl) * 22)));
|
||||
/*
|
||||
FNXC:RightDock 2026-06-23-00:50:
|
||||
Raised the right-dock max-width default from calc(var(--space-2xl) * 22) (~704px) to calc(var(--space-2xl) * 40) (~1280px) so the dock can be dragged MUCH wider (matching the raised RIGHT_DOCK_MAX_WIDTH JS clamp) and the Files view has room for its tree|viewer two-pane split. The min(100%, ...) wrapper is kept so the dock can never exceed the viewport regardless of the larger cap.
|
||||
*/
|
||||
max-width: min(100%, var(--right-dock-max-width, calc(var(--space-2xl) * 40)));
|
||||
min-height: 0;
|
||||
background: var(--surface);
|
||||
border-left: thin solid var(--border);
|
||||
|
||||
@@ -12,7 +12,11 @@ import "./RightDock.css";
|
||||
|
||||
export const RIGHT_DOCK_DEFAULT_WIDTH = 360;
|
||||
export const RIGHT_DOCK_MIN_WIDTH = 280;
|
||||
export const RIGHT_DOCK_MAX_WIDTH = 720;
|
||||
/*
|
||||
FNXC:RightDock 2026-06-23-00:50:
|
||||
The right-dock resize cap was raised 720 -> 1280 so the user can drag the dock MUCH wider (e.g. to run the Files view as a true two-pane tree|viewer split). The clamp and the persisted-width read both funnel through clampRightDockWidth/RIGHT_DOCK_MAX_WIDTH, so a single constant governs the drag clamp, the keyboard-step clamp, the stored-width read, and the resize-handle aria-valuemax. The CSS still wraps the rendered width in min(100%, ...), so the dock can never exceed the viewport even at the larger cap.
|
||||
*/
|
||||
export const RIGHT_DOCK_MAX_WIDTH = 1280;
|
||||
export const RIGHT_DOCK_WIDTH_STORAGE_KEY = "fusion:right-dock-width";
|
||||
export const RIGHT_DOCK_VIEW_STORAGE_KEY = "fusion:right-dock-view";
|
||||
export const RIGHT_DOCK_OPEN_STORAGE_KEY = "fusion:right-dock-open";
|
||||
@@ -261,7 +265,11 @@ export function RightDock({
|
||||
<div className="right-dock__title" role="heading" aria-level={3}>{selectedEntry.label}</div>
|
||||
</div>
|
||||
<div className="right-dock__body" role="tabpanel" aria-label={selectedEntry.label} data-testid="right-dock-body">
|
||||
{selectedEntry.render?.(renderProps)}
|
||||
{/*
|
||||
FNXC:RightDockFiles 2026-06-23-00:50:
|
||||
Thread the live dock width down to registry render functions as `dockWidth` (alongside surface="dock") so a view can deterministically choose its wide layout from the actual dock size. The Files entry uses this to force two-pane when the dock is wide enough, sidestepping the @container query that never reliably fired in the narrow-vs-wide dock body.
|
||||
*/}
|
||||
{selectedEntry.render?.({ ...renderProps, surface: "dock", dockWidth: width })}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -74,6 +74,22 @@ describe("RightDock", () => {
|
||||
expect(screen.getByTestId("right-dock-tab-git-manager")).toHaveAttribute("aria-selected", "true");
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:RightDockFiles 2026-06-23-00:50:
|
||||
Deterministic dock two-pane decision: the dock threads its measured width to the Files registry render as `dockWidth`, and the Files entry forces DockFilesView layout="two-pane" once that width crosses 640px (no @container gate). A narrow dock (default 360px) stays layout="auto" (stacked single-panel). Assert both via the data-layout attribute the view exposes.
|
||||
*/
|
||||
it("forces the Files two-pane layout when the dock is dragged wide, and stays stacked when narrow", () => {
|
||||
// Narrow default width (360px) -> stacked single-panel.
|
||||
const { unmount } = render(<RightDock open={true} renderProps={renderProps} />);
|
||||
expect(screen.getByTestId("right-dock-files-view")).toHaveAttribute("data-layout", "auto");
|
||||
unmount();
|
||||
|
||||
// Wide persisted width (>= 640px) -> deterministic LEFT|RIGHT two-pane.
|
||||
window.localStorage.setItem(RIGHT_DOCK_WIDTH_STORAGE_KEY, "900");
|
||||
render(<RightDock open={true} renderProps={renderProps} />);
|
||||
expect(screen.getByTestId("right-dock-files-view")).toHaveAttribute("data-layout", "two-pane");
|
||||
});
|
||||
|
||||
it("falls back to Files when storage points at a removed right-dock view", () => {
|
||||
window.localStorage.setItem(RIGHT_DOCK_VIEW_STORAGE_KEY, "documents");
|
||||
render(<RightDock open={true} renderProps={renderProps} />);
|
||||
@@ -160,17 +176,21 @@ describe("RightDock", () => {
|
||||
expect(screen.getByTestId("right-dock-files-view")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:RightDock 2026-06-23-00:50:
|
||||
The resize clamp + persisted-width read both funnel through RIGHT_DOCK_MAX_WIDTH, raised to 1280 so the dock drags MUCH wider. Drag far past the cap (startWidth 360 + 2000 px of leftward travel) and assert it clamps to the new 1280 max, then a keyboard step down lands one shift-step (48px) below the cap. This proves the new cap governs both the pointer drag and the keyboard path.
|
||||
*/
|
||||
it("clamps then persists resize width while open", () => {
|
||||
render(<RightDock open={true} renderProps={renderProps} />);
|
||||
|
||||
const handle = screen.getByTestId("right-dock-resize-handle");
|
||||
fireEvent.pointerDown(handle, { pointerId: 1, clientX: 900 });
|
||||
fireEvent.pointerDown(handle, { pointerId: 1, clientX: 2000 });
|
||||
fireEvent.pointerMove(document, { pointerId: 1, clientX: 0 });
|
||||
fireEvent.pointerUp(document, { pointerId: 1, clientX: 0 });
|
||||
expect(window.localStorage.getItem(RIGHT_DOCK_WIDTH_STORAGE_KEY)).toBe("720");
|
||||
expect(window.localStorage.getItem(RIGHT_DOCK_WIDTH_STORAGE_KEY)).toBe("1280");
|
||||
|
||||
fireEvent.keyDown(handle, { key: "ArrowRight", shiftKey: true });
|
||||
expect(window.localStorage.getItem(RIGHT_DOCK_WIDTH_STORAGE_KEY)).toBe("672");
|
||||
expect(window.localStorage.getItem(RIGHT_DOCK_WIDTH_STORAGE_KEY)).toBe("1232");
|
||||
});
|
||||
|
||||
it("restores persisted width on mount", () => {
|
||||
|
||||
@@ -58,6 +58,11 @@ export interface OverflowViewRenderProps {
|
||||
The compact right-dock body leaves this undefined ("dock"); the RightDockExpandModal sets `surface="expand"` so DockFilesView forces its LEFT|RIGHT two-pane layout regardless of measured container width.
|
||||
*/
|
||||
surface?: "dock" | "expand";
|
||||
/*
|
||||
FNXC:RightDockFiles 2026-06-23-00:50:
|
||||
Measured outer width (px) of the compact right dock body host, threaded from RightDock so a registry render function can deterministically pick a wide layout from the actual dock size. Only set on the "dock" surface; the expand pop-out leaves it undefined (it already forces its wide layout via surface="expand").
|
||||
*/
|
||||
dockWidth?: number;
|
||||
addToast: (message: string, type?: ToastType) => void;
|
||||
settingsLoaded?: boolean;
|
||||
readinessVersion?: number;
|
||||
@@ -100,6 +105,12 @@ export interface OverflowViewVisibilityOptions {
|
||||
pluginDashboardViews?: PluginDashboardViewEntry[];
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:RightDockFiles 2026-06-23-00:50:
|
||||
When the dock body is at least this wide there is clearly room for the Files tree|viewer two-pane split, so the dock forces DockFilesView layout="two-pane" deterministically instead of relying on the unreliable @container dock-files query (its root content-box often measured under the breakpoint and kept the view stacked). Matched to the CSS @container dock-files (min-width: 640px) breakpoint; compared against the threaded outer dock width (the dock chrome padding is small relative to 640px of content, so 640 outer width safely implies enough body width for two panes).
|
||||
*/
|
||||
const RIGHT_DOCK_FILES_TWO_PANE_MIN_WIDTH = 640;
|
||||
|
||||
function wrapOverflowView(node: ReactNode): ReactNode {
|
||||
return (
|
||||
<PageErrorBoundary>
|
||||
@@ -129,12 +140,20 @@ export const STATIC_OVERFLOW_VIEW_ENTRIES: readonly OverflowViewEntry[] = [
|
||||
/*
|
||||
FNXC:RightDockFiles 2026-06-22-15:00:
|
||||
Map the host surface to a deterministic DockFilesView layout. The expand pop-out gets `layout="two-pane"` so the tree+viewer render LEFT|RIGHT without depending on the @container query matching inside the modal body. The compact dock keeps `layout="auto"` (the container-query single-panel stack).
|
||||
|
||||
FNXC:RightDockFiles 2026-06-23-00:50:
|
||||
Extend the deterministic approach to the DOCK itself: when the dock body is dragged wide (threaded `dockWidth` >= 640px) force the same LEFT|RIGHT two-pane split deterministically, NOT via the unreliable @container dock-files query (which kept the wide dock stacked because the root content-box measured under the breakpoint). Below the threshold the narrow dock keeps the single-panel stacked nav. The expand pop-out is always two-pane.
|
||||
*/
|
||||
render: (props) => wrapOverflowView(
|
||||
<DockFilesView
|
||||
projectId={props.projectId}
|
||||
openFile={props.openFile}
|
||||
layout={props.surface === "expand" ? "two-pane" : "auto"}
|
||||
layout={
|
||||
props.surface === "expand"
|
||||
|| (props.surface === "dock" && (props.dockWidth ?? 0) >= RIGHT_DOCK_FILES_TWO_PANE_MIN_WIDTH)
|
||||
? "two-pane"
|
||||
: "auto"
|
||||
}
|
||||
/>,
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user