feat: refresh workbench visual layout

This commit is contained in:
2026-07-27 11:50:29 +08:00
parent b9bc9c3d39
commit f0485ca86e
33 changed files with 1624 additions and 353 deletions
@@ -1,5 +1,11 @@
import { describe, expect, it } from "vitest";
import { getWorkbenchBasemapTone } from "./workbench-layout";
import {
clampAgentPanelWidth,
getAgentPanelMaxWidth,
getWorkbenchBasemapTone,
getWorkbenchCameraPadding,
getWorkbenchViewportLayout
} from "./workbench-layout";
describe("workbench basemap surface tone", () => {
it.each([
@@ -11,3 +17,60 @@ describe("workbench basemap surface tone", () => {
expect(getWorkbenchBasemapTone(baseLayerId)).toBe(expectedTone);
});
});
describe("workbench floating panels", () => {
it("uses bounded desktop and wide panel widths", () => {
expect(getWorkbenchViewportLayout(1440)).toMatchObject({
agentWidth: 460,
conditionWidth: 432,
conditionExpandedWidth: 880,
toolbarWidth: 48
});
expect(getWorkbenchViewportLayout(1536)).toMatchObject({
agentWidth: 500,
conditionExpandedWidth: 960
});
});
it("tracks the expanded condition panel in camera padding", () => {
expect(
getWorkbenchCameraPadding(1440, {
agentOpen: true,
conditionOpen: true,
conditionExpanded: false
})
).toEqual({ top: 72, right: 504, bottom: 32, left: 484 });
expect(
getWorkbenchCameraPadding(1440, {
agentOpen: true,
conditionOpen: true,
conditionExpanded: true
})
).toEqual({ top: 72, right: 952, bottom: 32, left: 484 });
});
it("uses the committed Agent width without exceeding the hard limit", () => {
expect(
getWorkbenchCameraPadding(1920, {
agentOpen: true,
agentWidth: 620,
conditionOpen: true
})
).toEqual({ top: 72, right: 504, bottom: 32, left: 644 });
expect(
getWorkbenchCameraPadding(1920, {
agentOpen: true,
agentWidth: 900,
conditionOpen: false
}).left
).toBe(644);
});
it("reserves room for the compact condition panel on narrow desktops", () => {
expect(getAgentPanelMaxWidth(1024)).toBe(484);
expect(clampAgentPanelWidth(620, 1024)).toBe(484);
expect(getAgentPanelMaxWidth(1440)).toBe(620);
});
});
@@ -4,32 +4,58 @@ export const WORKBENCH_LAYOUT = {
persistentConditionMinWidth: 1280,
wideMinWidth: 1536,
collapsedAgentWidth: 72,
maxAgentViewportRatio: 0.5,
maxAgentWidth: 620,
mapEdgeGap: 24,
desktop: {
agentWidth: 460,
conditionWidth: 432,
conditionExpandedWidth: 880,
toolbarWidth: 48,
tickerWidth: 420
},
wide: {
agentWidth: 500,
conditionWidth: 432,
conditionExpandedWidth: 960,
toolbarWidth: 48,
tickerWidth: 460
},
mobileSheet: {
peekHeight: 96,
halfViewportRatio: 0.52,
fullTopGap: 68
}
} as const;
export function clampAgentPanelWidth(width: number, viewportWidth: number) {
const minWidth = getWorkbenchViewportLayout(viewportWidth).agentWidth;
const maxWidth = viewportWidth * WORKBENCH_LAYOUT.maxAgentViewportRatio;
return Math.round(Math.min(Math.max(width, minWidth), maxWidth));
return Math.round(
Math.min(Math.max(width, minWidth), getAgentPanelMaxWidth(viewportWidth))
);
}
export function getAgentPanelMaxWidth(viewportWidth: number) {
const layout = getWorkbenchViewportLayout(viewportWidth);
const widthWithCompactCondition =
viewportWidth -
12 -
WORKBENCH_LAYOUT.mapEdgeGap -
layout.conditionWidth -
WORKBENCH_LAYOUT.mapEdgeGap -
layout.toolbarWidth;
return Math.max(
layout.agentWidth,
Math.min(WORKBENCH_LAYOUT.maxAgentWidth, widthWithCompactCondition)
);
}
export type WorkbenchPanelState = {
agentOpen: boolean;
agentWidth?: number;
conditionOpen: boolean;
conditionExpanded?: boolean;
};
export type WorkbenchBasemapTone = "light" | "satellite" | "none";
@@ -37,6 +63,7 @@ export type WorkbenchBasemapTone = "light" | "satellite" | "none";
export type WorkbenchViewportLayout = {
agentWidth: number;
conditionWidth: number;
conditionExpandedWidth: number;
toolbarWidth: number;
tickerWidth: number;
};
@@ -65,8 +92,16 @@ export function getWorkbenchCameraPadding(viewportWidth: number, panels: Workben
}
const layout = getWorkbenchViewportLayout(viewportWidth);
const agentWidth = panels.agentOpen ? layout.agentWidth : WORKBENCH_LAYOUT.collapsedAgentWidth;
const conditionWidth = panels.conditionOpen ? layout.conditionWidth : 0;
const agentWidth = panels.agentOpen
? panels.agentWidth === undefined
? layout.agentWidth
: clampAgentPanelWidth(panels.agentWidth, viewportWidth)
: WORKBENCH_LAYOUT.collapsedAgentWidth;
const conditionWidth = panels.conditionOpen
? panels.conditionExpanded
? layout.conditionExpandedWidth
: layout.conditionWidth
: 0;
return {
top: WORKBENCH_LAYOUT.headerHeight + 16,
@@ -82,6 +117,8 @@ export function getWorkbenchLayoutCssVariables() {
"--workbench-agent-width-wide": `${WORKBENCH_LAYOUT.wide.agentWidth}px`,
"--workbench-condition-width": `${WORKBENCH_LAYOUT.desktop.conditionWidth}px`,
"--workbench-condition-width-wide": `${WORKBENCH_LAYOUT.wide.conditionWidth}px`,
"--workbench-condition-expanded-width": `${WORKBENCH_LAYOUT.desktop.conditionExpandedWidth}px`,
"--workbench-condition-expanded-width-wide": `${WORKBENCH_LAYOUT.wide.conditionExpandedWidth}px`,
"--workbench-toolbar-width": `${WORKBENCH_LAYOUT.desktop.toolbarWidth}px`,
"--workbench-ticker-width": `${WORKBENCH_LAYOUT.desktop.tickerWidth}px`,
"--workbench-ticker-width-wide": `${WORKBENCH_LAYOUT.wide.tickerWidth}px`