From 3d574722527b1d5d19b977a2d072eaa6b9b3ca83 Mon Sep 17 00:00:00 2001 From: homelab-updater Date: Wed, 30 Sep 2026 01:22:07 +0200 Subject: [PATCH] feat(web): sidebar shell, dark-first theme with toggle, card and stat components Restyles shared tables, headers, tabs and modals on new surface, shadow and radius tokens; the runs list sits in a card with its filters. Co-Authored-By: Claude Opus 5.5 --- src/web/app.tsx | 59 ++-- src/web/components/ui.tsx | 101 +++++++ src/web/main.tsx | 3 + src/web/styles.css | 550 ++++++++++++++++++++++++++++++------ src/web/theme.test.ts | 15 + src/web/theme.ts | 49 ++++ src/web/views/instances.tsx | 22 +- 7 files changed, 691 insertions(+), 108 deletions(-) create mode 100644 src/web/components/ui.tsx create mode 100644 src/web/theme.test.ts create mode 100644 src/web/theme.ts diff --git a/src/web/app.tsx b/src/web/app.tsx index 7ef5904..8dd786d 100644 --- a/src/web/app.tsx +++ b/src/web/app.tsx @@ -7,6 +7,9 @@ import type { LiveStatus } from './live.ts'; import { formatBuild } from './format.ts'; import type { BuildInfo } from '../http/contract.ts'; import { hrefFor, parseRoute } from './route.ts'; +import { useTheme } from './theme.ts'; +import { Icon } from './components/ui.tsx'; +import type { IconName } from './components/ui.tsx'; import type { Route } from './route.ts'; import { AppDetailView, AppsView } from './views/apps.tsx'; import { OverviewView } from './views/overview.tsx'; @@ -31,10 +34,11 @@ function useRoute(): Route { const LIVE_LABEL: Record = { connecting: 'connecting…', live: 'live', offline: 'offline — retrying' }; -function Nav({ route, status }: { route: Route; status: LiveStatus }) { +function Sidebar({ route, status }: { route: Route; status: LiveStatus }) { const gates = useResource(() => api.workflowInstances({ status: 'waiting' }), []); + const { theme, toggle } = useTheme(); const waiting = gates.data?.length ?? 0; - const link = (view: Route['view'], label: string, target: Parameters[0], count = 0) => ( + const link = (view: Route['view'], label: string, icon: IconName, target: Parameters[0], count = 0) => ( - {label} + + {label} {count > 0 ? {count} : null} ); return ( -
- Homelab upgrade - - - {LIVE_LABEL[status]} +
+ + + ); } @@ -135,11 +148,15 @@ export function App() { const { revision, status, refresh } = useLive(); return ( -