@demokit-ai/react 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.ts","../src/provider.tsx","../src/hooks.ts","../src/banner.tsx"],"names":["createContext","useState","useRef","useEffect","createDemoInterceptor","useCallback","useMemo","jsx","useContext","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext } from 'react'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * React context for demo mode state\n * @internal\n */\nexport const DemoModeContext = createContext<DemoModeContextValue | undefined>(undefined)\n\nDemoModeContext.displayName = 'DemoModeContext'\n","'use client'\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react'\nimport { createDemoInterceptor, type DemoInterceptor, type SessionState } from '@demokit-ai/core'\nimport { DemoModeContext } from './context'\nimport type { DemoKitProviderProps, DemoModeContextValue } from './types'\n\n/**\n * Provider component that enables demo mode functionality\n *\n * Wraps your app to provide demo mode state and controls.\n * Handles SSR hydration safely and persists state to localStorage.\n *\n * @example\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * 'GET /api/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider\n * fixtures={fixtures}\n * onDemoModeChange={(enabled) => console.log('Demo mode:', enabled)}\n * >\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n */\nexport function DemoKitProvider({\n children,\n fixtures,\n storageKey = 'demokit-mode',\n initialEnabled = false,\n onDemoModeChange,\n baseUrl,\n}: DemoKitProviderProps) {\n // Start with initialEnabled for SSR to avoid hydration mismatch\n const [isDemoMode, setIsDemoMode] = useState(initialEnabled)\n const [isHydrated, setIsHydrated] = useState(false)\n\n // Keep a ref to the interceptor instance\n const interceptorRef = useRef<DemoInterceptor | null>(null)\n\n // Track if we've initialized\n const initializedRef = useRef(false)\n\n // Initialize interceptor on mount (client-side only)\n useEffect(() => {\n if (initializedRef.current) {\n return\n }\n initializedRef.current = true\n\n interceptorRef.current = createDemoInterceptor({\n fixtures,\n storageKey,\n initialEnabled,\n baseUrl,\n onEnable: () => {\n setIsDemoMode(true)\n onDemoModeChange?.(true)\n },\n onDisable: () => {\n setIsDemoMode(false)\n onDemoModeChange?.(false)\n },\n })\n\n // Sync state from storage after hydration\n const storedState = interceptorRef.current.isEnabled()\n setIsDemoMode(storedState)\n\n // Mark as hydrated\n setIsHydrated(true)\n\n return () => {\n interceptorRef.current?.destroy()\n interceptorRef.current = null\n initializedRef.current = false\n }\n }, []) // Empty deps - only run once on mount\n\n // Update fixtures if they change\n useEffect(() => {\n if (interceptorRef.current && isHydrated) {\n interceptorRef.current.setFixtures(fixtures)\n }\n }, [fixtures, isHydrated])\n\n const enable = useCallback(() => {\n interceptorRef.current?.enable()\n }, [])\n\n const disable = useCallback(() => {\n interceptorRef.current?.disable()\n }, [])\n\n const toggle = useCallback(() => {\n interceptorRef.current?.toggle()\n }, [])\n\n const setDemoMode = useCallback((enabled: boolean) => {\n if (enabled) {\n interceptorRef.current?.enable()\n } else {\n interceptorRef.current?.disable()\n }\n }, [])\n\n const resetSession = useCallback(() => {\n interceptorRef.current?.resetSession()\n }, [])\n\n const getSession = useCallback((): SessionState | null => {\n return interceptorRef.current?.getSession() ?? null\n }, [])\n\n const value = useMemo<DemoModeContextValue>(\n () => ({\n isDemoMode,\n isHydrated,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n }),\n [isDemoMode, isHydrated, enable, disable, toggle, setDemoMode, resetSession, getSession]\n )\n\n return (\n <DemoModeContext.Provider value={value}>\n {children}\n </DemoModeContext.Provider>\n )\n}\n","'use client'\n\nimport { useContext } from 'react'\nimport { DemoModeContext } from './context'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * Hook to access demo mode state and controls\n *\n * @returns Demo mode context value with state and control methods\n * @throws Error if used outside of DemoKitProvider\n *\n * @example\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n *\n * // Wait for hydration before rendering demo-dependent UI\n * if (!isHydrated) {\n * return <Loading />\n * }\n *\n * return (\n * <div>\n * <p>Demo mode: {isDemoMode ? 'ON' : 'OFF'}</p>\n * <button onClick={toggle}>Toggle</button>\n * </div>\n * )\n * }\n */\nexport function useDemoMode(): DemoModeContextValue {\n const context = useContext(DemoModeContext)\n\n if (context === undefined) {\n throw new Error(\n 'useDemoMode must be used within a DemoKitProvider. ' +\n 'Make sure to wrap your app with <DemoKitProvider>.'\n )\n }\n\n return context\n}\n\n/**\n * Hook to check if demo mode is enabled\n * Shorthand for useDemoMode().isDemoMode\n *\n * @returns Whether demo mode is enabled\n */\nexport function useIsDemoMode(): boolean {\n return useDemoMode().isDemoMode\n}\n\n/**\n * Hook to check if the component has hydrated\n * Shorthand for useDemoMode().isHydrated\n *\n * @returns Whether the component has hydrated\n */\nexport function useIsHydrated(): boolean {\n return useDemoMode().isHydrated\n}\n\n/**\n * Hook to access the session state\n * Shorthand for useDemoMode().getSession()\n *\n * @returns The session state, or null if not yet initialized\n *\n * @example\n * function MyComponent() {\n * const session = useDemoSession()\n *\n * const cart = session?.get<CartItem[]>('cart') || []\n * const addToCart = (item: CartItem) => {\n * session?.set('cart', [...cart, item])\n * }\n *\n * return <CartView items={cart} onAdd={addToCart} />\n * }\n */\nexport function useDemoSession() {\n return useDemoMode().getSession()\n}\n","'use client'\n\nimport { useDemoMode } from './hooks'\nimport type { DemoModeBannerProps } from './types'\n\n/**\n * Eye icon SVG component\n */\nfunction EyeIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\n/**\n * Default styles for the banner\n */\nconst defaultStyles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#fef3c7',\n borderBottom: '1px solid rgba(217, 119, 6, 0.2)',\n fontSize: '14px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n } as React.CSSProperties,\n content: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n } as React.CSSProperties,\n icon: {\n color: '#d97706',\n flexShrink: 0,\n } as React.CSSProperties,\n label: {\n fontWeight: 600,\n color: '#78350f',\n } as React.CSSProperties,\n description: {\n color: 'rgba(120, 53, 15, 0.7)',\n fontSize: '12px',\n } as React.CSSProperties,\n button: {\n padding: '4px 12px',\n fontSize: '14px',\n backgroundColor: 'transparent',\n border: '1px solid rgba(217, 119, 6, 0.3)',\n borderRadius: '4px',\n cursor: 'pointer',\n color: '#78350f',\n fontFamily: 'inherit',\n transition: 'background-color 0.15s ease',\n } as React.CSSProperties,\n}\n\n/**\n * A ready-to-use banner component that shows when demo mode is active\n *\n * Displays a prominent amber banner with a label, description, and exit button.\n * Automatically hides when demo mode is disabled or before hydration.\n *\n * @example\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * @example Custom labels\n * <DemoModeBanner\n * demoLabel=\"Preview Mode\"\n * description=\"You're viewing sample data\"\n * exitLabel=\"Exit Preview\"\n * />\n */\nexport function DemoModeBanner({\n className = '',\n exitLabel = 'Exit Demo Mode',\n demoLabel = 'Demo Mode Active',\n description = 'Changes are simulated and not saved',\n showIcon = true,\n style,\n onExit,\n}: DemoModeBannerProps) {\n const { isDemoMode, isHydrated, disable } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated || !isDemoMode) {\n return null\n }\n\n const handleExit = () => {\n if (onExit) {\n onExit()\n } else {\n disable()\n }\n }\n\n return (\n <div\n className={`demokit-banner ${className}`.trim()}\n style={{ ...defaultStyles.container, ...style }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div style={defaultStyles.content}>\n {showIcon && (\n <span style={defaultStyles.icon}>\n <EyeIcon />\n </span>\n )}\n <span style={defaultStyles.label}>{demoLabel}</span>\n {description && <span style={defaultStyles.description}>{description}</span>}\n </div>\n <button\n onClick={handleExit}\n style={defaultStyles.button}\n onMouseOver={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(217, 119, 6, 0.1)'\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n type=\"button\"\n >\n {exitLabel}\n </button>\n </div>\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/context.ts","../src/hooks.ts","../src/powered-by.tsx","../src/banner.tsx","../src/toggle.tsx"],"sourcesContent":["/**\n * @demokit-ai/react\n *\n * React bindings for DemoKit - provider, hooks, and components.\n *\n * @example\n * import { DemoKitProvider, useDemoMode, DemoModeBanner } from '@demokit-ai/react'\n *\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * // In any component\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n * // ...\n * }\n *\n * @packageDocumentation\n */\n\n// Provider\nexport { DemoKitProvider } from './provider'\n\n// Hooks\nexport { useDemoMode, useIsDemoMode, useIsHydrated, useDemoSession } from './hooks'\n\n// Components\nexport { DemoModeBanner } from './banner'\nexport { DemoModeToggle } from './toggle'\nexport { PoweredByBadge } from './powered-by'\n\n// Context (for advanced use cases)\nexport { DemoModeContext } from './context'\n\n// Types\nexport type {\n DemoKitProviderProps,\n DemoModeContextValue,\n DemoModeBannerProps,\n} from './types'\n\n// Component types (from component files)\nexport type { DemoModeToggleProps } from './toggle'\nexport type { PoweredByBadgeProps } from './powered-by'\n\n// Re-export core types for convenience\nexport type {\n FixtureMap,\n FixtureHandler,\n RequestContext,\n SessionState,\n} from '@demokit-ai/core'\n","'use client'\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react'\nimport {\n createDemoInterceptor,\n fetchCloudFixtures,\n createRemoteFixtures,\n type DemoInterceptor,\n type SessionState,\n type FixtureMap,\n} from '@demokit-ai/core'\nimport { DemoModeContext } from './context'\nimport type { DemoKitProviderProps, DemoModeContextValue } from './types'\n\n/**\n * Provider component that enables demo mode functionality\n *\n * Wraps your app to provide demo mode state and controls.\n * Handles SSR hydration safely and persists state to localStorage.\n *\n * Supports two modes:\n * 1. **Local mode**: Pass `fixtures` prop with pattern handlers\n * 2. **Remote mode**: Pass `apiKey` to fetch from DemoKit Cloud\n *\n * @example Local mode\n * ```tsx\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * 'GET /api/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n * ```\n *\n * @example Remote mode (zero-config)\n * ```tsx\n * function App() {\n * return (\n * <DemoKitProvider\n * apiKey=\"dk_live_xxx\"\n * loadingFallback={<LoadingSpinner />}\n * >\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n * ```\n */\nexport function DemoKitProvider({\n children,\n fixtures,\n // Remote config\n apiKey,\n cloudUrl,\n timeout,\n retry,\n maxRetries,\n onRemoteLoad,\n onRemoteError,\n loadingFallback = null,\n errorFallback,\n // Standard props\n storageKey = 'demokit-mode',\n initialEnabled = false,\n onDemoModeChange,\n baseUrl,\n}: DemoKitProviderProps) {\n // Start with initialEnabled for SSR to avoid hydration mismatch\n const [isDemoMode, setIsDemoMode] = useState(initialEnabled)\n const [isHydrated, setIsHydrated] = useState(false)\n\n // Remote loading state\n const [isLoading, setIsLoading] = useState(!!apiKey)\n const [remoteError, setRemoteError] = useState<Error | null>(null)\n const [remoteVersion, setRemoteVersion] = useState<string | null>(null)\n\n // Keep a ref to the interceptor instance\n const interceptorRef = useRef<DemoInterceptor | null>(null)\n\n // Track if we've initialized\n const initializedRef = useRef(false)\n\n // Store loaded remote fixtures for refetch merging\n const remoteFixturesRef = useRef<FixtureMap | null>(null)\n\n // Store the refetch function for context\n const refetchFnRef = useRef<(() => Promise<void>) | null>(null)\n\n /**\n * Create and configure the demo interceptor\n */\n const setupInterceptor = useCallback(\n (mergedFixtures: FixtureMap) => {\n interceptorRef.current?.destroy()\n\n interceptorRef.current = createDemoInterceptor({\n fixtures: mergedFixtures,\n storageKey,\n initialEnabled,\n baseUrl,\n onEnable: () => {\n setIsDemoMode(true)\n onDemoModeChange?.(true)\n },\n onDisable: () => {\n setIsDemoMode(false)\n onDemoModeChange?.(false)\n },\n })\n\n // Sync state from storage after hydration\n const storedState = interceptorRef.current.isEnabled()\n setIsDemoMode(storedState)\n setIsHydrated(true)\n },\n [storageKey, initialEnabled, baseUrl, onDemoModeChange]\n )\n\n /**\n * Fetch fixtures from DemoKit Cloud and set up interceptor\n */\n const fetchAndSetup = useCallback(async () => {\n if (!apiKey) return\n\n setIsLoading(true)\n setRemoteError(null)\n\n try {\n const response = await fetchCloudFixtures({\n apiKey,\n cloudUrl,\n timeout,\n retry,\n maxRetries,\n onLoad: onRemoteLoad,\n onError: onRemoteError,\n })\n\n // Build fixtures from remote response with local overrides\n const remoteFixtures = createRemoteFixtures(response, fixtures)\n remoteFixturesRef.current = remoteFixtures\n\n setRemoteVersion(response.version)\n setupInterceptor(remoteFixtures)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n setRemoteError(err)\n onRemoteError?.(err)\n\n // If we have local fixtures, still set up with those\n if (fixtures && Object.keys(fixtures).length > 0) {\n setupInterceptor(fixtures)\n } else {\n setIsHydrated(true)\n }\n } finally {\n setIsLoading(false)\n }\n }, [\n apiKey,\n cloudUrl,\n timeout,\n retry,\n maxRetries,\n fixtures,\n onRemoteLoad,\n onRemoteError,\n setupInterceptor,\n ])\n\n // Store refetch function in ref for context access\n refetchFnRef.current = fetchAndSetup\n\n // Initialize on mount\n useEffect(() => {\n if (initializedRef.current) {\n return\n }\n initializedRef.current = true\n\n if (apiKey) {\n // Remote mode: fetch from cloud\n fetchAndSetup()\n } else if (fixtures) {\n // Local mode: use provided fixtures\n setupInterceptor(fixtures)\n } else {\n // No fixtures at all - just mark as hydrated\n setIsHydrated(true)\n setIsLoading(false)\n }\n\n return () => {\n interceptorRef.current?.destroy()\n interceptorRef.current = null\n initializedRef.current = false\n }\n }, []) // Empty deps - only run once on mount\n\n // Update fixtures if they change (local mode or overrides)\n useEffect(() => {\n if (!isHydrated || isLoading) return\n\n if (apiKey && remoteFixturesRef.current) {\n // Remote mode: merge new local overrides with cached remote fixtures\n const merged = { ...remoteFixturesRef.current, ...fixtures }\n interceptorRef.current?.setFixtures(merged)\n } else if (fixtures) {\n // Local mode: update fixtures\n interceptorRef.current?.setFixtures(fixtures)\n }\n }, [fixtures, isHydrated, isLoading, apiKey])\n\n const enable = useCallback(() => {\n interceptorRef.current?.enable()\n }, [])\n\n const disable = useCallback(() => {\n interceptorRef.current?.disable()\n }, [])\n\n const toggle = useCallback(() => {\n interceptorRef.current?.toggle()\n }, [])\n\n const setDemoMode = useCallback((enabled: boolean) => {\n if (enabled) {\n interceptorRef.current?.enable()\n } else {\n interceptorRef.current?.disable()\n }\n }, [])\n\n const resetSession = useCallback(() => {\n interceptorRef.current?.resetSession()\n }, [])\n\n const getSession = useCallback((): SessionState | null => {\n return interceptorRef.current?.getSession() ?? null\n }, [])\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!apiKey) {\n console.warn('[DemoKit] refetch() called but no apiKey provided')\n return\n }\n await refetchFnRef.current?.()\n }, [apiKey])\n\n const value = useMemo<DemoModeContextValue>(\n () => ({\n isDemoMode,\n isHydrated,\n isLoading,\n remoteError,\n remoteVersion,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n refetch,\n }),\n [\n isDemoMode,\n isHydrated,\n isLoading,\n remoteError,\n remoteVersion,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n refetch,\n ]\n )\n\n // Render loading state\n if (isLoading && apiKey) {\n return (\n <DemoModeContext.Provider value={value}>\n {loadingFallback}\n </DemoModeContext.Provider>\n )\n }\n\n // Render error state\n if (remoteError && errorFallback) {\n const errorContent =\n typeof errorFallback === 'function'\n ? errorFallback(remoteError)\n : errorFallback\n\n return (\n <DemoModeContext.Provider value={value}>\n {errorContent}\n </DemoModeContext.Provider>\n )\n }\n\n return (\n <DemoModeContext.Provider value={value}>{children}</DemoModeContext.Provider>\n )\n}\n","import { createContext } from 'react'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * React context for demo mode state\n * @internal\n */\nexport const DemoModeContext = createContext<DemoModeContextValue | undefined>(undefined)\n\nDemoModeContext.displayName = 'DemoModeContext'\n","'use client'\n\nimport { useContext } from 'react'\nimport { DemoModeContext } from './context'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * Hook to access demo mode state and controls\n *\n * @returns Demo mode context value with state and control methods\n * @throws Error if used outside of DemoKitProvider\n *\n * @example\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n *\n * // Wait for hydration before rendering demo-dependent UI\n * if (!isHydrated) {\n * return <Loading />\n * }\n *\n * return (\n * <div>\n * <p>Demo mode: {isDemoMode ? 'ON' : 'OFF'}</p>\n * <button onClick={toggle}>Toggle</button>\n * </div>\n * )\n * }\n */\nexport function useDemoMode(): DemoModeContextValue {\n const context = useContext(DemoModeContext)\n\n if (context === undefined) {\n throw new Error(\n 'useDemoMode must be used within a DemoKitProvider. ' +\n 'Make sure to wrap your app with <DemoKitProvider>.'\n )\n }\n\n return context\n}\n\n/**\n * Hook to check if demo mode is enabled\n * Shorthand for useDemoMode().isDemoMode\n *\n * @returns Whether demo mode is enabled\n */\nexport function useIsDemoMode(): boolean {\n return useDemoMode().isDemoMode\n}\n\n/**\n * Hook to check if the component has hydrated\n * Shorthand for useDemoMode().isHydrated\n *\n * @returns Whether the component has hydrated\n */\nexport function useIsHydrated(): boolean {\n return useDemoMode().isHydrated\n}\n\n/**\n * Hook to access the session state\n * Shorthand for useDemoMode().getSession()\n *\n * @returns The session state, or null if not yet initialized\n *\n * @example\n * function MyComponent() {\n * const session = useDemoSession()\n *\n * const cart = session?.get<CartItem[]>('cart') || []\n * const addToCart = (item: CartItem) => {\n * session?.set('cart', [...cart, item])\n * }\n *\n * return <CartView items={cart} onAdd={addToCart} />\n * }\n */\nexport function useDemoSession() {\n return useDemoMode().getSession()\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\n\n/**\n * Props for the PoweredByBadge component\n */\nexport interface PoweredByBadgeProps {\n /**\n * URL to link to when clicked\n * @default 'https://demokit.ai'\n */\n url?: string\n\n /**\n * Visual variant for light/dark backgrounds\n * @default 'auto'\n */\n variant?: 'light' | 'dark' | 'auto'\n\n /**\n * Size of the badge\n * @default 'sm'\n */\n size?: 'xs' | 'sm' | 'md'\n\n /**\n * Additional CSS class name\n */\n className?: string\n\n /**\n * Custom styles\n */\n style?: CSSProperties\n}\n\n/**\n * External link icon\n */\nfunction ExternalLinkIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n <polyline points=\"15 3 21 3 21 9\" />\n <line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\" />\n </svg>\n )\n}\n\n/**\n * Size configurations\n */\nconst sizeConfig = {\n xs: {\n fontSize: '10px',\n padding: '2px 6px',\n gap: '3px',\n iconSize: 8,\n },\n sm: {\n fontSize: '11px',\n padding: '3px 8px',\n gap: '4px',\n iconSize: 10,\n },\n md: {\n fontSize: '12px',\n padding: '4px 10px',\n gap: '5px',\n iconSize: 12,\n },\n}\n\n/**\n * Variant configurations\n */\nconst variantStyles = {\n light: {\n color: 'rgba(120, 53, 15, 0.7)',\n hoverColor: 'rgba(120, 53, 15, 0.9)',\n backgroundColor: 'transparent',\n hoverBackgroundColor: 'rgba(217, 119, 6, 0.08)',\n },\n dark: {\n color: 'rgba(255, 255, 255, 0.6)',\n hoverColor: 'rgba(255, 255, 255, 0.9)',\n backgroundColor: 'transparent',\n hoverBackgroundColor: 'rgba(255, 255, 255, 0.1)',\n },\n}\n\n/**\n * A \"Powered by DemoKit\" badge that links to demokit.ai\n *\n * This badge is shown by default for OSS users and cannot be hidden\n * without a valid DemoKit Cloud paid plan.\n *\n * @example\n * <PoweredByBadge />\n *\n * @example With dark theme\n * <PoweredByBadge variant=\"dark\" />\n *\n * @example Small size\n * <PoweredByBadge size=\"xs\" />\n */\nexport function PoweredByBadge({\n url = 'https://demokit.ai',\n variant = 'light',\n size = 'sm',\n className = '',\n style,\n}: PoweredByBadgeProps) {\n const config = sizeConfig[size]\n const colors = variantStyles[variant === 'auto' ? 'light' : variant]\n\n const baseStyles: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: config.gap,\n fontSize: config.fontSize,\n padding: config.padding,\n color: colors.color,\n textDecoration: 'none',\n borderRadius: '4px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n fontWeight: 500,\n transition: 'color 0.15s ease, background-color 0.15s ease',\n whiteSpace: 'nowrap',\n ...style,\n }\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={`demokit-powered-by ${className}`.trim()}\n style={baseStyles}\n onMouseOver={(e) => {\n e.currentTarget.style.color = colors.hoverColor\n e.currentTarget.style.backgroundColor = colors.hoverBackgroundColor\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.color = colors.color\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n >\n <span>Powered by DemoKit</span>\n <ExternalLinkIcon size={config.iconSize} />\n </a>\n )\n}\n","'use client'\n\nimport { useDemoMode } from './hooks'\nimport { PoweredByBadge } from './powered-by'\nimport type { DemoModeBannerProps } from './types'\n\n/**\n * Eye icon SVG component\n */\nfunction EyeIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\n/**\n * Default styles for the banner\n */\nconst defaultStyles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#fef3c7',\n borderBottom: '1px solid rgba(217, 119, 6, 0.2)',\n fontSize: '14px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n } as React.CSSProperties,\n content: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n } as React.CSSProperties,\n icon: {\n color: '#d97706',\n flexShrink: 0,\n } as React.CSSProperties,\n label: {\n fontWeight: 600,\n color: '#78350f',\n } as React.CSSProperties,\n description: {\n color: 'rgba(120, 53, 15, 0.7)',\n fontSize: '12px',\n } as React.CSSProperties,\n button: {\n padding: '4px 12px',\n fontSize: '14px',\n backgroundColor: 'transparent',\n border: '1px solid rgba(217, 119, 6, 0.3)',\n borderRadius: '4px',\n cursor: 'pointer',\n color: '#78350f',\n fontFamily: 'inherit',\n transition: 'background-color 0.15s ease',\n } as React.CSSProperties,\n}\n\n/**\n * A ready-to-use banner component that shows when demo mode is active\n *\n * Displays a prominent amber banner with a label, description, and exit button.\n * Automatically hides when demo mode is disabled or before hydration.\n *\n * @example\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * @example Custom labels\n * <DemoModeBanner\n * demoLabel=\"Preview Mode\"\n * description=\"You're viewing sample data\"\n * exitLabel=\"Exit Preview\"\n * />\n */\nexport function DemoModeBanner({\n className = '',\n exitLabel = 'Exit Demo Mode',\n demoLabel = 'Demo Mode Active',\n description = 'Changes are simulated and not saved',\n showIcon = true,\n showPoweredBy = true,\n poweredByUrl = 'https://demokit.ai',\n style,\n onExit,\n}: DemoModeBannerProps) {\n const { isDemoMode, isHydrated, disable } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated || !isDemoMode) {\n return null\n }\n\n const handleExit = () => {\n if (onExit) {\n onExit()\n } else {\n disable()\n }\n }\n\n // For OSS users, branding is always shown (enforced server-side in cloud)\n // The prop is only respected for paid cloud users\n const effectiveShowPoweredBy = showPoweredBy\n\n return (\n <div\n className={`demokit-banner ${className}`.trim()}\n style={{ ...defaultStyles.container, flexDirection: 'column', gap: '4px', ...style }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>\n <div style={defaultStyles.content}>\n {showIcon && (\n <span style={defaultStyles.icon}>\n <EyeIcon />\n </span>\n )}\n <span style={defaultStyles.label}>{demoLabel}</span>\n {description && <span style={defaultStyles.description}>{description}</span>}\n </div>\n <button\n onClick={handleExit}\n style={defaultStyles.button}\n onMouseOver={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(217, 119, 6, 0.1)'\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n type=\"button\"\n >\n {exitLabel}\n </button>\n </div>\n {effectiveShowPoweredBy && (\n <div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>\n <PoweredByBadge url={poweredByUrl} size=\"xs\" />\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\nimport { useDemoMode } from './hooks'\nimport { PoweredByBadge } from './powered-by'\n\n/**\n * Props for the DemoModeToggle component\n */\nexport interface DemoModeToggleProps {\n /**\n * Show \"Demo Mode\" label next to the toggle\n * @default true\n */\n showLabel?: boolean\n\n /**\n * Label text to display\n * @default 'Demo Mode'\n */\n label?: string\n\n /**\n * Show \"Powered by DemoKit\" branding\n * Note: For OSS users, this is always true regardless of the prop value.\n * Only paid DemoKit Cloud users can hide the branding.\n * @default true\n */\n showPoweredBy?: boolean\n\n /**\n * URL for the \"Powered by\" link\n * @default 'https://demokit.ai'\n */\n poweredByUrl?: string\n\n /**\n * Position of the toggle\n * - 'inline': Renders where placed in the component tree\n * - 'floating': Fixed position, can be moved around\n * - 'corner': Fixed to bottom-right corner\n * @default 'inline'\n */\n position?: 'inline' | 'floating' | 'corner'\n\n /**\n * Size of the toggle\n * @default 'md'\n */\n size?: 'sm' | 'md' | 'lg'\n\n /**\n * Additional CSS class name\n */\n className?: string\n\n /**\n * Custom styles\n */\n style?: CSSProperties\n\n /**\n * Callback when toggle state changes\n */\n onChange?: (enabled: boolean) => void\n}\n\n/**\n * Size configurations for the toggle\n */\nconst sizeConfig = {\n sm: {\n trackWidth: 36,\n trackHeight: 20,\n thumbSize: 16,\n thumbOffset: 2,\n fontSize: '12px',\n padding: '8px 12px',\n gap: '8px',\n },\n md: {\n trackWidth: 44,\n trackHeight: 24,\n thumbSize: 20,\n thumbOffset: 2,\n fontSize: '14px',\n padding: '12px 16px',\n gap: '10px',\n },\n lg: {\n trackWidth: 52,\n trackHeight: 28,\n thumbSize: 24,\n thumbOffset: 2,\n fontSize: '16px',\n padding: '16px 20px',\n gap: '12px',\n },\n}\n\n/**\n * Position configurations\n */\nconst positionStyles: Record<string, CSSProperties> = {\n inline: {},\n floating: {\n position: 'fixed',\n zIndex: 9999,\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n },\n corner: {\n position: 'fixed',\n bottom: '20px',\n right: '20px',\n zIndex: 9999,\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n },\n}\n\n/**\n * Default styles\n */\nconst defaultStyles = {\n container: {\n display: 'inline-flex',\n flexDirection: 'column' as const,\n alignItems: 'flex-start',\n gap: '4px',\n backgroundColor: '#ffffff',\n border: '1px solid rgba(0, 0, 0, 0.1)',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n },\n row: {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n },\n label: {\n fontWeight: 500,\n color: '#1f2937',\n userSelect: 'none' as const,\n },\n track: {\n position: 'relative' as const,\n borderRadius: '9999px',\n cursor: 'pointer',\n transition: 'background-color 0.2s ease',\n flexShrink: 0,\n },\n trackOff: {\n backgroundColor: '#d1d5db',\n },\n trackOn: {\n backgroundColor: '#d97706',\n },\n thumb: {\n position: 'absolute' as const,\n top: '50%',\n transform: 'translateY(-50%)',\n backgroundColor: '#ffffff',\n borderRadius: '50%',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n transition: 'left 0.2s ease',\n },\n poweredByContainer: {\n display: 'flex',\n justifyContent: 'flex-end',\n width: '100%',\n },\n}\n\n/**\n * A toggle switch component for enabling/disabling demo mode\n *\n * Supports inline placement or fixed positioning (floating/corner).\n * Includes optional \"Powered by DemoKit\" branding that is always shown for OSS users.\n *\n * @example Basic inline usage\n * <DemoModeToggle />\n *\n * @example Corner position (floating)\n * <DemoModeToggle position=\"corner\" />\n *\n * @example Without label\n * <DemoModeToggle showLabel={false} />\n *\n * @example With custom label and callback\n * <DemoModeToggle\n * label=\"Preview Mode\"\n * onChange={(enabled) => console.log('Demo mode:', enabled)}\n * />\n */\nexport function DemoModeToggle({\n showLabel = true,\n label = 'Demo Mode',\n showPoweredBy = true,\n poweredByUrl = 'https://demokit.ai',\n position = 'inline',\n size = 'md',\n className = '',\n style,\n onChange,\n}: DemoModeToggleProps) {\n const { isDemoMode, isHydrated, toggle } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated) {\n return null\n }\n\n const config = sizeConfig[size]\n const posStyle = positionStyles[position]\n\n const handleToggle = () => {\n toggle()\n onChange?.(!isDemoMode)\n }\n\n const thumbLeft = isDemoMode\n ? config.trackWidth - config.thumbSize - config.thumbOffset\n : config.thumbOffset\n\n // For OSS users, branding is always shown (enforced server-side in cloud)\n // The prop is only respected for paid cloud users\n const effectiveShowPoweredBy = showPoweredBy\n\n return (\n <div\n className={`demokit-toggle ${className}`.trim()}\n style={{\n ...defaultStyles.container,\n padding: config.padding,\n ...posStyle,\n ...style,\n }}\n role=\"group\"\n aria-label=\"Demo mode toggle\"\n >\n <div style={{ ...defaultStyles.row, gap: config.gap }}>\n {showLabel && (\n <span style={{ ...defaultStyles.label, fontSize: config.fontSize }}>{label}</span>\n )}\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isDemoMode}\n aria-label={`${label}: ${isDemoMode ? 'On' : 'Off'}`}\n onClick={handleToggle}\n style={{\n ...defaultStyles.track,\n ...(isDemoMode ? defaultStyles.trackOn : defaultStyles.trackOff),\n width: config.trackWidth,\n height: config.trackHeight,\n border: 'none',\n padding: 0,\n }}\n >\n <span\n style={{\n ...defaultStyles.thumb,\n width: config.thumbSize,\n height: config.thumbSize,\n left: thumbLeft,\n }}\n />\n </button>\n </div>\n {effectiveShowPoweredBy && (\n <div style={defaultStyles.poweredByContainer}>\n <PoweredByBadge url={poweredByUrl} size=\"xs\" />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","sizeConfig","defaultStyles"]}
package/dist/index.d.cts CHANGED
@@ -1,12 +1,40 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as _demokit_ai_core from '@demokit-ai/core';
3
- import { FixtureMap, SessionState } from '@demokit-ai/core';
3
+ import { FixtureMap, CloudFixtureResponse, SessionState } from '@demokit-ai/core';
4
4
  export { FixtureHandler, FixtureMap, RequestContext, SessionState } from '@demokit-ai/core';
5
5
  import * as react from 'react';
6
- import { ReactNode } from 'react';
6
+ import { ReactNode, CSSProperties } from 'react';
7
7
 
8
8
  /**
9
9
  * Props for the DemoKitProvider component
10
+ *
11
+ * The provider supports two modes:
12
+ * 1. **Local mode**: Provide `fixtures` prop with pattern handlers
13
+ * 2. **Remote mode**: Provide `apiKey` to fetch from DemoKit Cloud
14
+ *
15
+ * @example Local mode
16
+ * ```tsx
17
+ * <DemoKitProvider fixtures={{ 'GET /api/users': () => [] }}>
18
+ * <App />
19
+ * </DemoKitProvider>
20
+ * ```
21
+ *
22
+ * @example Remote mode (zero-config)
23
+ * ```tsx
24
+ * <DemoKitProvider apiKey="dk_live_xxx">
25
+ * <App />
26
+ * </DemoKitProvider>
27
+ * ```
28
+ *
29
+ * @example Remote mode with local overrides
30
+ * ```tsx
31
+ * <DemoKitProvider
32
+ * apiKey="dk_live_xxx"
33
+ * fixtures={{ 'POST /api/users': ({ body }) => ({ id: 'custom', ...body }) }}
34
+ * >
35
+ * <App />
36
+ * </DemoKitProvider>
37
+ * ```
10
38
  */
11
39
  interface DemoKitProviderProps {
12
40
  /**
@@ -14,9 +42,56 @@ interface DemoKitProviderProps {
14
42
  */
15
43
  children: ReactNode;
16
44
  /**
17
- * Map of URL patterns to fixture handlers
45
+ * Map of URL patterns to fixture handlers (local mode)
46
+ * In remote mode, these act as overrides for cloud fixtures
47
+ */
48
+ fixtures?: FixtureMap;
49
+ /**
50
+ * DemoKit Cloud API key for remote mode
51
+ * Format: dk_live_xxxx
52
+ *
53
+ * When provided, fixtures are fetched from DemoKit Cloud.
54
+ * Any `fixtures` prop values will override the cloud fixtures.
55
+ */
56
+ apiKey?: string;
57
+ /**
58
+ * DemoKit Cloud API URL
59
+ * @default 'https://api.demokit.cloud'
60
+ */
61
+ cloudUrl?: string;
62
+ /**
63
+ * Timeout for cloud API requests in milliseconds
64
+ * @default 10000
65
+ */
66
+ timeout?: number;
67
+ /**
68
+ * Whether to retry on fetch failure
69
+ * @default true
70
+ */
71
+ retry?: boolean;
72
+ /**
73
+ * Maximum number of retries for cloud fetch
74
+ * @default 3
75
+ */
76
+ maxRetries?: number;
77
+ /**
78
+ * Callback when remote fixtures are successfully loaded
18
79
  */
19
- fixtures: FixtureMap;
80
+ onRemoteLoad?: (response: CloudFixtureResponse) => void;
81
+ /**
82
+ * Callback when remote fetch fails
83
+ */
84
+ onRemoteError?: (error: Error) => void;
85
+ /**
86
+ * Content to render while loading remote fixtures
87
+ * @default null (renders nothing while loading)
88
+ */
89
+ loadingFallback?: ReactNode;
90
+ /**
91
+ * Content to render when remote fetch fails
92
+ * If not provided, children are rendered (with local fixtures only if provided)
93
+ */
94
+ errorFallback?: ReactNode | ((error: Error) => ReactNode);
20
95
  /**
21
96
  * localStorage key for persisting demo mode state
22
97
  * @default 'demokit-mode'
@@ -51,6 +126,21 @@ interface DemoModeContextValue {
51
126
  * Always check this before rendering demo-dependent UI
52
127
  */
53
128
  isHydrated: boolean;
129
+ /**
130
+ * Whether remote fixtures are currently being loaded
131
+ * Only relevant when apiKey is provided
132
+ */
133
+ isLoading: boolean;
134
+ /**
135
+ * Error that occurred during remote fetch
136
+ * Only set when apiKey is provided and fetch fails
137
+ */
138
+ remoteError: Error | null;
139
+ /**
140
+ * Version identifier from the loaded cloud fixtures
141
+ * Useful for cache invalidation and debugging
142
+ */
143
+ remoteVersion: string | null;
54
144
  /**
55
145
  * Enable demo mode
56
146
  */
@@ -78,6 +168,12 @@ interface DemoModeContextValue {
78
168
  * Returns null if the interceptor hasn't been initialized yet
79
169
  */
80
170
  getSession(): SessionState | null;
171
+ /**
172
+ * Refetch fixtures from DemoKit Cloud
173
+ * Only works when apiKey is provided
174
+ * Returns a promise that resolves when the fetch completes
175
+ */
176
+ refetch(): Promise<void>;
81
177
  }
82
178
  /**
83
179
  * Props for the DemoModeBanner component
@@ -107,6 +203,18 @@ interface DemoModeBannerProps {
107
203
  * @default true
108
204
  */
109
205
  showIcon?: boolean;
206
+ /**
207
+ * Show "Powered by DemoKit" branding
208
+ * Note: For OSS users, this is always true regardless of the prop value.
209
+ * Only paid DemoKit Cloud users can hide the branding.
210
+ * @default true
211
+ */
212
+ showPoweredBy?: boolean;
213
+ /**
214
+ * URL for the "Powered by" link
215
+ * @default 'https://demokit.ai'
216
+ */
217
+ poweredByUrl?: string;
110
218
  /**
111
219
  * Custom styles for the banner container
112
220
  */
@@ -124,7 +232,12 @@ interface DemoModeBannerProps {
124
232
  * Wraps your app to provide demo mode state and controls.
125
233
  * Handles SSR hydration safely and persists state to localStorage.
126
234
  *
127
- * @example
235
+ * Supports two modes:
236
+ * 1. **Local mode**: Pass `fixtures` prop with pattern handlers
237
+ * 2. **Remote mode**: Pass `apiKey` to fetch from DemoKit Cloud
238
+ *
239
+ * @example Local mode
240
+ * ```tsx
128
241
  * const fixtures = {
129
242
  * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],
130
243
  * 'GET /api/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),
@@ -132,16 +245,28 @@ interface DemoModeBannerProps {
132
245
  *
133
246
  * function App() {
134
247
  * return (
248
+ * <DemoKitProvider fixtures={fixtures}>
249
+ * <YourApp />
250
+ * </DemoKitProvider>
251
+ * )
252
+ * }
253
+ * ```
254
+ *
255
+ * @example Remote mode (zero-config)
256
+ * ```tsx
257
+ * function App() {
258
+ * return (
135
259
  * <DemoKitProvider
136
- * fixtures={fixtures}
137
- * onDemoModeChange={(enabled) => console.log('Demo mode:', enabled)}
260
+ * apiKey="dk_live_xxx"
261
+ * loadingFallback={<LoadingSpinner />}
138
262
  * >
139
263
  * <YourApp />
140
264
  * </DemoKitProvider>
141
265
  * )
142
266
  * }
267
+ * ```
143
268
  */
144
- declare function DemoKitProvider({ children, fixtures, storageKey, initialEnabled, onDemoModeChange, baseUrl, }: DemoKitProviderProps): react_jsx_runtime.JSX.Element;
269
+ declare function DemoKitProvider({ children, fixtures, apiKey, cloudUrl, timeout, retry, maxRetries, onRemoteLoad, onRemoteError, loadingFallback, errorFallback, storageKey, initialEnabled, onDemoModeChange, baseUrl, }: DemoKitProviderProps): react_jsx_runtime.JSX.Element;
145
270
 
146
271
  /**
147
272
  * Hook to access demo mode state and controls
@@ -224,7 +349,127 @@ declare function useDemoSession(): _demokit_ai_core.SessionState | null;
224
349
  * exitLabel="Exit Preview"
225
350
  * />
226
351
  */
227
- declare function DemoModeBanner({ className, exitLabel, demoLabel, description, showIcon, style, onExit, }: DemoModeBannerProps): react_jsx_runtime.JSX.Element | null;
352
+ declare function DemoModeBanner({ className, exitLabel, demoLabel, description, showIcon, showPoweredBy, poweredByUrl, style, onExit, }: DemoModeBannerProps): react_jsx_runtime.JSX.Element | null;
353
+
354
+ /**
355
+ * Props for the DemoModeToggle component
356
+ */
357
+ interface DemoModeToggleProps {
358
+ /**
359
+ * Show "Demo Mode" label next to the toggle
360
+ * @default true
361
+ */
362
+ showLabel?: boolean;
363
+ /**
364
+ * Label text to display
365
+ * @default 'Demo Mode'
366
+ */
367
+ label?: string;
368
+ /**
369
+ * Show "Powered by DemoKit" branding
370
+ * Note: For OSS users, this is always true regardless of the prop value.
371
+ * Only paid DemoKit Cloud users can hide the branding.
372
+ * @default true
373
+ */
374
+ showPoweredBy?: boolean;
375
+ /**
376
+ * URL for the "Powered by" link
377
+ * @default 'https://demokit.ai'
378
+ */
379
+ poweredByUrl?: string;
380
+ /**
381
+ * Position of the toggle
382
+ * - 'inline': Renders where placed in the component tree
383
+ * - 'floating': Fixed position, can be moved around
384
+ * - 'corner': Fixed to bottom-right corner
385
+ * @default 'inline'
386
+ */
387
+ position?: 'inline' | 'floating' | 'corner';
388
+ /**
389
+ * Size of the toggle
390
+ * @default 'md'
391
+ */
392
+ size?: 'sm' | 'md' | 'lg';
393
+ /**
394
+ * Additional CSS class name
395
+ */
396
+ className?: string;
397
+ /**
398
+ * Custom styles
399
+ */
400
+ style?: CSSProperties;
401
+ /**
402
+ * Callback when toggle state changes
403
+ */
404
+ onChange?: (enabled: boolean) => void;
405
+ }
406
+ /**
407
+ * A toggle switch component for enabling/disabling demo mode
408
+ *
409
+ * Supports inline placement or fixed positioning (floating/corner).
410
+ * Includes optional "Powered by DemoKit" branding that is always shown for OSS users.
411
+ *
412
+ * @example Basic inline usage
413
+ * <DemoModeToggle />
414
+ *
415
+ * @example Corner position (floating)
416
+ * <DemoModeToggle position="corner" />
417
+ *
418
+ * @example Without label
419
+ * <DemoModeToggle showLabel={false} />
420
+ *
421
+ * @example With custom label and callback
422
+ * <DemoModeToggle
423
+ * label="Preview Mode"
424
+ * onChange={(enabled) => console.log('Demo mode:', enabled)}
425
+ * />
426
+ */
427
+ declare function DemoModeToggle({ showLabel, label, showPoweredBy, poweredByUrl, position, size, className, style, onChange, }: DemoModeToggleProps): react_jsx_runtime.JSX.Element | null;
428
+
429
+ /**
430
+ * Props for the PoweredByBadge component
431
+ */
432
+ interface PoweredByBadgeProps {
433
+ /**
434
+ * URL to link to when clicked
435
+ * @default 'https://demokit.ai'
436
+ */
437
+ url?: string;
438
+ /**
439
+ * Visual variant for light/dark backgrounds
440
+ * @default 'auto'
441
+ */
442
+ variant?: 'light' | 'dark' | 'auto';
443
+ /**
444
+ * Size of the badge
445
+ * @default 'sm'
446
+ */
447
+ size?: 'xs' | 'sm' | 'md';
448
+ /**
449
+ * Additional CSS class name
450
+ */
451
+ className?: string;
452
+ /**
453
+ * Custom styles
454
+ */
455
+ style?: CSSProperties;
456
+ }
457
+ /**
458
+ * A "Powered by DemoKit" badge that links to demokit.ai
459
+ *
460
+ * This badge is shown by default for OSS users and cannot be hidden
461
+ * without a valid DemoKit Cloud paid plan.
462
+ *
463
+ * @example
464
+ * <PoweredByBadge />
465
+ *
466
+ * @example With dark theme
467
+ * <PoweredByBadge variant="dark" />
468
+ *
469
+ * @example Small size
470
+ * <PoweredByBadge size="xs" />
471
+ */
472
+ declare function PoweredByBadge({ url, variant, size, className, style, }: PoweredByBadgeProps): react_jsx_runtime.JSX.Element;
228
473
 
229
474
  /**
230
475
  * React context for demo mode state
@@ -232,4 +477,4 @@ declare function DemoModeBanner({ className, exitLabel, demoLabel, description,
232
477
  */
233
478
  declare const DemoModeContext: react.Context<DemoModeContextValue | undefined>;
234
479
 
235
- export { DemoKitProvider, type DemoKitProviderProps, DemoModeBanner, type DemoModeBannerProps, DemoModeContext, type DemoModeContextValue, useDemoMode, useDemoSession, useIsDemoMode, useIsHydrated };
480
+ export { DemoKitProvider, type DemoKitProviderProps, DemoModeBanner, type DemoModeBannerProps, DemoModeContext, type DemoModeContextValue, DemoModeToggle, type DemoModeToggleProps, PoweredByBadge, type PoweredByBadgeProps, useDemoMode, useDemoSession, useIsDemoMode, useIsHydrated };