import React, { createContext, useContext, useMemo } from 'react'; import { resolveTheme, type ShadeTheme, type ThemeMode } from './theme.js'; export interface ShadeContextValue { observerUrl: string; token: string; theme: ShadeTheme; pollIntervalMs: number; } const ShadeContext = createContext(null); export interface ShadeProviderProps { /** Base URL of the Shade observer endpoint, e.g. "https://shade.example.com/shade-observer" */ observerUrl: string; /** Bearer token for the observer (matches SHADE_OBSERVER_TOKEN on the server) */ token: string; /** Theme mode: dark, light, or auto (matches system preference). Default: dark. */ themeMode?: ThemeMode; /** How often to poll /api/state in milliseconds. Default: 5000. */ pollIntervalMs?: number; children: React.ReactNode; } /** * ShadeProvider — root context provider that all Shade widgets need. * * Wrap your dashboard or specific widget container with this and pass * the observer URL + token. All child widgets will share the connection. * * ```tsx * * * * * ``` */ export function ShadeProvider({ observerUrl, token, themeMode = 'dark', pollIntervalMs = 5000, children, }: ShadeProviderProps): React.ReactElement { const value = useMemo( () => ({ observerUrl: observerUrl.replace(/\/$/, ''), token, theme: resolveTheme(themeMode), pollIntervalMs, }), [observerUrl, token, themeMode, pollIntervalMs], ); return {children}; } /** Internal hook used by widgets to access the context. Throws if no provider. */ export function useShadeContext(): ShadeContextValue { const ctx = useContext(ShadeContext); if (!ctx) { throw new Error( 'Shade widgets must be wrapped in ', ); } return ctx; }