From 6d13796b2d1a886a5d7270cf9efc86213e31d65e Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 3 May 2026 19:49:10 +0700 Subject: [PATCH] Move workspace gate background and root layout off useUnistyles The workspace gate (loading/unreachable/missing) used surfaceWorkspace, which clashed with the panels' surface0. Switch the gate shell to surface0 so it matches the agent panel. Also remove three hot-path useUnistyles() calls from _layout.tsx that were re-rendering the whole app on every theme/breakpoint/insets change: RootLayout and AppContainer move to StyleSheet.create, and the desktop window-controls effect lifts into a small leaf component. --- packages/app/src/app/_layout.tsx | 75 +++++++++---------- .../screens/workspace/workspace-screen.tsx | 2 +- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx index c51fb8a67..1042a1189 100644 --- a/packages/app/src/app/_layout.tsx +++ b/packages/app/src/app/_layout.tsx @@ -26,7 +26,7 @@ import { Gesture, GestureDetector, GestureHandlerRootView } from "react-native-g import { KeyboardProvider } from "react-native-keyboard-controller"; import { Extrapolation, interpolate, runOnJS, useSharedValue } from "react-native-reanimated"; import { SafeAreaProvider } from "react-native-safe-area-context"; -import { UnistylesRuntime, useUnistyles } from "react-native-unistyles"; +import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles"; import { CommandCenter } from "@/components/command-center"; import { WorktreeSetupCalloutSource } from "@/components/worktree-setup-callout-source"; import { DownloadToast } from "@/components/download-toast"; @@ -58,7 +58,6 @@ import { getDesktopHost } from "@/desktop/host"; import { RosettaCalloutSource } from "@/desktop/updates/rosetta-callout-source"; import { UpdateCalloutSource } from "@/desktop/updates/update-callout-source"; import { useActiveWorktreeNewAction } from "@/hooks/use-active-worktree-new-action"; -import { useColorScheme } from "@/hooks/use-color-scheme"; import { useFaviconStatus } from "@/hooks/use-favicon-status"; import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts"; import { useOpenProject } from "@/hooks/use-open-project"; @@ -393,7 +392,6 @@ function AppContainer({ selectedAgentId, chromeEnabled: chromeEnabledOverride, }: AppContainerProps) { - const { theme } = useUnistyles(); const daemons = useHosts(); const { settings, updateSettings } = useAppSettings(); const toggleMobileAgentList = usePanelStore((state) => state.toggleMobileAgentList); @@ -450,13 +448,8 @@ function AppContainer({ useActiveWorktreeNewAction(); - const containerStyle = useMemo( - () => ({ flex: 1 as const, backgroundColor: theme.colors.surface0 }), - [theme.colors.surface0], - ); - const content = ( - + {!isCompactLayout && chromeEnabled && !isFocusModeEnabled && ( @@ -594,9 +587,6 @@ function MobileGestureWrapper({ function ProvidersWrapper({ children }: { children: ReactNode }) { const { settings, isLoading: settingsLoading } = useAppSettings(); const { upsertConnectionFromOfferUrl } = useHostMutations(); - const systemColorScheme = useColorScheme(); - const { theme } = useUnistyles(); - const resolvedTheme = settings.theme === "auto" ? (systemColorScheme ?? "light") : settings.theme; // Apply theme setting on mount and when it changes useEffect(() => { @@ -609,21 +599,9 @@ function ProvidersWrapper({ children }: { children: ReactNode }) { } }, [settingsLoading, settings.theme]); - useEffect(() => { - if (settingsLoading || isNative) { - return; - } - - void updateDesktopWindowControls({ - backgroundColor: theme.colors.surface0, - foregroundColor: theme.colors.foreground, - }).catch((error) => { - console.warn("[DesktopWindow] Failed to update window controls overlay", error); - }); - }, [settingsLoading, resolvedTheme, theme.colors.foreground, theme.colors.surface0]); - return ( + @@ -632,6 +610,24 @@ function ProvidersWrapper({ children }: { children: ReactNode }) { ); } +function DesktopWindowControlsSync({ enabled }: { enabled: boolean }) { + const { theme } = useUnistyles(); + const surface0 = theme.colors.surface0; + const foreground = theme.colors.foreground; + + useEffect(() => { + if (!enabled || isNative) return; + void updateDesktopWindowControls({ + backgroundColor: surface0, + foregroundColor: foreground, + }).catch((error) => { + console.warn("[DesktopWindow] Failed to update window controls overlay", error); + }); + }, [enabled, surface0, foreground]); + + return null; +} + function OfferLinkListener({ upsertDaemonFromOfferUrl, }: { @@ -911,20 +907,23 @@ function RootProviders({ children }: { children: ReactNode }) { } export default function RootLayout() { - const { theme } = useUnistyles(); - const gestureRootStyle = useMemo( - () => ({ flex: 1, backgroundColor: theme.colors.surface0 }), - [theme.colors.surface0], - ); - return ( - - - - - - - + + + + + + + + + ); } + +const layoutStyles = StyleSheet.create((theme) => ({ + surfaceFill: { + flex: 1, + backgroundColor: theme.colors.surface0, + }, +})); diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx index b1eb0893e..38df733bc 100644 --- a/packages/app/src/screens/workspace/workspace-screen.tsx +++ b/packages/app/src/screens/workspace/workspace-screen.tsx @@ -1259,7 +1259,7 @@ function renderWorkspaceScreenGateShell(input: { return ( - + {input.gate}