diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx index 6bc73f08e..953dd493e 100644 --- a/packages/app/src/app/_layout.tsx +++ b/packages/app/src/app/_layout.tsx @@ -17,7 +17,6 @@ import { useAppSettings } from "@/hooks/use-settings"; import { useFaviconStatus } from "@/hooks/use-favicon-status"; import { View, Text } from "react-native"; import { UnistylesRuntime, useUnistyles } from "react-native-unistyles"; -import { darkTheme } from "@/styles/theme"; import { QueryClientProvider } from "@tanstack/react-query"; import { getHostRuntimeStore, @@ -603,6 +602,7 @@ function FaviconStatusSync() { function RootStack() { const storeReady = useStoreReady(); + const { theme } = useUnistyles(); return ( @@ -654,8 +654,10 @@ function NavigationActiveWorkspaceObserver() { } export default function RootLayout() { + const { theme } = useUnistyles(); + return ( - + diff --git a/packages/app/src/components/agent-form/agent-form-dropdowns.tsx b/packages/app/src/components/agent-form/agent-form-dropdowns.tsx index 6137eb76d..2bf142aba 100644 --- a/packages/app/src/components/agent-form/agent-form-dropdowns.tsx +++ b/packages/app/src/components/agent-form/agent-form-dropdowns.tsx @@ -1444,11 +1444,7 @@ const styles = StyleSheet.create((theme) => ({ borderRadius: theme.borderRadius.lg, borderWidth: 1, borderColor: theme.colors.borderAccent, - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, maxHeight: 400, overflow: "hidden", }, diff --git a/packages/app/src/components/agent-stream-view.tsx b/packages/app/src/components/agent-stream-view.tsx index 6e136bbd5..a4bf19ddf 100644 --- a/packages/app/src/components/agent-stream-view.tsx +++ b/packages/app/src/components/agent-stream-view.tsx @@ -1127,14 +1127,7 @@ const stylesheet = StyleSheet.create((theme) => ({ backgroundColor: theme.colors.surface2, alignItems: "center", justifyContent: "center", - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 2, - }, - shadowOpacity: 0.25, - shadowRadius: 3.84, - elevation: 5, + ...theme.shadow.sm, }, scrollToBottomIcon: { color: theme.colors.foreground, diff --git a/packages/app/src/components/command-center.tsx b/packages/app/src/components/command-center.tsx index b243ace72..0854707f3 100644 --- a/packages/app/src/components/command-center.tsx +++ b/packages/app/src/components/command-center.tsx @@ -268,10 +268,7 @@ const styles = StyleSheet.create((theme) => ({ borderWidth: 1, borderRadius: theme.borderRadius.lg, overflow: "hidden", - shadowColor: "#000", - shadowOpacity: 0.4, - shadowRadius: 24, - shadowOffset: { width: 0, height: 12 }, + ...theme.shadow.lg, }, header: { paddingHorizontal: theme.spacing[4], diff --git a/packages/app/src/components/download-toast.tsx b/packages/app/src/components/download-toast.tsx index 7dc5f97d0..61a7ef713 100644 --- a/packages/app/src/components/download-toast.tsx +++ b/packages/app/src/components/download-toast.tsx @@ -108,11 +108,7 @@ const styles = StyleSheet.create((theme) => ({ borderColor: theme.colors.border, paddingVertical: theme.spacing[3], paddingHorizontal: theme.spacing[4], - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.15, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, }, textContainer: { flex: 1, diff --git a/packages/app/src/components/file-pane.tsx b/packages/app/src/components/file-pane.tsx index 40400ed0d..2f375cf60 100644 --- a/packages/app/src/components/file-pane.tsx +++ b/packages/app/src/components/file-pane.tsx @@ -119,7 +119,7 @@ function FilePreviewBody({ filePath, }: FilePreviewBodyProps) { const { theme } = useUnistyles(); - const isDark = theme.colors.surface0 === "#181B1A"; + const isDark = theme.colorScheme === "dark"; const colorMap = isDark ? darkHighlightColors : lightHighlightColors; const baseColor = isDark ? "#c9d1d9" : "#24292f"; diff --git a/packages/app/src/components/git-diff-pane.tsx b/packages/app/src/components/git-diff-pane.tsx index 6cf0d59c9..12a85fe3a 100644 --- a/packages/app/src/components/git-diff-pane.tsx +++ b/packages/app/src/components/git-diff-pane.tsx @@ -86,9 +86,8 @@ interface HighlightedTextProps { function HighlightedText({ tokens, lineType }: HighlightedTextProps) { const { theme } = useUnistyles(); - const isDark = theme.colors.surface0 === "#181B1A"; + const isDark = theme.colorScheme === "dark"; - // Get color for a highlight style const getTokenColor = (style: HighlightStyle | null): string => { const baseColor = isDark ? "#c9d1d9" : "#24292f"; if (!style) return baseColor; diff --git a/packages/app/src/components/icons/paseo-logo.tsx b/packages/app/src/components/icons/paseo-logo.tsx index b484f5163..72bdc19ed 100644 --- a/packages/app/src/components/icons/paseo-logo.tsx +++ b/packages/app/src/components/icons/paseo-logo.tsx @@ -1,16 +1,20 @@ import Svg, { Path } from "react-native-svg"; +import { useUnistyles } from "react-native-unistyles"; interface PaseoLogoProps { size?: number; color?: string; } -export function PaseoLogo({ size = 64, color = "white" }: PaseoLogoProps) { +export function PaseoLogo({ size = 64, color }: PaseoLogoProps) { + const { theme } = useUnistyles(); + const fill = color ?? theme.colors.foreground; + return ( ); diff --git a/packages/app/src/components/project-picker-modal.tsx b/packages/app/src/components/project-picker-modal.tsx index 3f8e84b76..0556f2aec 100644 --- a/packages/app/src/components/project-picker-modal.tsx +++ b/packages/app/src/components/project-picker-modal.tsx @@ -269,10 +269,7 @@ const styles = StyleSheet.create((theme) => ({ borderWidth: 1, borderRadius: theme.borderRadius.lg, overflow: "hidden", - shadowColor: "#000", - shadowOpacity: 0.4, - shadowRadius: 24, - shadowOffset: { width: 0, height: 12 }, + ...theme.shadow.lg, }, header: { paddingHorizontal: theme.spacing[4], diff --git a/packages/app/src/components/sidebar-workspace-list.tsx b/packages/app/src/components/sidebar-workspace-list.tsx index c47fe09b5..1e2727387 100644 --- a/packages/app/src/components/sidebar-workspace-list.tsx +++ b/packages/app/src/components/sidebar-workspace-list.tsx @@ -2022,11 +2022,7 @@ const styles = StyleSheet.create((theme) => ({ borderColor: theme.colors.border, transform: [{ scale: 1.02 }], zIndex: 3, - elevation: 4, - shadowColor: "#000", - shadowOpacity: 0.2, - shadowRadius: 8, - shadowOffset: { width: 0, height: 4 }, + ...theme.shadow.md, }, projectRowLeft: { flexDirection: "row", @@ -2169,11 +2165,7 @@ const styles = StyleSheet.create((theme) => ({ borderColor: theme.colors.border, transform: [{ scale: 1.02 }], zIndex: 3, - elevation: 4, - shadowColor: "#000", - shadowOpacity: 0.2, - shadowRadius: 8, - shadowOffset: { width: 0, height: 4 }, + ...theme.shadow.md, }, sidebarRowSelected: { backgroundColor: theme.colors.surface1, diff --git a/packages/app/src/components/toast-host.tsx b/packages/app/src/components/toast-host.tsx index 5ffe0c6c3..2ff21f875 100644 --- a/packages/app/src/components/toast-host.tsx +++ b/packages/app/src/components/toast-host.tsx @@ -259,11 +259,7 @@ const styles = StyleSheet.create((theme) => ({ borderColor: theme.colors.border, paddingVertical: theme.spacing[2], paddingHorizontal: theme.spacing[3], - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.15, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, }, toastSuccess: { borderColor: theme.colors.border, diff --git a/packages/app/src/components/ui/autocomplete.tsx b/packages/app/src/components/ui/autocomplete.tsx index ad7d2172c..95ee62f29 100644 --- a/packages/app/src/components/ui/autocomplete.tsx +++ b/packages/app/src/components/ui/autocomplete.tsx @@ -248,11 +248,7 @@ const styles = StyleSheet.create(((theme: Theme) => ({ borderRadius: theme.borderRadius.lg, paddingHorizontal: theme.spacing[3], paddingVertical: theme.spacing[3], - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, }, detailLabel: { color: theme.colors.foreground, @@ -275,11 +271,7 @@ const styles = StyleSheet.create(((theme: Theme) => ({ borderColor: theme.colors.borderAccent, borderRadius: theme.borderRadius.lg, overflow: "hidden", - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, }, scrollView: { flexGrow: 0, diff --git a/packages/app/src/components/ui/button.tsx b/packages/app/src/components/ui/button.tsx index 11105236e..bc16ed2c4 100644 --- a/packages/app/src/components/ui/button.tsx +++ b/packages/app/src/components/ui/button.tsx @@ -136,9 +136,11 @@ export function Button({ return {leftIcon}; } - const color = variant === "ghost" - ? (isGhostHovered ? theme.colors.foreground : theme.colors.foregroundMuted) - : theme.colors.foreground; + const color = variant === "default" + ? theme.colors.accentForeground + : variant === "ghost" + ? (isGhostHovered ? theme.colors.foreground : theme.colors.foregroundMuted) + : theme.colors.foreground; const iconSize = ICON_SIZE[size]; // Render function diff --git a/packages/app/src/components/ui/combobox.tsx b/packages/app/src/components/ui/combobox.tsx index 6212a2e3c..450c46597 100644 --- a/packages/app/src/components/ui/combobox.tsx +++ b/packages/app/src/components/ui/combobox.tsx @@ -845,11 +845,7 @@ const styles = StyleSheet.create((theme) => ({ borderRadius: theme.borderRadius.lg, borderWidth: 1, borderColor: theme.colors.border, - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, maxHeight: 400, overflow: "hidden", }, diff --git a/packages/app/src/components/ui/context-menu.tsx b/packages/app/src/components/ui/context-menu.tsx index 4448d7a92..c539af754 100644 --- a/packages/app/src/components/ui/context-menu.tsx +++ b/packages/app/src/components/ui/context-menu.tsx @@ -722,11 +722,7 @@ const styles = StyleSheet.create((theme) => ({ borderWidth: 1, borderColor: theme.colors.border, borderRadius: theme.borderRadius.lg, - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, overflow: "hidden", }, sheetBackground: { diff --git a/packages/app/src/components/ui/dropdown-menu.tsx b/packages/app/src/components/ui/dropdown-menu.tsx index 9ba84fca0..32280504a 100644 --- a/packages/app/src/components/ui/dropdown-menu.tsx +++ b/packages/app/src/components/ui/dropdown-menu.tsx @@ -605,11 +605,7 @@ const styles = StyleSheet.create((theme) => ({ borderColor: theme.colors.borderAccent, borderRadius: theme.borderRadius.lg, overflow: "hidden", - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, }, labelContainer: { paddingHorizontal: theme.spacing[3], diff --git a/packages/app/src/components/ui/tooltip.tsx b/packages/app/src/components/ui/tooltip.tsx index b762b67bb..1b08ed45b 100644 --- a/packages/app/src/components/ui/tooltip.tsx +++ b/packages/app/src/components/ui/tooltip.tsx @@ -535,11 +535,7 @@ const styles = StyleSheet.create((theme) => ({ backgroundColor: theme.colors.popover, borderWidth: theme.borderWidth[2], borderColor: theme.colors.border, - shadowColor: "#000", - shadowOpacity: 0.2, - shadowRadius: 12, - shadowOffset: { width: 0, height: 6 }, - elevation: 6, + ...theme.shadow.sm, zIndex: 1000, }, })); diff --git a/packages/app/src/components/voice-button.tsx b/packages/app/src/components/voice-button.tsx index ed6c22703..20aeb2c14 100644 --- a/packages/app/src/components/voice-button.tsx +++ b/packages/app/src/components/voice-button.tsx @@ -25,11 +25,7 @@ const styles = StyleSheet.create((theme) => ({ borderRadius: theme.borderRadius.full, alignItems: "center", justifyContent: "center", - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.3, - shadowRadius: 8, - elevation: 8, + ...theme.shadow.md, }, buttonIdle: { backgroundColor: theme.colors.surface2, diff --git a/packages/app/src/components/web-desktop-scrollbar.tsx b/packages/app/src/components/web-desktop-scrollbar.tsx index d8fb148a3..62f7b063b 100644 --- a/packages/app/src/components/web-desktop-scrollbar.tsx +++ b/packages/app/src/components/web-desktop-scrollbar.tsx @@ -350,8 +350,7 @@ export function WebDesktopScrollbarOverlay({ ? HANDLE_OPACITY_VISIBLE : 0; const handleWidth = isDragging || isHandleHovered ? HANDLE_WIDTH_ACTIVE : HANDLE_WIDTH_IDLE; - const isDark = theme.colors.surface0 === "#181B1A"; - const handleColor = isDark ? theme.colors.palette.zinc[500] : theme.colors.palette.zinc[700]; + const handleColor = theme.colors.scrollbarHandle; const handleCursor = isDragging ? "grabbing" : "grab"; const handleTravelDurationMs = isDragging || isScrollActive ? 0 : HANDLE_TRAVEL_TRANSITION_DURATION_MS; diff --git a/packages/app/src/components/welcome-screen.tsx b/packages/app/src/components/welcome-screen.tsx index 8ed402b76..19f7a5310 100644 --- a/packages/app/src/components/welcome-screen.tsx +++ b/packages/app/src/components/welcome-screen.tsx @@ -320,7 +320,7 @@ export function WelcomeScreen({ onHostAdded }: WelcomeScreenProps) { testID="welcome-screen" > - + Welcome to Paseo {showHostList ? "Connecting to your hosts…" : "Connect to your host to start"} diff --git a/packages/app/src/desktop/updates/update-banner.tsx b/packages/app/src/desktop/updates/update-banner.tsx index 82fb40c85..b5f927a4e 100644 --- a/packages/app/src/desktop/updates/update-banner.tsx +++ b/packages/app/src/desktop/updates/update-banner.tsx @@ -128,11 +128,7 @@ const styles = StyleSheet.create((theme) => ({ paddingVertical: theme.spacing[3], paddingLeft: theme.spacing[4], paddingRight: theme.spacing[3], - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.2, - shadowRadius: 12, - elevation: 8, + ...theme.shadow.md, maxWidth: 480, }, closeButton: { diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx index 9ccb82a7f..e73eadb02 100644 --- a/packages/app/src/screens/workspace/workspace-screen.tsx +++ b/packages/app/src/screens/workspace/workspace-screen.tsx @@ -8,7 +8,6 @@ import { Platform, Pressable, Text, - useColorScheme, View, } from "react-native"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; @@ -578,8 +577,7 @@ function useCloseTabs(): UseCloseTabsResult { function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps) { const { theme } = useUnistyles(); const insets = useSafeAreaInsets(); - const isDarkMode = useColorScheme() === "dark"; - const mainBackgroundColor = isDarkMode ? theme.colors.surface1 : theme.colors.surface0; + const mainBackgroundColor = theme.colors.surfaceWorkspace; const toast = useToast(); const isMobile = isCompactFormFactor(); const isFocusModeEnabled = usePanelStore((state) => state.desktop.focusModeEnabled); diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index 31c08c83c..a38b15958 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -112,11 +112,15 @@ const lightSemanticColors = { surface3: "#e4e4e7", // Highest elevation (was zinc-300, now zinc-200) surface4: "#d4d4d8", // Extra emphasis (was zinc-400, now zinc-300) surfaceSidebar: "#f4f4f5", // Sidebar background (darker than main) + surfaceWorkspace: "#ffffff", // Workspace main background // Text foreground: "#09090b", foregroundMuted: "#71717a", + // Controls + scrollbarHandle: "#3f3f46", // zinc-700 + // Borders - shifted one step lighter border: "#e4e4e7", // (was zinc-200, now zinc-200 - keep for contrast) borderAccent: "#ececf1", // Softer accent border for low-emphasis outlines @@ -182,11 +186,15 @@ const darkSemanticColors = { surface3: "#434645", // Highest elevation surface4: "#595B5B", // Extra emphasis surfaceSidebar: "#141716", // Sidebar background (darker than main) + surfaceWorkspace: "#1E2120", // Workspace main background (surface1) // Text foreground: "#fafafa", foregroundMuted: "#A1A5A4", + // Controls + scrollbarHandle: "#71717a", // zinc-500 + // Borders border: "#252B2A", borderAccent: "#2F3534", @@ -310,18 +318,60 @@ const commonTheme = { } as const; export const darkTheme = { + colorScheme: "dark" as const, colors: { ...darkSemanticColors, palette: baseColors, }, + shadow: { + sm: { + shadowColor: "rgba(0, 0, 0, 0.25)", + shadowOffset: { width: 0, height: 2 }, + shadowRadius: 4, + elevation: 2, + }, + md: { + shadowColor: "rgba(0, 0, 0, 0.20)", + shadowOffset: { width: 0, height: 4 }, + shadowRadius: 8, + elevation: 8, + }, + lg: { + shadowColor: "rgba(0, 0, 0, 0.40)", + shadowOffset: { width: 0, height: 12 }, + shadowRadius: 24, + elevation: 8, + }, + }, ...commonTheme, } as const; export const lightTheme = { + colorScheme: "light" as const, colors: { ...lightSemanticColors, palette: baseColors, }, + shadow: { + sm: { + shadowColor: "rgba(0, 0, 0, 0.02)", + shadowOffset: { width: 0, height: 2 }, + shadowRadius: 8, + elevation: 2, + }, + md: { + shadowColor: "rgba(0, 0, 0, 0.04)", + shadowOffset: { width: 0, height: 4 }, + shadowRadius: 16, + elevation: 4, + }, + lg: { + shadowColor: "rgba(0, 0, 0, 0.08)", + shadowOffset: { width: 0, height: 8 }, + shadowRadius: 24, + elevation: 8, + }, + }, ...commonTheme, } as const;