// Loop MVP — App shell, persona switcher, router, tweaks const TWEAK_DEFAULS = /*EDITMODE-BEGIN*/{ "persona": "principal", "darkStatus": false, "showOrbits": true }/*EDITMODE-END*/; const TABS = [ { key: 'home', label: 'Home', icon: 'home' }, { key: 'meetings', label: 'Meetings', icon: 'calendar' }, { key: 'people', label: 'People', icon: 'users' }, { key: 'agents', label: 'Agents', icon: 'sparkles' }, { key: 'more', label: 'More', icon: 'grid' }, ]; // Map every route key to its primary tab so the tab bar highlights correctly const ROUTE_TO_TAB = { home: 'home', meetings: 'meetings', brief: 'meetings', live: 'meetings', recap: 'meetings', people: 'people', person: 'people', agents: 'agents', 'agent-chat': 'agents', 'agent-builder': 'agents', more: 'more', notifications: 'home', tasks: 'more', dataroom: 'more', skills: 'more', compare: 'more', settings: 'more', }; function App() { const [t, setTweak] = useTweaks(TWEAK_DEFAULS); const persona = t.persona; // On a real phone IOSDevice is frameless (no fake status bar), so the // shell shouldn't reserve the 62px the mockup status bar needs. const isPhone = useIsPhone(); // Per-persona history so switching personas doesn't carry you mid-flow const [history, setHistory] = React.useState({ principal: [{ route: 'home' }], moderator: [{ route: 'home' }] }); const stack = history[persona]; const current = stack[stack.length - 1]; const go = React.useCallback((route, params) => { setHistory(h => ({ ...h, [persona]: [...h[persona], { route, params }] })); }, [persona]); // Provide a back action by walking the stack const back = React.useCallback(() => { setHistory(h => { const s = h[persona]; if (s.length <= 1) return h; return { ...h, [persona]: s.slice(0, -1) }; }); }, [persona]); const goTab = (tabKey) => { setHistory(h => ({ ...h, [persona]: [{ route: tabKey }] })); }; const screen = renderScreen(current.route, { persona, go: (r, p) => r === 'back' ? back() : go(r, p), params: current.params }); const tab = ROUTE_TO_TAB[current.route] || 'home'; const hideTabBar = current.route === 'live' || current.route === 'agent-chat'; return (
{screen} {!hideTabBar && }
setTweak('persona', v)} />
Two personas, one product — same core, persona-specific surface and rituals.
setTweak('darkStatus', v)} /> setTweak('showOrbits', v)} />
{[ ['home', 'Home'], ['meetings', 'Meetings'], ['brief', 'Brief'], ['live', 'Live'], ['recap', 'Recap'], ['people', 'People'], ['person', 'Person'], ['agents', 'Agents'], ['agent-chat', 'Agent chat'], ['agent-builder', 'Build agent'], ['more', 'More'], ['tasks', 'Tasks'], ['notifications', 'Alerts'], ['dataroom', 'Dataroom'], ['compare', 'Compare'], ['skills', 'Skills'], ['settings', 'Settings'], ].map(([r, l]) => ( ))}
); } function renderScreen(route, ctx) { switch (route) { case 'home': return ; case 'meetings': return ; case 'brief': return ; case 'live': return ; case 'recap': return ; case 'people': return ; case 'person': return ; case 'agents': return ; case 'agent-chat': return ; case 'agent-builder': return ; case 'more': return ; case 'notifications': return ; case 'tasks': return ; case 'dataroom': return ; case 'skills': return ; case 'compare': return ; case 'settings': return ; default: return ; } } ReactDOM.createRoot(document.getElementById('root')).render();