From 45b1903e6b3619b861936795cca75fd5e417ffde Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 26 Feb 2026 19:43:18 -0800 Subject: [PATCH] Use URL-based routing instead of local state --- src/client/components/DashboardLanding.tsx | 5 ++--- src/client/components/Nav.tsx | 6 +++--- src/client/router.ts | 17 +++++++++++++++-- src/client/state.ts | 6 ++---- src/server/index.tsx | 3 +++ 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/client/components/DashboardLanding.tsx b/src/client/components/DashboardLanding.tsx index a8dd5ac..758f2d3 100644 --- a/src/client/components/DashboardLanding.tsx +++ b/src/client/components/DashboardLanding.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'hono/jsx' import { openAppSelectorModal } from '../modals' import { navigate } from '../router' -import { dashboardTab, isNarrow, setDashboardTab } from '../state' +import { dashboardTab, isNarrow } from '../state' import { AppSelectorChevron, DashboardContainer, @@ -30,8 +30,7 @@ export function DashboardLanding({ render }: { render: () => void }) { } const switchTab = (tab: typeof dashboardTab) => { - setDashboardTab(tab) - render() + navigate(tab === 'urls' ? '/' : `/${tab}`) if (tab === 'logs') scrollLogsToBottom() } diff --git a/src/client/components/Nav.tsx b/src/client/components/Nav.tsx index 4f222cb..516f106 100644 --- a/src/client/components/Nav.tsx +++ b/src/client/components/Nav.tsx @@ -1,5 +1,6 @@ import type { App } from '../../shared/types' -import { apps, getSelectedTab, setSelectedTab } from '../state' +import { navigate } from '../router' +import { apps, getSelectedTab } from '../state' import { Tab, TabBar } from '../styles' import { resetToolIframe } from '../tool-iframes' @@ -12,8 +13,7 @@ export function Nav({ app, render }: { app: App; render: () => void }) { resetToolIframe(tab, app.name) return } - setSelectedTab(app.name, tab) - render() + navigate(tab === 'overview' ? `/app/${app.name}` : `/app/${app.name}/${tab}`) } // Find all tools diff --git a/src/client/router.ts b/src/client/router.ts index eeb7dd0..5bedb2f 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -1,4 +1,4 @@ -import { setCurrentView, setSelectedApp } from './state' +import { setCurrentView, setDashboardTab, setSelectedApp, setSelectedTab } from './state' let _render: () => void @@ -31,14 +31,27 @@ function route() { const path = location.pathname if (path.startsWith('/app/')) { - const name = decodeURIComponent(path.slice(5)) + const rest = decodeURIComponent(path.slice(5)) + const slashIdx = rest.indexOf('/') + const name = slashIdx === -1 ? rest : rest.slice(0, slashIdx) + const tab = slashIdx === -1 ? 'overview' : rest.slice(slashIdx + 1) setSelectedApp(name) + setSelectedTab(name, tab) setCurrentView('dashboard') } else if (path === '/settings') { setSelectedApp(null) setCurrentView('settings') + } else if (path === '/logs') { + setSelectedApp(null) + setDashboardTab('logs') + setCurrentView('dashboard') + } else if (path === '/metrics') { + setSelectedApp(null) + setDashboardTab('metrics') + setCurrentView('dashboard') } else { setSelectedApp(null) + setDashboardTab('urls') setCurrentView('dashboard') } diff --git a/src/client/state.ts b/src/client/state.ts index e994cd4..a1501f6 100644 --- a/src/client/state.ts +++ b/src/client/state.ts @@ -7,19 +7,18 @@ export let currentView: 'dashboard' | 'settings' = 'dashboard' export let isNarrow: boolean = window.matchMedia('(max-width: 768px)').matches export let selectedApp: string | null = null export let sidebarCollapsed: boolean = localStorage.getItem('sidebarCollapsed') === 'true' -export let dashboardTab: DashboardTab = (localStorage.getItem('dashboardTab') as DashboardTab) || 'urls' +export let dashboardTab: DashboardTab = 'urls' export let sidebarSection: 'apps' | 'tools' = (localStorage.getItem('sidebarSection') as 'apps' | 'tools') || 'apps' // Server state (from SSE) export let apps: App[] = [] // Tab state -export let appTabs: Record = JSON.parse(localStorage.getItem('appTabs') || '{}') +export let appTabs: Record = {} // State setters export function setDashboardTab(tab: DashboardTab) { dashboardTab = tab - localStorage.setItem('dashboardTab', tab) } export function setCurrentView(view: 'dashboard' | 'settings') { @@ -54,5 +53,4 @@ export const getSelectedTab = (appName: string | null) => export function setSelectedTab(appName: string | null, tab: string) { if (!appName) return appTabs[appName] = tab - localStorage.setItem('appTabs', JSON.stringify(appTabs)) } diff --git a/src/server/index.tsx b/src/server/index.tsx index 71ae33d..011de87 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -115,7 +115,10 @@ app.get('/dist/:file', async c => { }) // SPA routes — serve the shell for all client-side paths +app.get('/app/:name/:tab', c => c.html()) app.get('/app/:name', c => c.html()) +app.get('/logs', c => c.html()) +app.get('/metrics', c => c.html()) app.get('/settings', c => c.html()) cleanupStalePublishers()