// shell.jsx - app shell: sidebar nav, topbar, route container const NAV_TREE = [ { id: "home", label: "Home", icon: "home" }, { id: "library", label: "Library", icon: "library" }, { id: "projects", label: "Projects", icon: "folder" }, { id: "ingest", label: "Ingest", icon: "upload", group: true, children: [ { id: "upload", label: "Upload", icon: "upload" }, { id: "recorders", label: "Recorders", icon: "record", badge: { kind: "live", text: "4" } }, { id: "capture", label: "Capture", icon: "capture" }, { id: "monitors", label: "Monitors", icon: "monitor" }, ], }, { id: "jobs", label: "Jobs", icon: "jobs", badge: { kind: "neutral", text: "3" } }, { id: "editor", label: "Editor", icon: "editor", badge: { kind: "dev", text: "DEV" } }, ]; const ADMIN_TREE = [ { id: "users", label: "Users", icon: "users" }, { id: "tokens", label: "Tokens", icon: "token" }, { id: "containers", label: "Containers", icon: "container" }, { id: "cluster", label: "Cluster", icon: "cluster" }, { id: "settings", label: "Settings", icon: "settings" }, ]; function NavItem({ item, active, onSelect, depth = 0, openGroups, toggleGroup }) { const isGroup = item.group && item.children; const isOpen = isGroup && openGroups.has(item.id); const isActive = active === item.id || (isGroup && item.children.some(c => c.id === active)); return ( <>
{ if (isGroup) toggleGroup(item.id); else onSelect(item.id); }} > {item.label} {item.badge && ( {item.badge.text} )} {isGroup && }
{isGroup && isOpen && (
{item.children.map(c => (
onSelect(c.id)} > {c.label} {c.badge && {c.badge.text}}
))}
)} ); } function Sidebar({ active, onNavigate }) { const [openGroups, setOpenGroups] = React.useState(new Set(["ingest"])); const toggleGroup = (id) => { setOpenGroups(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; React.useEffect(() => { const ingestChildren = ["upload", "recorders", "capture", "monitors"]; if (ingestChildren.includes(active)) { setOpenGroups(prev => prev.has("ingest") ? prev : new Set([...prev, "ingest"])); } }, [active]); return ( ); } function Topbar({ crumbs, onNavigate, right }) { return (
{crumbs.map((c, i) => ( {i > 0 && } c.to && onNavigate && onNavigate(c.to)} > {c.label} ))}
⌘K
cluster healthy
{right}
); } // General-purpose read-only form field used by recorder modal and other forms. // Renders as a labeled select (when select=true) or text input. function Field({ label, value, select, children }) { return (
{children || (select ? ( ) : ( ) )}
); } window.Sidebar = Sidebar; window.Topbar = Topbar; window.NAV_TREE = NAV_TREE; window.Field = Field;