diff --git a/packages/app/src/components/explorer-sidebar.tsx b/packages/app/src/components/explorer-sidebar.tsx index 1499bfe03..50bbfda5d 100644 --- a/packages/app/src/components/explorer-sidebar.tsx +++ b/packages/app/src/components/explorer-sidebar.tsx @@ -17,6 +17,7 @@ import { HEADER_INNER_HEIGHT } from "@/constants/layout"; import { GitDiffPane } from "./git-diff-pane"; import { FileExplorerPane } from "./file-explorer-pane"; import { useKeyboardShiftStyle } from "@/hooks/use-keyboard-shift-style"; +import { useWindowControlsPadding } from "@/utils/desktop-window"; const MIN_CHAT_WIDTH = 400; function logExplorerSidebar(_event: string, _details: Record): void {} @@ -336,12 +337,13 @@ function SidebarContent({ onOpenFile, }: SidebarContentProps) { const { theme } = useUnistyles(); + const padding = useWindowControlsPadding("explorerSidebar"); const resolvedTab: ExplorerTab = !isGit && activeTab === "changes" ? "files" : activeTab; return ( {/* Header with tabs and close button */} - + {isGit && ( state.desktop.agentListOpen); - const trafficLightPadding = useTrafficLightPadding(); + const padding = useWindowControlsPadding("header"); // Only add extra padding on mobile for better touch targets; on desktop, only use safe area insets const topPadding = isMobile ? HEADER_TOP_PADDING_MOBILE : 0; const baseHorizontalPadding = theme.spacing[2]; - const collapsedSidebarInset = - !isMobile && !desktopAgentListOpen && trafficLightPadding.side - ? trafficLightPadding - : { left: 0, right: 0 }; const dragHandlers = useDesktopDragHandlers(); @@ -45,8 +39,8 @@ export function ScreenHeader({ left, right, leftStyle, rightStyle, borderless }: style={[ styles.row, { - paddingLeft: baseHorizontalPadding + collapsedSidebarInset.left, - paddingRight: baseHorizontalPadding + collapsedSidebarInset.right, + paddingLeft: baseHorizontalPadding + padding.left, + paddingRight: baseHorizontalPadding + padding.right, }, borderless && styles.borderless, ]} @@ -90,6 +84,6 @@ const styles = StyleSheet.create((theme) => ({ gap: theme.spacing[2], }, borderless: { - borderBottomWidth: 0, + borderBottomColor: "transparent", }, })); diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index bce074c7b..718575dda 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -35,7 +35,7 @@ import { type SidebarProjectEntry, } from "@/hooks/use-sidebar-workspaces-list"; import { useSidebarAnimation } from "@/contexts/sidebar-animation-context"; -import { useDesktopDragHandlers, useTrafficLightPadding } from "@/utils/desktop-window"; +import { useDesktopDragHandlers, useWindowControlsPadding } from "@/utils/desktop-window"; import { Combobox } from "@/components/ui/combobox"; import { getHostRuntimeStore, useHosts } from "@/runtime/host-runtime"; import { formatConnectionStatus } from "@/utils/daemons"; @@ -632,7 +632,7 @@ function DesktopSidebar({ }: DesktopSidebarProps) { const newAgentKeys = useShortcutKeys("new-agent"); const dragHandlers = useDesktopDragHandlers(); - const trafficLightPadding = useTrafficLightPadding(); + const padding = useWindowControlsPadding("sidebar"); const sidebarWidth = usePanelStore((state) => state.sidebarWidth); const setSidebarWidth = usePanelStore((state) => state.setSidebarWidth); const { width: viewportWidth } = useWindowDimensions(); @@ -682,9 +682,7 @@ function DesktopSidebar({ return ( - {trafficLightPadding.side === 'left' ? ( - - ) : null} + {padding.top > 0 ? : null} diff --git a/packages/app/src/components/split-container.tsx b/packages/app/src/components/split-container.tsx index 88f458407..b7c192f78 100644 --- a/packages/app/src/components/split-container.tsx +++ b/packages/app/src/components/split-container.tsx @@ -32,7 +32,7 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { ResizeHandle } from "@/components/resize-handle"; import { shouldFocusPaneFromEventTarget } from "@/components/split-container-pane-focus"; import { usePanelStore } from "@/stores/panel-store"; -import { useTrafficLightPadding } from "@/utils/desktop-window"; +import { useWindowControlsPadding } from "@/utils/desktop-window"; import { computeTabDropPreview, type TabDropPreview, @@ -809,8 +809,7 @@ function SplitPaneView({ const { theme } = useUnistyles(); const paneRef = useRef(null); const stableOnFocusPane = useStableEvent(onFocusPane); - const isFocusModeEnabled = usePanelStore((s) => s.desktop.focusModeEnabled); - const trafficLightPadding = useTrafficLightPadding(); + const padding = useWindowControlsPadding("tabRow"); const paneState = useMemo( () => deriveWorkspacePaneState({ @@ -885,11 +884,7 @@ function SplitPaneView({ + - + diff --git a/packages/app/src/utils/desktop-window.ts b/packages/app/src/utils/desktop-window.ts index 5098ce2dd..15eeb4e3a 100644 --- a/packages/app/src/utils/desktop-window.ts +++ b/packages/app/src/utils/desktop-window.ts @@ -10,6 +10,7 @@ import { } from "@/constants/layout"; import { getDesktopWindow } from "@/desktop/electron/window"; import { isDesktop } from "@/desktop/host"; +import { usePanelStore } from "@/stores/panel-store"; import { readFiniteScreenPoint } from "./desktop-window-drag-coordinates"; export async function toggleMaximize() { @@ -123,7 +124,15 @@ export function useDesktopDragHandlers(): DesktopDragViewProps { }, [isActive]); } -export function useTrafficLightPadding(): { left: number; right: number; top: number; side: 'left' | 'right' | null } { +type RawWindowControlsPadding = { + left: number; + right: number; + top: number; +}; + +type WindowControlsPaddingRole = "sidebar" | "header" | "tabRow" | "explorerSidebar"; + +function useRawWindowControlsPadding(): RawWindowControlsPadding { const [isFullscreen, setIsFullscreen] = useState(false); useEffect(() => { @@ -178,23 +187,52 @@ export function useTrafficLightPadding(): { left: number; right: number; top: nu }; }, []); - if (!getIsDesktop() || isFullscreen) { - return { left: 0, right: 0, top: 0, side: null }; - } + return useMemo((): RawWindowControlsPadding => { + if (!getIsDesktop() || isFullscreen) { + return { left: 0, right: 0, top: 0 }; + } + + if (getIsDesktopMac()) { + return { + left: DESKTOP_TRAFFIC_LIGHT_WIDTH, + right: 0, + top: DESKTOP_TRAFFIC_LIGHT_HEIGHT, + }; + } - if (getIsDesktopMac()) { return { - left: DESKTOP_TRAFFIC_LIGHT_WIDTH, - right: 0, - top: DESKTOP_TRAFFIC_LIGHT_HEIGHT, - side: 'left', + left: 0, + right: DESKTOP_WINDOW_CONTROLS_WIDTH, + top: DESKTOP_WINDOW_CONTROLS_HEIGHT, }; + }, [isFullscreen]); +} + +export function useWindowControlsPadding( + role: WindowControlsPaddingRole, +): { left: number; right: number; top: number } { + const sidebarOpen = usePanelStore((state) => state.desktop.agentListOpen); + const explorerOpen = usePanelStore((state) => state.desktop.fileExplorerOpen); + const focusModeEnabled = usePanelStore((state) => state.desktop.focusModeEnabled); + const rawPadding = useRawWindowControlsPadding(); + const sidebarClosed = !sidebarOpen; + + let left = 0; + let right = 0; + let top = 0; + + if (role === "sidebar") { + left = rawPadding.left; + top = rawPadding.top; + } else if (role === "header") { + left = sidebarClosed ? rawPadding.left : 0; + right = explorerOpen ? 0 : rawPadding.right; + } else if (role === "tabRow") { + left = sidebarClosed && focusModeEnabled ? rawPadding.left : 0; + right = focusModeEnabled && !explorerOpen ? rawPadding.right : 0; + } else if (role === "explorerSidebar") { + right = rawPadding.right; } - return { - left: 0, - right: DESKTOP_WINDOW_CONTROLS_WIDTH, - top: DESKTOP_WINDOW_CONTROLS_HEIGHT, - side: 'right', - }; + return useMemo(() => ({ left, right, top }), [left, right, top]); }