mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 07:33:18 +00:00
fix(app): choose workspace header by measured column width
Co-authored-by: Elsa Granger <zeyugao@outlook.com>
This commit is contained in:
co-authored by
Elsa Granger
parent
fced746a27
commit
3e6f91dc7b
@@ -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<Map<string, WorkspaceTabLabelMeasurement> | null>(null);
|
||||
|
||||
export function WorkspaceTabMeasurementScope({ children }: { children: React.ReactNode }) {
|
||||
const cache = useMemo(() => new Map<string, WorkspaceTabLabelMeasurement>(), []);
|
||||
return <TabMeasurementCache.Provider value={cache}>{children}</TabMeasurementCache.Provider>;
|
||||
}
|
||||
|
||||
interface WorkspaceTabTrackSnapshot {
|
||||
signature: string;
|
||||
tabs: ResolvedWorkspaceDesktopTabRowItem[];
|
||||
@@ -1059,9 +1068,13 @@ function ResolvedWorkspaceDesktopTabsRow({
|
||||
const tabsScrollRef = useRef<ScrollView>(null);
|
||||
const [exitFocusModeWidth, setExitFocusModeWidth] = useState<number>(0);
|
||||
const tabScrollBoundary = useHorizontalScrollBoundary();
|
||||
const [labelMeasurements, setLabelMeasurements] = useState(
|
||||
() => new Map<string, WorkspaceTabLabelMeasurement>(),
|
||||
);
|
||||
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<WorkspaceTabTrackSnapshot | null>(null);
|
||||
|
||||
const handleTabsContainerLayout = useCallback((event: LayoutChangeEvent) => {
|
||||
|
||||
@@ -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<number | null>(null);
|
||||
const measure = useCallback(
|
||||
(event: LayoutChangeEvent) => setWidth(event.nativeEvent.layout.width),
|
||||
[],
|
||||
);
|
||||
return (
|
||||
<WorkspaceTabMeasurementScope>
|
||||
<View onLayout={measure}>
|
||||
{width !== null && shouldUseCompactWorkspaceHeader(width) ? compactHeader : children}
|
||||
</View>
|
||||
</WorkspaceTabMeasurementScope>
|
||||
);
|
||||
}
|
||||
|
||||
function WorkspacePanelSurface({
|
||||
launcher,
|
||||
children,
|
||||
@@ -3362,57 +3385,56 @@ function WorkspaceScreenContent({
|
||||
],
|
||||
);
|
||||
const compactWorkspaceHeader = useMemo(
|
||||
() =>
|
||||
isMobile ? (
|
||||
<CompactWorkspaceHeader
|
||||
isLoading={isWorkspaceHeaderLoading}
|
||||
title={workspaceHeaderTitle}
|
||||
subtitle={workspaceHeaderSubtitle}
|
||||
isSubtitleDistinct={isWorkspaceHeaderSubtitleDistinct}
|
||||
serverId={normalizedServerId}
|
||||
onToggleExplorer={handleToggleExplorerSidebar}
|
||||
explorerToggleLabel={explorerSidebarToggleLabel}
|
||||
explorerToggleAccessibilityState={explorerSidebarToggleAccessibilityState}
|
||||
>
|
||||
<MobileWorkspaceTabSwitcher
|
||||
tabs={tabs}
|
||||
activeTabKey={activeTabKey}
|
||||
normalizedServerId={normalizedServerId}
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
onSelectSwitcherTab={handleSelectSwitcherTab}
|
||||
createTerminalDisabled={createTerminalDisabled}
|
||||
onCreateDraftTab={handleCreateDraftTab}
|
||||
onCreateTerminal={handleCreateTerminal}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyTerminalId={handleCopyTerminalId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTab={handleCloseTabById}
|
||||
onCloseTabsAbove={handleCloseTabsToLeft}
|
||||
onCloseTabsBelow={handleCloseTabsToRight}
|
||||
onCloseOtherTabs={handleCloseOtherTabs}
|
||||
/>
|
||||
<WorkspaceHeaderMenuMobile
|
||||
normalizedServerId={normalizedServerId}
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
scripts={compactHeaderScripts}
|
||||
currentBranchName={currentBranchName}
|
||||
showWorkspaceSetup={showWorkspaceSetup}
|
||||
showCreateBrowserTab={showCreateBrowserTab}
|
||||
createTerminalDisabled={createTerminalDisabled}
|
||||
importAgentDisabled={!canOpenImportSheet}
|
||||
copyPathDisabled={!workspaceDirectory}
|
||||
onCreateTerminalWithProfile={handleCreateTerminalWithProfile}
|
||||
onCreateBrowser={handleCreateBrowserTab}
|
||||
onOpenImportSheet={openImportSheet}
|
||||
onCopyWorkspacePath={handleCopyWorkspacePath}
|
||||
onCopyBranchName={handleCopyBranchName}
|
||||
onOpenSetupTab={handleOpenSetupTab}
|
||||
/>
|
||||
</CompactWorkspaceHeader>
|
||||
) : null,
|
||||
() => (
|
||||
<CompactWorkspaceHeader
|
||||
isLoading={isWorkspaceHeaderLoading}
|
||||
title={workspaceHeaderTitle}
|
||||
subtitle={workspaceHeaderSubtitle}
|
||||
isSubtitleDistinct={isWorkspaceHeaderSubtitleDistinct}
|
||||
serverId={normalizedServerId}
|
||||
onToggleExplorer={handleToggleExplorerSidebar}
|
||||
explorerToggleLabel={explorerSidebarToggleLabel}
|
||||
explorerToggleAccessibilityState={explorerSidebarToggleAccessibilityState}
|
||||
>
|
||||
<MobileWorkspaceTabSwitcher
|
||||
tabs={tabs}
|
||||
activeTabKey={activeTabKey}
|
||||
normalizedServerId={normalizedServerId}
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
onSelectSwitcherTab={handleSelectSwitcherTab}
|
||||
createTerminalDisabled={createTerminalDisabled}
|
||||
onCreateDraftTab={handleCreateDraftTab}
|
||||
onCreateTerminal={handleCreateTerminal}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyTerminalId={handleCopyTerminalId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTab={handleCloseTabById}
|
||||
onCloseTabsAbove={handleCloseTabsToLeft}
|
||||
onCloseTabsBelow={handleCloseTabsToRight}
|
||||
onCloseOtherTabs={handleCloseOtherTabs}
|
||||
/>
|
||||
<WorkspaceHeaderMenuMobile
|
||||
normalizedServerId={normalizedServerId}
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
scripts={compactHeaderScripts}
|
||||
currentBranchName={currentBranchName}
|
||||
showWorkspaceSetup={showWorkspaceSetup}
|
||||
showCreateBrowserTab={showCreateBrowserTab}
|
||||
createTerminalDisabled={createTerminalDisabled}
|
||||
importAgentDisabled={!canOpenImportSheet}
|
||||
copyPathDisabled={!workspaceDirectory}
|
||||
onCreateTerminalWithProfile={handleCreateTerminalWithProfile}
|
||||
onCreateBrowser={handleCreateBrowserTab}
|
||||
onOpenImportSheet={openImportSheet}
|
||||
onCopyWorkspacePath={handleCopyWorkspacePath}
|
||||
onCopyBranchName={handleCopyBranchName}
|
||||
onOpenSetupTab={handleOpenSetupTab}
|
||||
/>
|
||||
</CompactWorkspaceHeader>
|
||||
),
|
||||
[
|
||||
activeTabKey,
|
||||
canOpenImportSheet,
|
||||
@@ -3440,7 +3462,6 @@ function WorkspaceScreenContent({
|
||||
handleRenameTab,
|
||||
handleSelectSwitcherTab,
|
||||
handleToggleExplorerSidebar,
|
||||
isMobile,
|
||||
isWorkspaceHeaderLoading,
|
||||
isWorkspaceHeaderSubtitleDistinct,
|
||||
normalizedServerId,
|
||||
@@ -3559,36 +3580,37 @@ function WorkspaceScreenContent({
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.centerColumn}>
|
||||
{rendersDesktopSplitContent ? null : renderWorkspaceScreenHeader()}
|
||||
|
||||
{shouldRenderDesktopPaneFallback ? (
|
||||
<NewTabLauncherProvider value={newTabLauncher}>
|
||||
<WorkspaceDesktopTabsRow
|
||||
paneId={focusedPaneIdOrUndefined}
|
||||
isFocused={isRouteFocused}
|
||||
tabs={desktopTabRowItems}
|
||||
normalizedServerId={normalizedServerId}
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
setHoveredCloseTabKey={setHoveredCloseTabKey}
|
||||
onNavigateTab={navigateToTabId}
|
||||
onCloseTab={handleCloseTabById}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyTerminalId={handleCopyTerminalId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTabsToLeft={handleCloseTabsToLeft}
|
||||
onCloseTabsToRight={handleCloseTabsToRight}
|
||||
onCloseOtherTabs={handleCloseOtherTabs}
|
||||
onCreateNewTab={handleCreateNewTab}
|
||||
onReorderTabs={handleReorderTabsInFocusedPane}
|
||||
focusModeEnabled={desktopFocusModeEnabled}
|
||||
onExitFocusMode={toggleFocusMode}
|
||||
/>
|
||||
</NewTabLauncherProvider>
|
||||
) : null}
|
||||
<AdaptiveWorkspaceChrome compactHeader={compactWorkspaceHeader}>
|
||||
{rendersDesktopSplitContent ? null : renderWorkspaceScreenHeader()}
|
||||
|
||||
{shouldRenderDesktopPaneFallback ? (
|
||||
<NewTabLauncherProvider value={newTabLauncher}>
|
||||
<WorkspaceDesktopTabsRow
|
||||
paneId={focusedPaneIdOrUndefined}
|
||||
isFocused={isRouteFocused}
|
||||
tabs={desktopTabRowItems}
|
||||
normalizedServerId={normalizedServerId}
|
||||
normalizedWorkspaceId={normalizedWorkspaceId}
|
||||
setHoveredCloseTabKey={setHoveredCloseTabKey}
|
||||
onNavigateTab={navigateToTabId}
|
||||
onCloseTab={handleCloseTabById}
|
||||
onCopyResumeCommand={handleCopyResumeCommand}
|
||||
onCopyAgentId={handleCopyAgentId}
|
||||
onCopyTerminalId={handleCopyTerminalId}
|
||||
onCopyFilePath={handleCopyFilePath}
|
||||
onReloadAgent={handleReloadAgent}
|
||||
onRenameTab={handleRenameTab}
|
||||
onCloseTabsToLeft={handleCloseTabsToLeft}
|
||||
onCloseTabsToRight={handleCloseTabsToRight}
|
||||
onCloseOtherTabs={handleCloseOtherTabs}
|
||||
onCreateNewTab={handleCreateNewTab}
|
||||
onReorderTabs={handleReorderTabsInFocusedPane}
|
||||
focusModeEnabled={desktopFocusModeEnabled}
|
||||
onExitFocusMode={toggleFocusMode}
|
||||
/>
|
||||
</NewTabLauncherProvider>
|
||||
) : null}
|
||||
</AdaptiveWorkspaceChrome>
|
||||
<View style={styles.centerContent}>
|
||||
<View style={styles.content}>{desktopContent}</View>
|
||||
</View>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user