From 53ba0f0a2604a75066443915d82ae7952f31924a Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 17 Aug 2026 21:04:51 +0200 Subject: [PATCH] fix(app): align workspace chrome controls --- packages/app/src/app/_layout.tsx | 11 +- .../app/src/components/branch-switcher.tsx | 40 +++-- packages/app/src/constants/layout.ts | 2 +- packages/app/src/git/actions-split-button.tsx | 11 +- .../workspace-open-in-editor-button.tsx | 6 +- .../screens/workspace/workspace-screen.tsx | 161 ++++-------------- .../workspace/workspace-scripts-button.tsx | 5 +- 7 files changed, 72 insertions(+), 164 deletions(-) diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx index 67fa8f598..19fd2e547 100644 --- a/packages/app/src/app/_layout.tsx +++ b/packages/app/src/app/_layout.tsx @@ -46,7 +46,6 @@ import { FloatingPanelPortalHost } from "@/components/ui/floating-panel-portal"; import { HostChooserModal, useHostChooser } from "@/hosts/host-chooser"; import { getIsElectronRuntime, - getIsElectronRuntimeMac, HEADER_INNER_HEIGHT, useIsCompactFormFactor, } from "@/constants/layout"; @@ -713,23 +712,17 @@ function DesktopWindowControlsSync({ enabled }: { enabled: boolean }) { const { theme } = useUnistyles(); const surface0 = theme.colors.surface0; const foreground = theme.colors.foreground; - const pathname = usePathname(); - const isFocusModeEnabled = usePanelStore((state) => state.desktop.focusModeEnabled); - const liftTrafficLights = - getIsElectronRuntimeMac() && - isFocusModeEnabled && - parseHostWorkspaceRouteFromPathname(pathname) !== null; useEffect(() => { if (!enabled || isNative) return; void updateDesktopWindowControls({ backgroundColor: surface0, foregroundColor: foreground, - trafficLightOffsetY: liftTrafficLights ? -5 : 0.5, + trafficLightOffsetY: -5, }).catch((error) => { console.warn("[DesktopWindow] Failed to update window controls overlay", error); }); - }, [enabled, surface0, foreground, liftTrafficLights]); + }, [enabled, surface0, foreground]); return null; } diff --git a/packages/app/src/components/branch-switcher.tsx b/packages/app/src/components/branch-switcher.tsx index 39eb7dadb..131e98519 100644 --- a/packages/app/src/components/branch-switcher.tsx +++ b/packages/app/src/components/branch-switcher.tsx @@ -6,6 +6,7 @@ import { StyleSheet, withUnistyles } from "react-native-unistyles"; import { useTranslation } from "react-i18next"; import type { Theme } from "@/styles/theme"; import { Combobox, ComboboxItem, type ComboboxProps } from "@/components/ui/combobox"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime"; import { useToast } from "@/contexts/toast-context"; import { useBranchSwitcher } from "@/hooks/use-branch-switcher"; @@ -87,19 +88,28 @@ export function BranchSwitcher({ return ( - - - - {currentBranchName} - - - + + + + + + {currentBranchName} + + + + + + {t("branchSwitcher.triggerTooltip")} + + ({ fontWeight: theme.fontWeight.normal, flexShrink: 1, }, + tooltipText: { + color: theme.colors.popoverForeground, + fontSize: theme.fontSize.xs, + }, })); diff --git a/packages/app/src/constants/layout.ts b/packages/app/src/constants/layout.ts index 676f1bb86..0a5b4be09 100644 --- a/packages/app/src/constants/layout.ts +++ b/packages/app/src/constants/layout.ts @@ -6,7 +6,7 @@ export const FOOTER_HEIGHT = 75; // Shared header inner height (excluding safe area insets and border) // Used by both agent header (ScreenHeader) and explorer sidebar header // This ensures both headers have the same visual height -export const HEADER_INNER_HEIGHT = 48; +export const HEADER_INNER_HEIGHT = 36; export const HEADER_INNER_HEIGHT_MOBILE = 56; export const WORKSPACE_SECONDARY_HEADER_HEIGHT = 36; export const HEADER_TOP_PADDING_MOBILE = 8; diff --git a/packages/app/src/git/actions-split-button.tsx b/packages/app/src/git/actions-split-button.tsx index 39364f494..cd8537a29 100644 --- a/packages/app/src/git/actions-split-button.tsx +++ b/packages/app/src/git/actions-split-button.tsx @@ -17,6 +17,7 @@ import { inlineUnistylesStyle } from "@/styles/unistyles-inline-style"; import type { ShortcutKey } from "@/utils/format-shortcut"; import type { GitAction, GitActions } from "@/git/policy"; import { useGitActionRunner } from "@/git/use-actions"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; interface GitActionsSplitButtonProps { gitActions: GitActions; @@ -210,16 +211,16 @@ const styles = StyleSheet.create((theme) => ({ flexShrink: 0, }, splitButton: { + height: buttonControlHeight.xs, flexDirection: "row", alignItems: "stretch", - borderRadius: theme.borderRadius.md, + borderRadius: theme.borderRadius.lg, borderWidth: theme.borderWidth[1], borderColor: theme.colors.borderAccent, overflow: "hidden", }, splitButtonPrimary: { paddingHorizontal: theme.spacing[3], - paddingVertical: theme.spacing[1], justifyContent: "center", position: "relative", }, @@ -249,11 +250,11 @@ const styles = StyleSheet.create((theme) => ({ borderLeftColor: theme.colors.borderAccent, }, iconButton: { - width: 32, - height: 32, + width: buttonControlHeight.xs, + height: buttonControlHeight.xs, alignItems: "center", justifyContent: "center", - borderRadius: theme.borderRadius.md, + borderRadius: theme.borderRadius.lg, }, overflowMenuButton: { marginRight: -theme.spacing[2], diff --git a/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx b/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx index 15e9ef09f..75e5e0893 100644 --- a/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx +++ b/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx @@ -27,6 +27,7 @@ import { planWorkspaceOpenTargets } from "@/workspace/open-target-planner"; import type { Theme } from "@/styles/theme"; import { ForgeBrandIcon } from "@/git/forge-icon"; import { getForgePresentation } from "@/git/forge"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; interface WorkspaceOpenInEditorButtonProps { serverId: string; @@ -284,9 +285,10 @@ const styles = StyleSheet.create((theme) => ({ flexShrink: 0, }, splitButton: { + height: buttonControlHeight.xs, flexDirection: "row", alignItems: "stretch", - borderRadius: theme.borderRadius.md, + borderRadius: theme.borderRadius.lg, borderWidth: theme.borderWidth[1], borderColor: theme.colors.borderAccent, overflow: "hidden", @@ -294,14 +296,12 @@ const styles = StyleSheet.create((theme) => ({ splitButtonPrimary: { paddingLeft: theme.spacing[3], paddingRight: theme.spacing[3], - paddingVertical: theme.spacing[1], justifyContent: "center", position: "relative", }, splitButtonPrimaryIconOnly: { paddingLeft: theme.spacing[2], paddingRight: theme.spacing[2], - paddingVertical: theme.spacing[1], justifyContent: "center", position: "relative", }, diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx index 5c1892a7e..520c683e7 100644 --- a/packages/app/src/screens/workspace/workspace-screen.tsx +++ b/packages/app/src/screens/workspace/workspace-screen.tsx @@ -19,7 +19,6 @@ import { useQueryClient } from "@tanstack/react-query"; import { useRouter, type Href } from "expo-router"; import * as Clipboard from "expo-clipboard"; import { useTranslation } from "react-i18next"; -import { DiffStat } from "@/components/diff-stat"; import { ChevronDown, Copy, @@ -37,12 +36,12 @@ import type { Theme } from "@/styles/theme"; import invariant from "tiny-invariant"; import { SidebarMenuToggle } from "@/components/headers/menu-header"; import { HeaderToggleButton } from "@/components/headers/header-toggle-button"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; import { ScreenHeader } from "@/components/headers/screen-header"; import { ScreenTitle } from "@/components/headers/screen-title"; import { HostBadge } from "@/hosts/host-badge"; import { useHostBadges } from "@/hosts/use-host-badges"; import { Combobox, type ComboboxOption } from "@/components/ui/combobox"; -import { Shortcut } from "@/components/ui/shortcut"; import type { ShortcutKey } from "@/utils/format-shortcut"; import { DropdownMenu, @@ -52,7 +51,6 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { FloatingPanelPortalHost, FloatingPanelPortalHostNameProvider, @@ -61,7 +59,6 @@ import { ExplorerSidebar } from "@/components/explorer-sidebar"; import { SplitContainer } from "@/components/split-container"; import { RetainedPanel } from "@/components/retained-panel"; import { WindowChromeRegion } from "@/utils/desktop-window"; -import { SourceControlPanelIcon } from "@/components/icons/source-control-panel-icon"; import { WorkspaceActions } from "@/git/workspace-actions"; import { WorkspaceOpenInEditorButton } from "@/screens/workspace/workspace-open-in-editor-button"; import { WorkspaceScriptsButton } from "@/screens/workspace/workspace-scripts-button"; @@ -175,7 +172,7 @@ import { useModifiedPanelTabIds, } from "@/panels/panel-instance-attributes"; import { findAdjacentPane } from "@/utils/split-navigation"; -import { useIsCompactFormFactor, supportsDesktopPaneSplits } from "@/constants/layout"; +import { supportsDesktopPaneSplits, useIsCompactFormFactor } from "@/constants/layout"; import { getIsElectron, isNative, isWeb } from "@/constants/platform"; import type { SurfaceBackdrop } from "@/styles/surface-backdrop"; import { @@ -252,7 +249,6 @@ const ThemedGlobe = withUnistyles(Globe); const ThemedImport = withUnistyles(ImportIcon); const ThemedSettings = withUnistyles(Settings); const ThemedPanelRight = withUnistyles(PanelRight); -const ThemedSourceControlPanelIcon = withUnistyles(SourceControlPanelIcon); interface DynamicProviderIconProps { iconKey: string; @@ -273,8 +269,6 @@ const extraMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundExtraMuted, }); -const sourceControlPanelStrokeWidth15 = { strokeWidth: 1.5 }; - const MENU_NEW_AGENT_ICON = ; const MENU_NEW_TERMINAL_ICON = ; const MENU_NEW_BROWSER_ICON = ; @@ -2022,15 +2016,6 @@ function WorkspaceScreenContent({ isDesktopExplorerOpen, ); - const hasDiffStat = useMemo(() => Boolean(workspaceDescriptor?.diffStat), [workspaceDescriptor]); - const explorerToggleStyle = useCallback( - ({ hovered, pressed }: { hovered?: boolean; pressed?: boolean }) => [ - styles.sourceControlButton, - hasDiffStat && styles.sourceControlButtonWithStats, - (Boolean(hovered) || Boolean(pressed) || isExplorerOpen) && styles.sourceControlButtonHovered, - ], - [hasDiffStat, isExplorerOpen], - ); const explorerToggleAccessibilityState = useMemo( () => ({ expanded: isExplorerOpen }), [isExplorerOpen], @@ -3612,71 +3597,25 @@ function WorkspaceScreenContent({ {!isMobile && workspaceDirectory ? ( <> - {isGitCheckout ? ( - - - - {({ hovered, pressed }) => { - const active = hovered || pressed; - const colorMapping = active ? foregroundColorMapping : extraMutedColorMapping; - return ( - <> - - {workspaceDescriptor?.diffStat ? ( - - ) : null} - - ); - }} - - - - - - {t("workspace.tabs.explorer.toggle")} - - - - - - ) : null} + + {({ hovered }) => { + const colorMapping = hovered ? foregroundColorMapping : extraMutedColorMapping; + return ; + }} + ) : null} - {!isMobile && !isGitCheckout ? ( - - {({ hovered, pressed }) => { - const colorMapping = - hovered || pressed ? foregroundColorMapping : extraMutedColorMapping; - return ; - }} - - ) : null} {isMobile ? ( {({ hovered }) => { - const colorMapping = - isExplorerOpen || hovered ? foregroundColorMapping : mutedColorMapping; - return isGitCheckout ? ( - - ) : ( - - ); + const colorMapping = hovered ? foregroundColorMapping : mutedColorMapping; + // Matches the ghost scripts Play icon in the same header (16 / 1.5). + return ; }} ) : null} @@ -3718,12 +3649,9 @@ function WorkspaceScreenContent({ handleScriptTerminalStarted, handleViewScriptTerminal, handleOpenUrlInBrowserTab, - isGitCheckout, handleToggleExplorer, - isExplorerOpen, explorerToggleLabel, explorerToggleAccessibilityState, - explorerToggleStyle, t, ], ); @@ -4087,18 +4015,19 @@ const styles = StyleSheet.create((theme) => ({ borderRadius: theme.borderRadius.lg, }, compactHeaderActionButton: { - width: theme.spacing[8], - height: theme.spacing[8], + width: { + xs: theme.spacing[8], + md: buttonControlHeight.xs, + }, + height: { + xs: theme.spacing[8], + md: buttonControlHeight.xs, + }, padding: 0, borderRadius: theme.borderRadius.lg, alignItems: "center", justifyContent: "center", }, - explorerPanelButton: { - // The 32px trigger and 16px panel glyph leave 8px more trailing space than - // the 22px split-pane trigger and its 14px glyph in the row below. - marginRight: -theme.spacing[2], - }, compactHeaderMenuCluster: { flexDirection: "row", alignItems: "center", @@ -4107,26 +4036,6 @@ const styles = StyleSheet.create((theme) => ({ md: theme.spacing[2], }, }, - sourceControlButton: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - gap: theme.spacing[2], - paddingHorizontal: theme.spacing[1], - paddingVertical: theme.spacing[1], - minHeight: Math.ceil(theme.fontSize.sm * 1.5) + theme.spacing[1] * 2, - minWidth: Math.ceil(theme.fontSize.sm * 1.5) + theme.spacing[1] * 2, - borderRadius: theme.borderRadius.md, - // Match the painted right edge of the trailing split-pane glyph below. The - // two header rows intentionally use different control sizes and padding. - marginRight: -7, - }, - sourceControlButtonWithStats: { - paddingHorizontal: theme.spacing[3], - }, - sourceControlButtonHovered: { - backgroundColor: theme.colors.surface2, - }, newTabActions: { flexDirection: "row", alignItems: "center", @@ -4155,16 +4064,6 @@ const styles = StyleSheet.create((theme) => ({ gap: theme.spacing[2], }, newTabTooltipShortcut: {}, - explorerTooltipRow: { - flexDirection: "row", - alignItems: "center", - gap: theme.spacing[2], - }, - explorerTooltipText: { - fontSize: theme.fontSize.sm, - color: theme.colors.popoverForeground, - }, - explorerTooltipShortcut: {}, mobileTabsRow: { backgroundColor: theme.colors.surface0, borderBottomWidth: theme.borderWidth[1], diff --git a/packages/app/src/screens/workspace/workspace-scripts-button.tsx b/packages/app/src/screens/workspace/workspace-scripts-button.tsx index 7a2a01b1e..2f859a999 100644 --- a/packages/app/src/screens/workspace/workspace-scripts-button.tsx +++ b/packages/app/src/screens/workspace/workspace-scripts-button.tsx @@ -36,6 +36,7 @@ import { } from "@/utils/workspace-script-links"; import type { Theme } from "@/styles/theme"; import { useWorkspaceServiceRoutePreferencesStore } from "@/workspace-service-routes/store"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; type RowActionIcon = "copy" | "open" | "restart" | "start" | "stop" | "terminal"; @@ -742,9 +743,10 @@ const styles = StyleSheet.create((theme) => ({ flexShrink: 0, }, splitButton: { + height: buttonControlHeight.xs, flexDirection: "row", alignItems: "stretch", - borderRadius: theme.borderRadius.md, + borderRadius: theme.borderRadius.lg, borderWidth: theme.borderWidth[1], borderColor: theme.colors.borderAccent, overflow: "hidden", @@ -766,7 +768,6 @@ const styles = StyleSheet.create((theme) => ({ }, splitButtonPrimary: { paddingHorizontal: theme.spacing[3], - paddingVertical: theme.spacing[1], justifyContent: "center", }, splitButtonPrimaryHovered: {