export interface Route { type: 'blog' | 'post' | 'browser-services' | 'browser-collections' | 'browser-record' | 'new' handle?: string collection?: string rkey?: string service?: string } export function parseRoute(pathname: string): Route { const parts = pathname.split('/').filter(Boolean) // / - Blog top if (parts.length === 0) { return { type: 'blog' } } // /new - New post form if (parts[0] === 'new') { return { type: 'new' } } // /app - SPA entry point (same as blog) if (parts[0] === 'app') { return { type: 'blog' } } // /post/${rkey} - Post detail if (parts[0] === 'post' && parts[1]) { return { type: 'post', rkey: parts[1] } } // /at/${handle} - Browser services // /at/${handle}/${service-or-collection} - Browser collections or records // /at/${handle}/${collection}/${rkey} - Browser record detail if (parts[0] === 'at' && parts[1]) { const handle = parts[1] if (!parts[2]) { // /at/${handle} return { type: 'browser-services', handle } } if (!parts[3]) { // /at/${handle}/${service-or-collection} // If it looks like a domain (2 parts), treat as service // Otherwise treat as collection NSID (3+ parts) const segment = parts[2] if (segment.split('.').length <= 2) { // Likely a service domain like "bsky.app" return { type: 'browser-collections', handle, service: segment } } else { // Likely a collection NSID like "app.bsky.feed.post" // Show record list for this collection return { type: 'browser-record', handle, collection: segment } } } // /at/${handle}/${collection}/${rkey} return { type: 'browser-record', handle, collection: parts[2], rkey: parts[3] } } // Fallback to blog return { type: 'blog' } } export function buildPath(route: Route): string { switch (route.type) { case 'blog': return '/' case 'new': return '/new' case 'post': return `/post/${route.rkey}` case 'browser-services': return `/at/${route.handle}` case 'browser-collections': return `/at/${route.handle}/${route.service}` case 'browser-record': if (route.rkey) { return `/at/${route.handle}/${route.collection}/${route.rkey}` } return `/at/${route.handle}/${route.collection}` default: return '/' } } export function navigate(path: string): void { window.history.pushState({}, '', path) window.dispatchEvent(new PopStateEvent('popstate')) }