Files
Shade/packages/shade-widgets/src/ShadeProvider.tsx

68 lines
2.0 KiB
TypeScript
Raw Normal View History

2026-04-10 19:00:21 +02:00
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<ShadeContextValue | null>(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
* <ShadeProvider observerUrl="https://x.com/shade-observer" token={process.env.TOKEN!}>
* <SessionList />
* <PrekeyStock />
* </ShadeProvider>
* ```
*/
export function ShadeProvider({
observerUrl,
token,
themeMode = 'dark',
pollIntervalMs = 5000,
children,
}: ShadeProviderProps): React.ReactElement {
const value = useMemo<ShadeContextValue>(
() => ({
observerUrl: observerUrl.replace(/\/$/, ''),
token,
theme: resolveTheme(themeMode),
pollIntervalMs,
}),
[observerUrl, token, themeMode, pollIntervalMs],
);
return <ShadeContext.Provider value={value}>{children}</ShadeContext.Provider>;
}
/** 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 <ShadeProvider observerUrl="..." token="..." />',
);
}
return ctx;
}