/** * Where the browser is, in the four shapes Wellformed cares about. * * There is no router library and no client-side history here on purpose. * A workspace is addressed by its path, and switching between workspaces is a * real navigation: that way two tabs can hold two workspaces, links name the * workspace they point at, and no cached query can survive into a workspace it * was never fetched from. The grid's own state (table, view, filters) is not * in the URL — it never was — so nothing else needs history at all. */ export type Route = | { kind: 'workspace'; slug: string } | { kind: 'signup' } | { kind: 'signin' } | { kind: 'invite'; token: string } | { kind: 'forgot' } | { kind: 'reset'; token: string } | { kind: 'verify'; token: string } | { kind: 'pricing' } | { kind: 'home'; locale?: 'en' | 'nl' }; export function readRoute(pathname: string = window.location.pathname): Route { const parts = pathname.split('/').filter(Boolean); if (parts[0] === 'w' && parts[1]) return { kind: 'workspace', slug: decodeURIComponent(parts[1]) }; // The landing page, pinned to one language. `/` adapts to the browser; these // exist so each language has a stable address search engines can index and // hreflang can point at. if (parts[0] === 'en' && !parts[1]) return { kind: 'home', locale: 'en' }; if (parts[0] === 'nl' && !parts[1]) return { kind: 'home', locale: 'nl' }; if (parts[0] === 'signup') return { kind: 'signup' }; if (parts[0] === 'signin' || parts[0] === 'login') return { kind: 'signin' }; if (parts[0] === 'invite' && parts[1]) return { kind: 'invite', token: decodeURIComponent(parts[1]) }; if (parts[0] === 'forgot') return { kind: 'forgot' }; if (parts[0] === 'reset' && parts[1]) return { kind: 'reset', token: decodeURIComponent(parts[1]) }; if (parts[0] === 'verify' && parts[1]) return { kind: 'verify', token: decodeURIComponent(parts[1]) }; if (parts[0] === 'pricing') return { kind: 'pricing' }; return { kind: 'home' }; } export function workspacePath(slug: string): string { return `/w/${encodeURIComponent(slug)}`; } /** A full navigation, deliberately — see the note at the top of this file. */ export function goTo(path: string): void { window.location.assign(path); } /** Make a server-issued path (an invite link) absolute for copying and sharing. */ export function absolute(path: string): string { return new URL(path, window.location.origin).toString(); }