import { lazy } from 'react' import { createBrowserRouter, Navigate, useParams } from 'react-router-dom' import { routerBasename } from './utils/basePath' import App from './App' import RequireAdmin from './components/RequireAdmin' import RequireAuth from './components/RequireAuth' import RequireAuthEnabled from './components/RequireAuthEnabled' import RequireFeature from './components/RequireFeature' // Pages are code-split: each becomes its own chunk loaded on demand, so a route // no longer drags every other page (and its heavy deps — CodeMirror, the MCP // SDK, yaml, marked) into the initial bundle. The boundary in // App.jsx (around ) shows nothing while a chunk loads, keeping the // sidebar/header mounted. // // `page(key, loader)` registers the dynamic import under a route-segment key // (the first segment after /app/) so a NavLink can warm the chunk on hover via // `preloadRoute('/app/chat')`. Dynamic import() is memoised by the module // loader, so a preloaded chunk is reused — not re-fetched — when the user // actually navigates. Pages with `key: null` aren't sidebar-reachable; they // still code-split, they just won't be preloaded from the nav. const preloaders = {} function page(key, loader) { if (key !== null) preloaders[key] = loader return lazy(loader) } export function preloadRoute(path) { if (!path) return const m = path.match(/^\/app(?:\/([^/?#]*))?/) if (!m) return preloaders[m[1] ?? '']?.().catch(() => { /* network blip — real click will retry */ }) } const Home = page('', () => import('./pages/Home')) const Chat = page('chat', () => import('./pages/Chat')) const Models = page('models', () => import('./pages/Models')) const Manage = page('manage', () => import('./pages/Manage')) const ImageGen = page('image', () => import('./pages/ImageGen')) const VideoGen = page('video', () => import('./pages/VideoGen')) const TTS = page('tts', () => import('./pages/TTS')) const Sound = page('sound', () => import('./pages/Sound')) const AudioTransform = page('transform', () => import('./pages/AudioTransform')) const Talk = page('talk', () => import('./pages/Talk')) const Backends = page('backends', () => import('./pages/Backends')) const Settings = page('settings', () => import('./pages/Settings')) const Traces = page('traces', () => import('./pages/Traces')) const P2P = page('p2p', () => import('./pages/P2P')) const Agents = page('agents', () => import('./pages/Agents')) const AgentCreate = page(null, () => import('./pages/AgentCreate')) const AgentChat = page(null, () => import('./pages/AgentChat')) const AgentStatus = page(null, () => import('./pages/AgentStatus')) const Collections = page('collections', () => import('./pages/Collections')) const CollectionDetails = page(null, () => import('./pages/CollectionDetails')) const Skills = page('skills', () => import('./pages/Skills')) const SkillEdit = page(null, () => import('./pages/SkillEdit')) const AgentJobs = page('agent-jobs', () => import('./pages/AgentJobs')) const AgentTaskDetails = page(null, () => import('./pages/AgentTaskDetails')) const AgentJobDetails = page(null, () => import('./pages/AgentJobDetails')) const ModelEditor = page(null, () => import('./pages/ModelEditor')) // PipelineEditor removed — the Model Editor with templates handles all model types const ImportModel = page(null, () => import('./pages/ImportModel')) const BackendLogs = page(null, () => import('./pages/BackendLogs')) const Explorer = page(null, () => import('./pages/Explorer')) const Login = page(null, () => import('./pages/Login')) const FineTune = page('fine-tune', () => import('./pages/FineTune')) const Quantize = page('quantize', () => import('./pages/Quantize')) const Studio = page('studio', () => import('./pages/Studio')) const FaceRecognition = page('face', () => import('./pages/FaceRecognition')) const VoiceRecognition = page('voice', () => import('./pages/VoiceRecognition')) const Nodes = page('nodes', () => import('./pages/Nodes')) const NodeBackendLogs = page(null, () => import('./pages/NodeBackendLogs')) const NotFound = page(null, () => import('./pages/NotFound')) const Usage = page('usage', () => import('./pages/Usage')) const Users = page('users', () => import('./pages/Users')) const Middleware = page('middleware', () => import('./pages/Middleware')) const Account = page('account', () => import('./pages/Account')) function BrowseRedirect() { const { '*': splat } = useParams() return } function Admin({ children }) { return {children} } function Feature({ feature, children }) { return {children} } const appChildren = [ { index: true, element: }, { path: 'models', element: }, { path: 'chat', element: }, { path: 'chat/:model', element: }, { path: 'image', element: }, { path: 'image/:model', element: }, { path: 'video', element: }, { path: 'video/:model', element: }, { path: 'tts', element: }, { path: 'tts/:model', element: }, { path: 'sound', element: }, { path: 'sound/:model', element: }, { path: 'transform', element: }, { path: 'transform/:model', element: }, { path: 'studio', element: }, { path: 'talk', element: }, { path: 'face', element: }, { path: 'face/:model', element: }, { path: 'voice', element: }, { path: 'voice/:model', element: }, { path: 'usage', element: }, { path: 'account', element: }, { path: 'users', element: }, { path: 'middleware', element: }, { path: 'manage', element: }, { path: 'backends', element: }, { path: 'settings', element: }, { path: 'traces', element: }, { path: 'backend-logs/:modelId', element: }, { path: 'p2p', element: }, { path: 'nodes', element: }, { path: 'node-backend-logs/:nodeId/:modelId', element: }, { path: 'agents', element: }, { path: 'agents/new', element: }, { path: 'agents/:name/edit', element: }, { path: 'agents/:name/chat', element: }, { path: 'agents/:name/status', element: }, { path: 'collections', element: }, { path: 'collections/:name', element: }, { path: 'skills', element: }, { path: 'skills/new', element: }, { path: 'skills/edit/:name', element: }, { path: 'agent-jobs', element: }, { path: 'agent-jobs/tasks/new', element: }, { path: 'agent-jobs/tasks/:id', element: }, { path: 'agent-jobs/tasks/:id/edit', element: }, { path: 'agent-jobs/jobs/:id', element: }, { path: 'fine-tune', element: }, { path: 'quantize', element: }, { path: 'model-editor', element: }, { path: 'model-editor/:name', element: }, { path: 'import-model', element: }, { path: '*', element: }, ] export const router = createBrowserRouter([ { path: '/login', element: , }, { path: '/invite/:code', element: , }, { path: '/explorer', element: , }, { path: '/app', element: , children: appChildren, }, // Backward compatibility: redirect /browse/* to /app/* { path: '/browse/*', element: , }, { path: '/', element: , }, ], { basename: routerBasename })