fix(app): choose workspace chrome within each desktop pane

The outer width wrapper added a compact header while the desktop split container kept its full header and pane rails. Each pane now owns the complete choice, replacing its header and rail together.

Each split pane uses its own measured width: below 500 points it shows its own tab dropdown, even if the neighboring pane is wide. Pane width determines usable header space. Keep new-tab keyboard ownership outside the switched chrome and retain positive measurements while hidden.

Co-authored-by: Elsa Granger <zeyugao@outlook.com>
This commit is contained in:
Mohamed Boudra
2026-11-04 07:15:43 +01:00
co-authored by Elsa Granger
parent bfe11d31a6
commit d48f1cc64e
6 changed files with 303 additions and 171 deletions
+86 -39
View File
@@ -1,3 +1,7 @@
import {
AdaptiveWorkspaceChrome,
type WorkspaceCompactHeaderActions,
} from "@/screens/workspace/adaptive-workspace-chrome";
import { useWorkspaceTabDrag } from "./workspace-tab-drag-context";
import {
Fragment,
@@ -42,6 +46,7 @@ import type { WorkspacePaneContentModel } from "@/screens/workspace/workspace-pa
import { WorkspacePanelHost } from "@/screens/workspace/workspace-panel-host";
import {
WorkspaceDesktopTabsRow,
useWorkspaceNewTabKeyboardAction,
type WorkspaceDesktopTabRowItem,
} from "@/screens/workspace/workspace-desktop-tabs-row";
import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types";
@@ -61,6 +66,11 @@ import { isNative } from "@/constants/platform";
interface SplitContainerProps {
layout: WorkspaceLayout;
renderMainHeader?: () => ReactNode;
renderCompactHeader: (
tabs: WorkspaceTabDescriptor[],
activeTabKey: string,
actions: WorkspaceCompactHeaderActions,
) => ReactNode;
workspaceKey: string;
normalizedServerId: string;
normalizedWorkspaceId: string;
@@ -145,7 +155,6 @@ const EMPTY_SPLIT_SIZES: number[] = [];
export function SplitContainer({
layout,
renderMainHeader,
workspaceKey,
normalizedServerId,
normalizedWorkspaceId,
@@ -174,6 +183,8 @@ export function SplitContainer({
onReorderTabsInPane,
focusModeEnabled,
onExitFocusMode,
renderMainHeader,
renderCompactHeader,
}: SplitContainerProps) {
const inheritedWindowChromeCorners = useWindowChromeCorners();
const windowChromeCorners = focusModeEnabled ? inheritedWindowChromeCorners : "none";
@@ -237,7 +248,6 @@ export function SplitContainer({
<View style={styles.workspaceShell}>
<WindowChromeRegion corners={mainColumnWindowChromeCorners}>
<View style={mainColumnStyle}>
{renderMainHeader?.()}
{splitRoot.usesFallbackStrip && <WindowChromeSafeArea placement="below" />}
{renderRoot ? (
<SplitNodeView
@@ -279,6 +289,8 @@ export function SplitContainer({
onTogglePaneMaximized={handleTogglePaneMaximized}
focusModeEnabled={focusModeEnabled}
onExitFocusMode={onExitFocusMode}
renderMainHeader={renderMainHeader}
renderCompactHeader={renderCompactHeader}
/>
) : null}
</View>
@@ -410,6 +422,8 @@ function SplitNodeView({
onTogglePaneMaximized,
focusModeEnabled,
onExitFocusMode,
renderMainHeader,
renderCompactHeader,
}: SplitNodeViewProps) {
const [groupContainerSize, setGroupContainerSize] = useState(0);
const groupId = node.kind === "group" ? node.group.id : null;
@@ -496,6 +510,8 @@ function SplitNodeView({
onTogglePaneMaximized={onTogglePaneMaximized}
focusModeEnabled={focusModeEnabled}
onExitFocusMode={onExitFocusMode}
renderMainHeader={renderMainHeader}
renderCompactHeader={renderCompactHeader}
/>
</WindowChromeRegion>
</RetainedPanel>
@@ -550,6 +566,8 @@ function SplitNodeView({
onTogglePaneMaximized={onTogglePaneMaximized}
focusModeEnabled={focusModeEnabled}
onExitFocusMode={onExitFocusMode}
renderMainHeader={renderMainHeader}
renderCompactHeader={renderCompactHeader}
/>
</SplitGroupChild>
{index < node.group.children.length - 1 &&
@@ -608,6 +626,8 @@ const SplitPaneView = memo(function SplitPaneView({
onTogglePaneMaximized,
focusModeEnabled,
onExitFocusMode,
renderMainHeader,
renderCompactHeader,
}: SplitPaneViewProps) {
recordRenderProfileReasons(`SplitPaneView:${pane.id}`, ["render"]);
const paneRef = useRef<View | null>(null);
@@ -701,6 +721,23 @@ const SplitPaneView = memo(function SplitPaneView({
() => onTogglePaneMaximized(paneId),
[onTogglePaneMaximized, paneId],
);
const compactHeaderActions = useMemo(
() => ({
closeAbove: handleCloseTabsToLeft,
closeBelow: handleCloseTabsToRight,
closeOthers: handleCloseOtherTabs,
}),
[handleCloseTabsToLeft, handleCloseTabsToRight, handleCloseOtherTabs],
);
const createNewTab = useCallback(() => onCreateNewTab({ paneId }), [onCreateNewTab, paneId]);
useWorkspaceNewTabKeyboardAction({
handlerName: "workspace-chrome-new-tab",
ownsKeyboardShortcuts: isFocused && isWorkspaceFocused,
normalizedServerId,
normalizedWorkspaceId,
paneId,
createNewTab,
});
return (
<RenderProfile id={`SplitPaneView:${pane.id}`}>
<View
@@ -709,43 +746,53 @@ const SplitPaneView = memo(function SplitPaneView({
style={styles.pane}
testID={`workspace-pane-${pane.id}`}
>
<WindowChromeSafeArea placement="inline" style={styles.paneTabs}>
<TitlebarDragRegion />
<WorkspaceDesktopTabsRow
paneId={pane.id}
isFocused={isFocused && isWorkspaceFocused}
tabs={desktopTabRowItems}
normalizedServerId={normalizedServerId}
normalizedWorkspaceId={normalizedWorkspaceId}
setHoveredCloseTabKey={setHoveredCloseTabKey}
onNavigateTab={onNavigateTab}
onCloseTab={onCloseTab}
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onCopyTerminalId={onCopyTerminalId}
onCopyFilePath={onCopyFilePath}
onReloadAgent={onReloadAgent}
onRenameTab={onRenameTab}
onCloseTabsToLeft={handleCloseTabsToLeft}
onCloseTabsToRight={handleCloseTabsToRight}
onCloseOtherTabs={handleCloseOtherTabs}
onCreateNewTab={onCreateNewTab}
onReorderTabs={handleReorderTabs}
externalDndContext
activeDragTabId={activeDragTabId}
tabDropPreviewIndex={
tabDropPreview?.paneId === pane.id ? tabDropPreview.indicatorIndex : null
}
showPaneSplitActions={!focusModeEnabled}
showPaneMaximizeAction={workspaceHasMultiplePanes && !focusModeEnabled}
paneMaximized={paneId === maximizedPaneId}
onTogglePaneMaximized={handleTogglePaneMaximized}
onSplitRight={handleSplitRight}
onSplitDown={handleSplitDown}
focusModeEnabled={Boolean(focusModeEnabled)}
onExitFocusMode={onExitFocusMode}
/>
</WindowChromeSafeArea>
<AdaptiveWorkspaceChrome
compactHeader={renderCompactHeader(
paneTabs,
activeTabDescriptor?.key ?? "",
compactHeaderActions,
)}
>
{renderMainHeader?.()}
<WindowChromeSafeArea placement="inline" style={styles.paneTabs}>
<TitlebarDragRegion />
<WorkspaceDesktopTabsRow
paneId={pane.id}
isFocused={isFocused && isWorkspaceFocused}
tabs={desktopTabRowItems}
normalizedServerId={normalizedServerId}
normalizedWorkspaceId={normalizedWorkspaceId}
setHoveredCloseTabKey={setHoveredCloseTabKey}
onNavigateTab={onNavigateTab}
onCloseTab={onCloseTab}
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onCopyTerminalId={onCopyTerminalId}
onCopyFilePath={onCopyFilePath}
onReloadAgent={onReloadAgent}
onRenameTab={onRenameTab}
onCloseTabsToLeft={handleCloseTabsToLeft}
onCloseTabsToRight={handleCloseTabsToRight}
onCloseOtherTabs={handleCloseOtherTabs}
onCreateNewTab={onCreateNewTab}
onReorderTabs={handleReorderTabs}
ownsKeyboardShortcuts={false}
externalDndContext
activeDragTabId={activeDragTabId}
tabDropPreviewIndex={
tabDropPreview?.paneId === pane.id ? tabDropPreview.indicatorIndex : null
}
showPaneSplitActions={!focusModeEnabled}
showPaneMaximizeAction={workspaceHasMultiplePanes && !focusModeEnabled}
paneMaximized={paneId === maximizedPaneId}
onTogglePaneMaximized={handleTogglePaneMaximized}
onSplitRight={handleSplitRight}
onSplitDown={handleSplitDown}
focusModeEnabled={Boolean(focusModeEnabled)}
onExitFocusMode={onExitFocusMode}
/>
</WindowChromeSafeArea>
</AdaptiveWorkspaceChrome>
<View style={styles.paneContent}>
<WorkspacePanelHost
@@ -0,0 +1,31 @@
import { useCallback, useState, type ReactNode } from "react";
import { View, type LayoutChangeEvent } from "react-native";
import { selectWorkspaceChrome } from "./workspace-tab-layout";
import { WorkspaceTabMeasurementScope } from "./workspace-desktop-tabs-row";
export function AdaptiveWorkspaceChrome({
compactHeader,
children,
}: {
compactHeader: ReactNode;
children: ReactNode;
}) {
const [width, setWidth] = useState<number | null>(null);
const measure = useCallback((event: LayoutChangeEvent) => {
const measured = event.nativeEvent.layout.width;
if (measured > 0) setWidth(measured);
}, []);
return (
<WorkspaceTabMeasurementScope>
<View onLayout={measure}>
{selectWorkspaceChrome({ width, compact: compactHeader, full: children })}
</View>
</WorkspaceTabMeasurementScope>
);
}
export interface WorkspaceCompactHeaderActions {
closeAbove: (key: string) => Promise<void> | void;
closeBelow: (key: string) => Promise<void> | void;
closeOthers: (key: string) => Promise<void> | void;
}
@@ -967,6 +967,47 @@ function TabChip({
);
}
export function useWorkspaceNewTabKeyboardAction({
handlerName = "workspace-new-tab",
ownsKeyboardShortcuts,
normalizedServerId,
normalizedWorkspaceId,
paneId,
createNewTab,
}: {
handlerName?: string;
ownsKeyboardShortcuts: boolean;
normalizedServerId: string;
normalizedWorkspaceId: string;
paneId?: string;
createNewTab: () => void;
}) {
const handleNewTabKeyboardAction = useCallback(
(action: KeyboardActionDefinition): boolean => {
if (!ownsKeyboardShortcuts) return false;
if (action.id === "workspace.tab.menu.open") {
createNewTab();
return true;
}
return false;
},
[createNewTab, ownsKeyboardShortcuts],
);
useKeyboardActionHandler({
handlerId: buildWorkspaceKeyboardHandlerId({
name: handlerName,
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
paneId,
}),
actions: ["workspace.tab.menu.open"],
enabled: ownsKeyboardShortcuts,
priority: 200,
handle: handleNewTabKeyboardAction,
});
}
export function WorkspaceDesktopTabsRow(props: WorkspaceDesktopTabsRowProps) {
const [presentations, setPresentations] = useState(
() => new Map<string, WorkspaceTabPresentation>(),
@@ -1259,29 +1300,12 @@ function ResolvedWorkspaceDesktopTabsRow({
const createNewTab = useCallback(() => onCreateNewTab({ paneId }), [onCreateNewTab, paneId]);
const handleNewTabKeyboardAction = useCallback(
(action: KeyboardActionDefinition): boolean => {
if (!ownsKeyboardShortcuts) return false;
if (action.id === "workspace.tab.menu.open") {
createNewTab();
return true;
}
return false;
},
[createNewTab, ownsKeyboardShortcuts],
);
useKeyboardActionHandler({
handlerId: buildWorkspaceKeyboardHandlerId({
name: "workspace-new-tab",
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
paneId,
}),
actions: ["workspace.tab.menu.open"],
enabled: ownsKeyboardShortcuts,
priority: 200,
handle: handleNewTabKeyboardAction,
useWorkspaceNewTabKeyboardAction({
ownsKeyboardShortcuts,
normalizedServerId,
normalizedWorkspaceId,
paneId,
createNewTab,
});
const rail: WorkspaceTabRailSurface = host === "explorer" ? "sidebar" : "surface";
@@ -1,4 +1,7 @@
import { shouldUseCompactWorkspaceHeader } from "./workspace-tab-layout";
import {
AdaptiveWorkspaceChrome,
type WorkspaceCompactHeaderActions,
} from "./adaptive-workspace-chrome";
import { planTimelineTailFetch } from "@/timeline/timeline-sync-plan";
import { useHostFeature } from "@/runtime/host-features";
import {
@@ -148,7 +151,7 @@ import {
} from "@/screens/workspace/use-workspace-tab-rename";
import {
WorkspaceDesktopTabsRow,
WorkspaceTabMeasurementScope,
useWorkspaceNewTabKeyboardAction,
type WorkspaceDesktopTabRowItem,
} from "@/screens/workspace/workspace-desktop-tabs-row";
import { useDesktopBrowserNewTabRequests } from "@/desktop/browser/new-tab-requests";
@@ -788,27 +791,6 @@ 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,
@@ -3384,69 +3366,71 @@ function WorkspaceScreenContent({
handleOpenUrlInBrowserTab,
],
);
const compactWorkspaceHeader = useMemo(
() => (
<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>
),
const renderCompactWorkspaceHeader = useCallback(
(
headerTabs: WorkspaceTabDescriptor[],
headerActiveTabKey: string,
actions: WorkspaceCompactHeaderActions,
) => {
return (
<CompactWorkspaceHeader
isLoading={isWorkspaceHeaderLoading}
title={workspaceHeaderTitle}
subtitle={workspaceHeaderSubtitle}
isSubtitleDistinct={isWorkspaceHeaderSubtitleDistinct}
serverId={normalizedServerId}
onToggleExplorer={handleToggleExplorerSidebar}
explorerToggleLabel={explorerSidebarToggleLabel}
explorerToggleAccessibilityState={explorerSidebarToggleAccessibilityState}
>
<MobileWorkspaceTabSwitcher
tabs={headerTabs}
activeTabKey={headerActiveTabKey}
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={actions.closeAbove}
onCloseTabsBelow={actions.closeBelow}
onCloseOtherTabs={actions.closeOthers}
/>
<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,
compactHeaderScripts,
createTerminalDisabled,
currentBranchName,
explorerSidebarToggleAccessibilityState,
explorerSidebarToggleLabel,
handleCloseOtherTabs,
handleCloseTabById,
handleCloseTabsToLeft,
handleCloseTabsToRight,
handleCopyAgentId,
handleCopyBranchName,
handleCopyFilePath,
@@ -3469,12 +3453,16 @@ function WorkspaceScreenContent({
openImportSheet,
showCreateBrowserTab,
showWorkspaceSetup,
tabs,
workspaceDirectory,
workspaceHeaderSubtitle,
workspaceHeaderTitle,
],
);
const compactWorkspaceHeader = renderCompactWorkspaceHeader(tabs, activeTabKey, {
closeAbove: handleCloseTabsToLeft,
closeBelow: handleCloseTabsToRight,
closeOthers: handleCloseOtherTabs,
});
const desktopSplitContent = useMemo(() => {
if (!canRenderDesktopPaneSplits || !workspaceLayout || !persistenceKey) {
return null;
@@ -3483,6 +3471,7 @@ function WorkspaceScreenContent({
<SplitContainer
layout={workspaceLayout}
renderMainHeader={renderWorkspaceScreenHeader}
renderCompactHeader={renderCompactWorkspaceHeader}
focusModeEnabled={desktopFocusModeEnabled}
onExitFocusMode={toggleFocusMode}
workspaceKey={persistenceKey}
@@ -3519,6 +3508,7 @@ function WorkspaceScreenContent({
canRenderDesktopPaneSplits,
workspaceLayout,
renderWorkspaceScreenHeader,
renderCompactWorkspaceHeader,
persistenceKey,
desktopFocusModeEnabled,
toggleFocusMode,
@@ -3557,6 +3547,22 @@ function WorkspaceScreenContent({
[uiTabs, sheets],
);
const workspacePanelKey = `${normalizedServerId}:${normalizedWorkspaceId}`;
const createFallbackTab = useCallback(
() => handleCreateNewTab({ paneId: focusedPaneIdOrUndefined }),
[handleCreateNewTab, focusedPaneIdOrUndefined],
);
const ownsFallbackKeyboardShortcuts = useMemo(
() => shouldRenderDesktopPaneFallback && isRouteFocused,
[shouldRenderDesktopPaneFallback, isRouteFocused],
);
useWorkspaceNewTabKeyboardAction({
handlerName: "workspace-chrome-new-tab",
ownsKeyboardShortcuts: ownsFallbackKeyboardShortcuts,
normalizedServerId,
normalizedWorkspaceId,
paneId: focusedPaneIdOrUndefined,
createNewTab: createFallbackTab,
});
const compactHeaderLayout = resolveCompactHeaderLayout({
header: compactWorkspaceHeader,
activeTab: activeTabDescriptor,
@@ -3580,37 +3586,40 @@ function WorkspaceScreenContent({
</View>
) : (
<View style={styles.centerColumn}>
<AdaptiveWorkspaceChrome compactHeader={compactWorkspaceHeader}>
{rendersDesktopSplitContent ? null : renderWorkspaceScreenHeader()}
{rendersDesktopSplitContent ? null : (
<AdaptiveWorkspaceChrome compactHeader={compactWorkspaceHeader}>
{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>
{shouldRenderDesktopPaneFallback ? (
<NewTabLauncherProvider value={newTabLauncher}>
<WorkspaceDesktopTabsRow
paneId={focusedPaneIdOrUndefined}
ownsKeyboardShortcuts={false}
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>
@@ -7,6 +7,7 @@ import { SidebarCloseButton } from "@/components/headers/sidebar-close-button";
import { resolveWorkspaceTabChipBackdrop } from "@/screens/workspace/workspace-tab-chip-backdrop";
import { darkPureBlackTheme, darkTheme, lightTheme } from "@/styles/theme";
import {
selectWorkspaceChrome,
computeWorkspaceTabLayout,
retainWorkspaceTabMeasuredWidth,
} from "@/screens/workspace/workspace-tab-layout";
@@ -266,3 +267,14 @@ it.each([
])("workspace width %s selects compact=%s", (width, compact) => {
expect(shouldUseCompactWorkspaceHeader(width)).toBe(compact);
});
describe("desktop pane chrome", () => {
it("replaces the pane header and rail together at the pane width boundary", () => {
const full = ["workspace header", "pane tab rail"];
expect(selectWorkspaceChrome({ width: 440, compact: "pane dropdown", full })).toBe(
"pane dropdown",
);
expect(selectWorkspaceChrome({ width: 500, compact: "pane dropdown", full })).toBe(full);
expect(selectWorkspaceChrome({ width: null, compact: "pane dropdown", full })).toBe(full);
});
});
@@ -126,3 +126,12 @@ export function computeWorkspaceTabLayout(
requiresHorizontalScrollFallback,
};
}
export function selectWorkspaceChrome<Compact, Full>(input: {
width: number | null;
compact: Compact;
full: Full;
}): Compact | Full {
const compact = input.width !== null && shouldUseCompactWorkspaceHeader(input.width);
return compact ? input.compact : input.full;
}