import React, { useMemo, useState, useCallback, useEffect } from 'react'; import type { PagesData } from '../common/types/component.types'; import { importFromJSON } from '../common/utils/serialization'; import { formatScopedBindingViolations, validatePagesDataScopedBindings } from '../common/state/validateScopedBindings'; import { RendererRuntimeProvider } from './runtime'; import { Preview } from './components/Preview'; // Ensure component definitions are registered once per bundle load. import '../common/registry/definitions'; export interface RendererRoutesProps { json: string; workflow?: { executeUrl: string }; initialRoute?: string; } export const RendererRoutes: React.FC = ({ json, workflow, initialRoute }) => { const parseResult = useMemo<{ pagesData: PagesData | null; error: string | null }>(() => { const parsed = importFromJSON(json); if (!parsed || !Array.isArray(parsed.pages)) { return { pagesData: null, error: 'Invalid renderer JSON.' }; } const violations = validatePagesDataScopedBindings(parsed); if (violations.length > 0) { return { pagesData: null, error: `Invalid scoped bindings detected:\n${formatScopedBindingViolations(violations)}`, }; } return { pagesData: parsed, error: null }; }, [json]); const defaultRoute = useMemo(() => { if (!parseResult.pagesData) return initialRoute || '/'; const defaultPage = parseResult.pagesData.pages.find((p) => p.isDefault) || parseResult.pagesData.pages[0]; return initialRoute || defaultPage?.route || '/'; }, [parseResult.pagesData, initialRoute]); const getHashRoute = (): string | null => { const hash = window.location.hash; return hash ? hash.slice(1) : null; }; const [currentRoute, setCurrentRoute] = useState( getHashRoute() || defaultRoute ); // Set initial hash on mount useEffect(() => { if (!window.location.hash) { window.location.hash = defaultRoute; } }, []); const handleNavigate = useCallback((route: string) => { window.location.hash = route; setCurrentRoute(route); }, []); // Sync state with browser back/forward useEffect(() => { const onHashChange = () => { const route = getHashRoute(); if (route) setCurrentRoute(route); }; window.addEventListener('hashchange', onHashChange); return () => window.removeEventListener('hashchange', onHashChange); }, []); if (!parseResult.pagesData || !Array.isArray(parseResult.pagesData.pages)) { return (
{parseResult.error || 'Invalid renderer JSON.'}
); } return ( ); };