feat(desktop): hide native titlebar on Windows/Linux with overlay window controls
Use titleBarStyle: 'hidden' on all platforms. On Windows/Linux, enable
titleBarOverlay with dark theme colors for native min/max/close buttons.
Extend useTrafficLightPadding to return side ('left'|'right'|null) so
consumers apply padding on the correct side per platform.
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
|||||||
HEADER_INNER_HEIGHT,
|
HEADER_INNER_HEIGHT,
|
||||||
HEADER_INNER_HEIGHT_MOBILE,
|
HEADER_INNER_HEIGHT_MOBILE,
|
||||||
HEADER_TOP_PADDING_MOBILE,
|
HEADER_TOP_PADDING_MOBILE,
|
||||||
getIsDesktopMac,
|
|
||||||
} from "@/constants/layout";
|
} from "@/constants/layout";
|
||||||
import { useDesktopDragHandlers, useTrafficLightPadding } from "@/utils/desktop-window";
|
import { useDesktopDragHandlers, useTrafficLightPadding } from "@/utils/desktop-window";
|
||||||
import { usePanelStore } from "@/stores/panel-store";
|
import { usePanelStore } from "@/stores/panel-store";
|
||||||
@@ -31,8 +30,10 @@ export function ScreenHeader({ left, right, leftStyle, rightStyle }: ScreenHeade
|
|||||||
// Only add extra padding on mobile for better touch targets; on desktop, only use safe area insets
|
// 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 topPadding = isMobile ? HEADER_TOP_PADDING_MOBILE : 0;
|
||||||
const baseHorizontalPadding = theme.spacing[2];
|
const baseHorizontalPadding = theme.spacing[2];
|
||||||
const collapsedSidebarTrafficLightInset =
|
const collapsedSidebarInset =
|
||||||
!isMobile && !desktopAgentListOpen && getIsDesktopMac() ? trafficLightPadding.left : 0;
|
!isMobile && !desktopAgentListOpen && trafficLightPadding.side
|
||||||
|
? trafficLightPadding
|
||||||
|
: { left: 0, right: 0 };
|
||||||
|
|
||||||
const dragHandlers = useDesktopDragHandlers();
|
const dragHandlers = useDesktopDragHandlers();
|
||||||
|
|
||||||
@@ -42,7 +43,10 @@ export function ScreenHeader({ left, right, leftStyle, rightStyle }: ScreenHeade
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.row,
|
styles.row,
|
||||||
{ paddingLeft: baseHorizontalPadding + collapsedSidebarTrafficLightInset },
|
{
|
||||||
|
paddingLeft: baseHorizontalPadding + collapsedSidebarInset.left,
|
||||||
|
paddingRight: baseHorizontalPadding + collapsedSidebarInset.right,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
{...dragHandlers}
|
{...dragHandlers}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -681,7 +681,7 @@ function DesktopSidebar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View style={[styles.desktopSidebar, resizeAnimatedStyle]}>
|
<Animated.View style={[styles.desktopSidebar, resizeAnimatedStyle]}>
|
||||||
{trafficLightPadding.top > 0 ? (
|
{trafficLightPadding.side === 'left' ? (
|
||||||
<View style={{ height: trafficLightPadding.top }} {...dragHandlers} />
|
<View style={{ height: trafficLightPadding.top }} {...dragHandlers} />
|
||||||
) : null}
|
) : null}
|
||||||
<View style={styles.sidebarHeader} {...dragHandlers}>
|
<View style={styles.sidebarHeader} {...dragHandlers}>
|
||||||
|
|||||||
@@ -886,7 +886,10 @@ function SplitPaneView({
|
|||||||
style={[
|
style={[
|
||||||
styles.paneTabs,
|
styles.paneTabs,
|
||||||
isFocusModeEnabled &&
|
isFocusModeEnabled &&
|
||||||
trafficLightPadding.left > 0 && { paddingLeft: trafficLightPadding.left },
|
trafficLightPadding.side && {
|
||||||
|
paddingLeft: trafficLightPadding.left,
|
||||||
|
paddingRight: trafficLightPadding.right,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<WorkspaceDesktopTabsRow
|
<WorkspaceDesktopTabsRow
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ export const MAX_CONTENT_WIDTH = 820;
|
|||||||
export const DESKTOP_TRAFFIC_LIGHT_WIDTH = 78;
|
export const DESKTOP_TRAFFIC_LIGHT_WIDTH = 78;
|
||||||
export const DESKTOP_TRAFFIC_LIGHT_HEIGHT = 45;
|
export const DESKTOP_TRAFFIC_LIGHT_HEIGHT = 45;
|
||||||
|
|
||||||
|
// Windows/Linux window controls (minimize/maximize/close) — top-right
|
||||||
|
export const DESKTOP_WINDOW_CONTROLS_WIDTH = 140;
|
||||||
|
export const DESKTOP_WINDOW_CONTROLS_HEIGHT = 48;
|
||||||
|
|
||||||
// Check if running in desktop app (any OS)
|
// Check if running in desktop app (any OS)
|
||||||
function isDesktopEnvironment(): boolean {
|
function isDesktopEnvironment(): boolean {
|
||||||
if (Platform.OS !== "web") return false;
|
if (Platform.OS !== "web") return false;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { PaseoLogo } from "@/components/icons/paseo-logo";
|
|||||||
import { SidebarMenuToggle } from "@/components/headers/menu-header";
|
import { SidebarMenuToggle } from "@/components/headers/menu-header";
|
||||||
import { useOpenProjectPicker } from "@/hooks/use-open-project-picker";
|
import { useOpenProjectPicker } from "@/hooks/use-open-project-picker";
|
||||||
import { usePanelStore } from "@/stores/panel-store";
|
import { usePanelStore } from "@/stores/panel-store";
|
||||||
import { getIsDesktopMac } from "@/constants/layout";
|
|
||||||
import { useDesktopDragHandlers, useTrafficLightPadding } from "@/utils/desktop-window";
|
import { useDesktopDragHandlers, useTrafficLightPadding } from "@/utils/desktop-window";
|
||||||
|
|
||||||
export function OpenProjectScreen({ serverId }: { serverId: string }) {
|
export function OpenProjectScreen({ serverId }: { serverId: string }) {
|
||||||
@@ -19,8 +18,10 @@ export function OpenProjectScreen({ serverId }: { serverId: string }) {
|
|||||||
const openProjectPicker = useOpenProjectPicker(serverId);
|
const openProjectPicker = useOpenProjectPicker(serverId);
|
||||||
|
|
||||||
const isMobile = UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
const isMobile = UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
|
||||||
const needsTrafficLightInset = !isMobile && !desktopAgentListOpen && getIsDesktopMac();
|
const collapsedSidebarInset =
|
||||||
const trafficLightInset = needsTrafficLightInset ? trafficLightPadding.left : 0;
|
!isMobile && !desktopAgentListOpen && trafficLightPadding.side
|
||||||
|
? trafficLightPadding
|
||||||
|
: { left: 0, right: 0 };
|
||||||
const dragHandlers = useDesktopDragHandlers();
|
const dragHandlers = useDesktopDragHandlers();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -31,7 +32,7 @@ export function OpenProjectScreen({ serverId }: { serverId: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container} {...dragHandlers}>
|
<View style={styles.container} {...dragHandlers}>
|
||||||
<View style={[styles.menuToggle, { paddingTop: insets.top, paddingLeft: trafficLightInset }]}>
|
<View style={[styles.menuToggle, { paddingTop: insets.top, paddingLeft: collapsedSidebarInset.left, paddingRight: collapsedSidebarInset.right }]}>
|
||||||
<SidebarMenuToggle />
|
<SidebarMenuToggle />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { Platform, type PointerEvent as RNPointerEvent, type ViewProps } from "react-native";
|
import { Platform, type PointerEvent as RNPointerEvent, type ViewProps } from "react-native";
|
||||||
import {
|
import {
|
||||||
getIsDesktopMac,
|
getIsDesktopMac,
|
||||||
|
getIsDesktop,
|
||||||
DESKTOP_TRAFFIC_LIGHT_WIDTH,
|
DESKTOP_TRAFFIC_LIGHT_WIDTH,
|
||||||
DESKTOP_TRAFFIC_LIGHT_HEIGHT,
|
DESKTOP_TRAFFIC_LIGHT_HEIGHT,
|
||||||
|
DESKTOP_WINDOW_CONTROLS_WIDTH,
|
||||||
|
DESKTOP_WINDOW_CONTROLS_HEIGHT,
|
||||||
} from "@/constants/layout";
|
} from "@/constants/layout";
|
||||||
import { getDesktopWindow } from "@/desktop/electron/window";
|
import { getDesktopWindow } from "@/desktop/electron/window";
|
||||||
import { isDesktop } from "@/desktop/host";
|
import { isDesktop } from "@/desktop/host";
|
||||||
@@ -120,11 +123,11 @@ export function useDesktopDragHandlers(): DesktopDragViewProps {
|
|||||||
}, [isActive]);
|
}, [isActive]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTrafficLightPadding(): { left: number; top: number } {
|
export function useTrafficLightPadding(): { left: number; right: number; top: number; side: 'left' | 'right' | null } {
|
||||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.OS !== "web" || !getIsDesktopMac()) return;
|
if (Platform.OS !== "web" || !getIsDesktop()) return;
|
||||||
|
|
||||||
let disposed = false;
|
let disposed = false;
|
||||||
let cleanup: (() => void) | undefined;
|
let cleanup: (() => void) | undefined;
|
||||||
@@ -175,12 +178,23 @@ export function useTrafficLightPadding(): { left: number; top: number } {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!getIsDesktopMac() || isFullscreen) {
|
if (!getIsDesktop() || isFullscreen) {
|
||||||
return { left: 0, top: 0 };
|
return { left: 0, right: 0, top: 0, side: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getIsDesktopMac()) {
|
||||||
|
return {
|
||||||
|
left: DESKTOP_TRAFFIC_LIGHT_WIDTH,
|
||||||
|
right: 0,
|
||||||
|
top: DESKTOP_TRAFFIC_LIGHT_HEIGHT,
|
||||||
|
side: 'left',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
left: DESKTOP_TRAFFIC_LIGHT_WIDTH,
|
left: 0,
|
||||||
top: DESKTOP_TRAFFIC_LIGHT_HEIGHT,
|
right: DESKTOP_WINDOW_CONTROLS_WIDTH,
|
||||||
|
top: DESKTOP_WINDOW_CONTROLS_HEIGHT,
|
||||||
|
side: 'right',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,8 +92,17 @@ async function createMainWindow(): Promise<void> {
|
|||||||
height: 800,
|
height: 800,
|
||||||
show: false,
|
show: false,
|
||||||
...(iconPath ? { icon: iconPath } : {}),
|
...(iconPath ? { icon: iconPath } : {}),
|
||||||
titleBarStyle: isMac ? "hidden" : "default",
|
titleBarStyle: "hidden",
|
||||||
trafficLightPosition: isMac ? { x: 16, y: 14 } : undefined,
|
...(isMac
|
||||||
|
? { trafficLightPosition: { x: 16, y: 14 } }
|
||||||
|
: {
|
||||||
|
titleBarOverlay: {
|
||||||
|
color: "#18181c",
|
||||||
|
symbolColor: "#e4e4e7",
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
}),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: getPreloadPath(),
|
preload: getPreloadPath(),
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user