diff --git a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx index bf957a27d4..9d372f0cf3 100644 --- a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx +++ b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx @@ -1,6 +1,8 @@ import { LoadingSpinner } from "@/components/ui/loading-spinner"; import React, { Fragment, + createContext, + useContext, useCallback, useEffect, useLayoutEffect, @@ -485,6 +487,13 @@ interface WorkspaceTabLabelMeasurement { width: number; } +const TabMeasurementCache = createContext | null>(null); + +export function WorkspaceTabMeasurementScope({ children }: { children: React.ReactNode }) { + const cache = useMemo(() => new Map(), []); + return {children}; +} + interface WorkspaceTabTrackSnapshot { signature: string; tabs: ResolvedWorkspaceDesktopTabRowItem[]; @@ -1059,9 +1068,13 @@ function ResolvedWorkspaceDesktopTabsRow({ const tabsScrollRef = useRef(null); const [exitFocusModeWidth, setExitFocusModeWidth] = useState(0); const tabScrollBoundary = useHorizontalScrollBoundary(); - const [labelMeasurements, setLabelMeasurements] = useState( - () => new Map(), - ); + const measurementCache = useContext(TabMeasurementCache); + const [labelMeasurements, setLabelMeasurements] = useState(() => new Map(measurementCache ?? [])); + useLayoutEffect(() => { + if (!measurementCache) return; + measurementCache.clear(); + for (const [key, value] of labelMeasurements) measurementCache.set(key, value); + }, [measurementCache, labelMeasurements]); const [trackSnapshot, setTrackSnapshot] = useState(null); const handleTabsContainerLayout = useCallback((event: LayoutChangeEvent) => { diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx index 2aa5a54bae..22839d103f 100644 --- a/packages/app/src/screens/workspace/workspace-screen.tsx +++ b/packages/app/src/screens/workspace/workspace-screen.tsx @@ -1,3 +1,4 @@ +import { shouldUseCompactWorkspaceHeader } from "./workspace-tab-layout"; import { planTimelineTailFetch } from "@/timeline/timeline-sync-plan"; import { useHostFeature } from "@/runtime/host-features"; import { @@ -147,6 +148,7 @@ import { } from "@/screens/workspace/use-workspace-tab-rename"; import { WorkspaceDesktopTabsRow, + WorkspaceTabMeasurementScope, type WorkspaceDesktopTabRowItem, } from "@/screens/workspace/workspace-desktop-tabs-row"; import { useDesktopBrowserNewTabRequests } from "@/desktop/browser/new-tab-requests"; @@ -786,6 +788,27 @@ function WorkspaceContentProviders({ ); } +function AdaptiveWorkspaceChrome({ + compactHeader, + children, +}: { + compactHeader: ReactNode; + children: ReactNode; +}) { + const [width, setWidth] = useState(null); + const measure = useCallback( + (event: LayoutChangeEvent) => setWidth(event.nativeEvent.layout.width), + [], + ); + return ( + + + {width !== null && shouldUseCompactWorkspaceHeader(width) ? compactHeader : children} + + + ); +} + function WorkspacePanelSurface({ launcher, children, @@ -3362,57 +3385,56 @@ function WorkspaceScreenContent({ ], ); const compactWorkspaceHeader = useMemo( - () => - isMobile ? ( - - - - - ) : null, + () => ( + + + + + ), [ activeTabKey, canOpenImportSheet, @@ -3440,7 +3462,6 @@ function WorkspaceScreenContent({ handleRenameTab, handleSelectSwitcherTab, handleToggleExplorerSidebar, - isMobile, isWorkspaceHeaderLoading, isWorkspaceHeaderSubtitleDistinct, normalizedServerId, @@ -3559,36 +3580,37 @@ function WorkspaceScreenContent({ ) : ( - {rendersDesktopSplitContent ? null : renderWorkspaceScreenHeader()} - - {shouldRenderDesktopPaneFallback ? ( - - - - ) : null} + + {rendersDesktopSplitContent ? null : renderWorkspaceScreenHeader()} + {shouldRenderDesktopPaneFallback ? ( + + + + ) : null} + {desktopContent} diff --git a/packages/app/src/screens/workspace/workspace-tab-layout.test.ts b/packages/app/src/screens/workspace/workspace-tab-layout.test.ts index 7c7b98cee0..1ba6e05f01 100644 --- a/packages/app/src/screens/workspace/workspace-tab-layout.test.ts +++ b/packages/app/src/screens/workspace/workspace-tab-layout.test.ts @@ -256,3 +256,13 @@ describe("resolveWorkspaceTabChipBackdrop", () => { ).toBe("surface0"); }); }); + +import { shouldUseCompactWorkspaceHeader } from "./workspace-tab-layout"; +it.each([ + [349, true], + [499, true], + [500, false], + [800, false], +])("workspace width %s selects compact=%s", (width, compact) => { + expect(shouldUseCompactWorkspaceHeader(width)).toBe(compact); +}); diff --git a/packages/app/src/screens/workspace/workspace-tab-layout.ts b/packages/app/src/screens/workspace/workspace-tab-layout.ts index cb89d9c54c..94aad3c1b6 100644 --- a/packages/app/src/screens/workspace/workspace-tab-layout.ts +++ b/packages/app/src/screens/workspace/workspace-tab-layout.ts @@ -1,3 +1,9 @@ +import { COMPACT_CONTENT_WIDTH } from "@/constants/layout"; + +export function shouldUseCompactWorkspaceHeader(width: number): boolean { + return width < COMPACT_CONTENT_WIDTH; +} + export type WorkspaceTabCloseButtonPolicy = "all" | "none"; export interface WorkspaceTabLayoutMetrics {