From cd07b2143f6d1ac8b57d86881794344f22d80583 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 21 Jul 2026 00:03:37 -0700 Subject: [PATCH] [eric] desktop: sidebar toggle pins the sidebar open in fullscreen; card anchors to the viewport so docked chrome squeezes it --- .../src/app/components/Layout/AppShell.tsx | 18 ++++++++++++++--- .../app/pages/Dashboard/cards/tileZones.ts | 20 +++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/components/Layout/AppShell.tsx b/frontend/src/app/components/Layout/AppShell.tsx index df6e8322..261ca811 100644 --- a/frontend/src/app/components/Layout/AppShell.tsx +++ b/frontend/src/app/components/Layout/AppShell.tsx @@ -444,7 +444,10 @@ const AppShell: React.FC = () => { // Zen compact mode: the sidebar is the only chrome now, so whenever it's "away" (user collapsed it, // OR a fullscreen card hides everything) a left-edge hover floats it back in as an overlay. const fsActive = !!fullscreenCardId && isDashboardViewActive; - const sidebarAway = (sidebarCollapsed || fsActive) && isDashboardViewActive; + // Arc: the sidebar toggle PINS the sidebar open inside fullscreen (docked, card shrinks beside it); + // unpinned fullscreen keeps the hover-peek overlay. + const [fsSidebarPinned, setFsSidebarPinned] = useState(false); + const sidebarAway = (sidebarCollapsed || (fsActive && !fsSidebarPinned)) && isDashboardViewActive; const [sidePeek, setSidePeek] = useState(false); useEffect(() => { if (!sidebarAway) setSidePeek(false); }, [sidebarAway]); // When the sidebar docks away, the canvas runs flush to the window's left edge, so the floating @@ -849,7 +852,7 @@ const AppShell: React.FC = () => { )} - {((!sidebarCollapsed && !fsHideChrome) || sideOverlay) && ( + {((!sidebarCollapsed && (!fsHideChrome || fsSidebarPinned)) || sideOverlay) && ( <> { if (sideOverlay) cancelPeekClose(); }} @@ -900,7 +903,16 @@ const AppShell: React.FC = () => { {/* Arc-style pin: from the floating peek this DOCKS the sidebar permanently (pushes the canvas), from docked it collapses back to peek. Toggle, not one-way collapse. */} - setSidebarCollapsed((v) => !v)} + { + if (fsActive) { + setFsSidebarPinned((v) => { + if (!v) setSidebarCollapsed(false); + return !v; + }); + return; + } + setSidebarCollapsed((v) => !v); + }} data-onboarding="sidebar-toggle" aria-expanded={!sidebarCollapsed} sx={{ color: c.text.tertiary, p: 0.5, borderRadius: 1, '&:hover': { color: c.text.secondary, bgcolor: `${c.text.tertiary}14` } }}> diff --git a/frontend/src/app/pages/Dashboard/cards/tileZones.ts b/frontend/src/app/pages/Dashboard/cards/tileZones.ts index 3d34b755..eeb19867 100644 --- a/frontend/src/app/pages/Dashboard/cards/tileZones.ts +++ b/frontend/src/app/pages/Dashboard/cards/tileZones.ts @@ -48,19 +48,17 @@ function workspaceSize(): { w: number; h: number } { } export function computeTiledStyle(zone: string, panX: number, panY: number, zoom: number): TiledStyle | null { - // 'fullscreen' = the Arc layout: the app chrome hides except the dock rail, which stays put on - // the left, and the card expands into the ENTIRE dashboard beside it, floating on the same gap - // the tile zones use. Target is WINDOW space here, so the viewport origin does NOT cancel. + // 'fullscreen' = the Arc layout: the dock rail stays put on the left and the card expands into + // the ENTIRE dashboard beside it, floating on the same gap the tile zones use. Anchored to the + // VIEWPORT (not the window) so docked chrome, like the pinned sidebar, squeezes the card instead + // of being covered by it. if (zone === 'fullscreen') { - const el = document.querySelector('[data-canvas-viewport]'); - const r = el ? el.getBoundingClientRect() : null; - const ox = r ? r.left : 0; - const oy = r ? r.top : 0; + const { w: vpW, h: vpH } = workspaceSize(); return { - left: (FULLSCREEN_DOCK_RAIL + GAP - ox - panX) / zoom, - top: (GAP - oy - panY) / zoom, - width: window.innerWidth - FULLSCREEN_DOCK_RAIL - GAP * 2, - height: window.innerHeight - GAP * 2, + left: (FULLSCREEN_DOCK_RAIL + GAP - panX) / zoom, + top: (GAP - panY) / zoom, + width: vpW - FULLSCREEN_DOCK_RAIL - GAP * 2, + height: vpH - GAP * 2, transform: `scale(${1 / zoom})`, transformOrigin: 'top left', };