From 28287a1447c6dd2adf96e054bb97196a750eb285 Mon Sep 17 00:00:00 2001 From: Huarch Date: Wed, 19 Aug 2026 18:55:43 +0800 Subject: [PATCH] fix: coordinate mobile workbench overlays Independent fixed offsets caused the timeline, map dock, tool panel, and zoom controls to overlap. Derive them from one layout function and scope collapse to the expanded timeline state. --- .../workbench/layout/workbench-layout.test.ts | 16 ++++- .../workbench/layout/workbench-layout.ts | 20 ++++++ src/features/workbench/map-workbench-page.tsx | 61 +++++++++++++------ tests/browser/button-system.e2e.ts | 59 ++++++++++++++++++ 4 files changed, 136 insertions(+), 20 deletions(-) diff --git a/src/features/workbench/layout/workbench-layout.test.ts b/src/features/workbench/layout/workbench-layout.test.ts index b1094c3..4f072eb 100644 --- a/src/features/workbench/layout/workbench-layout.test.ts +++ b/src/features/workbench/layout/workbench-layout.test.ts @@ -5,7 +5,8 @@ import { getAgentPanelMaxWidth, getWorkbenchBasemapTone, getWorkbenchCameraPadding, - getWorkbenchViewportLayout + getWorkbenchViewportLayout, + resolveMobileWorkbenchOverlayLayout } from "./workbench-layout"; describe("workbench basemap surface tone", () => { @@ -20,6 +21,19 @@ describe("workbench basemap surface tone", () => { }); describe("workbench floating panels", () => { + it("derives mobile dock and map control offsets from one overlay stack", () => { + expect(resolveMobileWorkbenchOverlayLayout(true)).toEqual({ + timelineBottom: 12, + dockBottom: 68, + mapControlBottom: 120 + }); + expect(resolveMobileWorkbenchOverlayLayout(false)).toEqual({ + timelineBottom: 12, + dockBottom: 12, + mapControlBottom: 64 + }); + }); + it("uses bounded desktop and wide panel widths", () => { expect(getWorkbenchViewportLayout(1440)).toMatchObject({ agentWidth: 500, diff --git a/src/features/workbench/layout/workbench-layout.ts b/src/features/workbench/layout/workbench-layout.ts index d3af352..4778e2d 100644 --- a/src/features/workbench/layout/workbench-layout.ts +++ b/src/features/workbench/layout/workbench-layout.ts @@ -26,9 +26,29 @@ export const WORKBENCH_LAYOUT = { mobileSheet: { halfViewportRatio: 0.52, fullTopGap: 68 + }, + mobileOverlay: { + edgeInset: 12, + gap: 8, + dockHeight: 44, + timelineSummaryHeight: 48 } } as const; +export function resolveMobileWorkbenchOverlayLayout(timelineVisible: boolean) { + const { edgeInset, gap, dockHeight, timelineSummaryHeight } = + WORKBENCH_LAYOUT.mobileOverlay; + const dockBottom = timelineVisible + ? edgeInset + timelineSummaryHeight + gap + : edgeInset; + + return { + timelineBottom: edgeInset, + dockBottom, + mapControlBottom: dockBottom + dockHeight + gap + }; +} + export function clampAgentPanelWidth( width: number, viewportWidth: number, diff --git a/src/features/workbench/map-workbench-page.tsx b/src/features/workbench/map-workbench-page.tsx index 5e61e5d..81357fe 100644 --- a/src/features/workbench/map-workbench-page.tsx +++ b/src/features/workbench/map-workbench-page.tsx @@ -78,7 +78,8 @@ import { import { WORKBENCH_LAYOUT, getWorkbenchBasemapTone, - getWorkbenchLayoutCssVariables + getWorkbenchLayoutCssVariables, + resolveMobileWorkbenchOverlayLayout } from "./layout/workbench-layout"; import { getResponsiveWorkbenchPadding } from "./map/camera"; import { exportMapViewImage } from "./map/export-view"; @@ -220,9 +221,10 @@ export function MapWorkbenchPage({ timelineMode, timelineVisible ); + const mobileOverlayLayout = resolveMobileWorkbenchOverlayLayout(timelineVisible); const timelineBottom = isLargeScreen ? timelineScaleLayout.timelineBottom - : 12; + : mobileOverlayLayout.timelineBottom; useEffect(() => { setTimelineMode((current) => { @@ -987,19 +989,30 @@ export function MapWorkbenchPage({ onReturnNow={handleReturnOperationalNow} /> - + + : null )} mapContent={( @@ -1019,10 +1032,13 @@ export function MapWorkbenchPage({
-
+
+
-
+
{mobileSheet === null ? (