Use URL-based routing instead of local state

This commit is contained in:
Chris Wanstrath 2026-02-26 19:43:18 -08:00
parent 68274d8651
commit 45b1903e6b
5 changed files with 25 additions and 12 deletions

View File

@ -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()
}

View File

@ -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

View File

@ -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')
}

View File

@ -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<string, string> = JSON.parse(localStorage.getItem('appTabs') || '{}')
export let appTabs: Record<string, string> = {}
// 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))
}

View File

@ -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(<Shell />))
app.get('/app/:name', c => c.html(<Shell />))
app.get('/logs', c => c.html(<Shell />))
app.get('/metrics', c => c.html(<Shell />))
app.get('/settings', c => c.html(<Shell />))
cleanupStalePublishers()