From 2ce759d8a0837b14ad8493ef966d337b85fa1971 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Thu, 27 Nov 2025 03:11:02 -0600 Subject: [PATCH] MAESTRO: Create ThemeProvider component for web interface - Add src/web/components/ThemeProvider.tsx with React context for theming - Provide useTheme and useThemeColors hooks for child components - Include default theme (Dracula) for initial render before WebSocket connection - Update tsconfig.json to include src/web and src/shared directories --- src/web/components/ThemeProvider.tsx | 141 +++++++++++++++++++++++++++ src/web/components/index.ts | 13 +++ src/web/index.ts | 9 ++ tsconfig.json | 2 +- 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/web/components/ThemeProvider.tsx create mode 100644 src/web/components/index.ts create mode 100644 src/web/index.ts diff --git a/src/web/components/ThemeProvider.tsx b/src/web/components/ThemeProvider.tsx new file mode 100644 index 00000000..be371992 --- /dev/null +++ b/src/web/components/ThemeProvider.tsx @@ -0,0 +1,141 @@ +/** + * ThemeProvider component for Maestro web interface + * + * Provides theme context to web components. Accepts theme via props + * (typically received from WebSocket connection to desktop app). + */ + +import React, { createContext, useContext, useMemo } from 'react'; +import type { Theme, ThemeColors } from '../../shared/theme-types'; + +/** + * Context value containing the current theme and utility functions + */ +interface ThemeContextValue { + /** Current theme object */ + theme: Theme; + /** Whether the theme is a light theme */ + isLight: boolean; + /** Whether the theme is a dark theme */ + isDark: boolean; + /** Whether the theme is a vibe theme */ + isVibe: boolean; +} + +/** + * Default theme used when no theme is provided + * Matches the Dracula theme from the desktop app + */ +const defaultTheme: Theme = { + id: 'dracula', + name: 'Dracula', + mode: 'dark', + colors: { + bgMain: '#0b0b0d', + bgSidebar: '#111113', + bgActivity: '#1c1c1f', + border: '#27272a', + textMain: '#e4e4e7', + textDim: '#a1a1aa', + accent: '#6366f1', + accentDim: 'rgba(99, 102, 241, 0.2)', + accentText: '#a5b4fc', + success: '#22c55e', + warning: '#eab308', + error: '#ef4444', + }, +}; + +const ThemeContext = createContext(null); + +export interface ThemeProviderProps { + /** Theme object to provide to children. If not provided, uses default theme. */ + theme?: Theme; + /** Children components that will have access to the theme */ + children: React.ReactNode; +} + +/** + * ThemeProvider component that provides theme context to the component tree + * + * @example + * ```tsx + * // With theme from WebSocket + * + * + * + * + * // Using the context in a child component + * const { theme, isDark } = useTheme(); + * ``` + */ +export function ThemeProvider({ theme = defaultTheme, children }: ThemeProviderProps) { + const contextValue = useMemo( + () => ({ + theme, + isLight: theme.mode === 'light', + isDark: theme.mode === 'dark', + isVibe: theme.mode === 'vibe', + }), + [theme] + ); + + return ( + + {children} + + ); +} + +/** + * Hook to access the current theme context + * + * @throws Error if used outside of a ThemeProvider + * + * @example + * ```tsx + * function MyComponent() { + * const { theme, isDark } = useTheme(); + * return ( + *
+ * {isDark ? 'Dark mode' : 'Light mode'} + *
+ * ); + * } + * ``` + */ +export function useTheme(): ThemeContextValue { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +} + +/** + * Hook to access just the theme colors for convenience + * + * @throws Error if used outside of a ThemeProvider + * + * @example + * ```tsx + * function Button() { + * const colors = useThemeColors(); + * return ( + * + * ); + * } + * ``` + */ +export function useThemeColors(): ThemeColors { + const { theme } = useTheme(); + return theme.colors; +} + +export { ThemeContext }; +export type { ThemeContextValue }; diff --git a/src/web/components/index.ts b/src/web/components/index.ts new file mode 100644 index 00000000..8b456f88 --- /dev/null +++ b/src/web/components/index.ts @@ -0,0 +1,13 @@ +/** + * Web interface components for Maestro + * + * Shared components used by both mobile and desktop web interfaces. + */ + +export { + ThemeProvider, + useTheme, + useThemeColors, + ThemeContext, +} from './ThemeProvider'; +export type { ThemeProviderProps, ThemeContextValue } from './ThemeProvider'; diff --git a/src/web/index.ts b/src/web/index.ts new file mode 100644 index 00000000..eb359b1b --- /dev/null +++ b/src/web/index.ts @@ -0,0 +1,9 @@ +/** + * Maestro Web Interface + * + * This module contains shared components, hooks, and utilities + * for the Maestro web interface (both mobile and desktop web). + */ + +// Components +export * from './components'; diff --git a/tsconfig.json b/tsconfig.json index 1f0339cf..45b151a5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,5 +15,5 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["src/renderer"] + "include": ["src/renderer", "src/web", "src/shared"] }