/** * Hash routing, shared. * * `pageFromHash()` turns `location.hash` into the page id the app navigates by * (`llm-page-change`'s `detail.page`, the sidebar's `_activePage`, the value the * view-context store describes). It lives here — and not in `sidebar.js`, where * it grew — because there are now two readers of it: the menu highlight and the * view context attached to a message. Two copies of this logic would drift, and * the drift would be invisible: the assistant would be told the user is on one * page while the menu highlights another. * * The mobile shell (`mobile-app.js`) routes a fixed set of sections of its own * and does not go through this. */ // Every hash segment the app accepts as a page. Anything else falls back to // `home`, so a hand-typed or stale URL lands on the chat rather than nowhere. export const KNOWN_PAGES = [ 'inbox', 'dashboard', 'tasks', 'projects', 'files', 'models', 'providers', 'approval', 'agents', 'users', 'roles', 'shared-folders', 'connectors', 'connector', 'plugins', 'plugin-detail', 'marketplace', 'profile', 'config', 'llm-requests', 'session', 'system-agents', 'file_viewer', 'tool_detail', ]; export function pageFromHash() { const hash = location.hash.slice(1); if (!hash) return 'home'; // Segment ends at the first `/` (e.g. `#session/123`) or `?` (e.g. `#file_viewer?path=...`). const match = hash.match(/^([^/?]+)/); const segment = match ? match[1] : ''; // Plugin pages: `#plugin//` — the route is accepted by // shape (deep links must survive the async `/api/plugins/pages` load); the // host reports an error if the page turns out not to exist for this user. if (segment === 'plugin') { const m = hash.match(/^plugin\/([^/?]+)\/([^/?]+)/); return m ? `plugin/${m[1]}/${m[2]}` : 'home'; } // `connector` (singular) is the per-connector detail page, `connectors` the list. // `plugin-catalog` is the pre-merge hash of what is now `#plugins`. const page = segment === 'plugin-catalog' ? 'plugins' : segment; return KNOWN_PAGES.includes(page) ? page : 'home'; }