3.8 KiB
CLAUDE.md — Fleet Kiosk App
The kiosk SPA for the diskless fleet: a cyber sci-fi spaceship
monitoring/control terminal (Svelte 5 + Tailwind v4 + Vite) shown fullscreen by
the diskless workstation's kiosk session (weston + Chromium). Built to a static
bundle, served by a tiny nginx, and reverse-proxied at /kiosk/ by the boot
serving stack.
This is an independent project, consumed by the fleet boot repo
(diskless-workstation) as a git submodule at serving/kiosk-app/. It builds
and publishes its own container image; the serving stack pulls it. Keep this repo
focused on the app — the OS-side kiosk runtime (weston, Chromium lockdown
policy, the kiosk-session script, the systemd unit) lives in the parent repo
under files/kiosk/, not here.
Build & run
npm install
npm run dev # http://localhost:5173/kiosk/ (Vite dev server)
npm run build # -> dist/ (static bundle, base path /kiosk/)
npm run preview # serve the built bundle locally
# container (same as CI does):
docker build -t fleet-kiosk:dev .
docker run --rm -p 8088:80 fleet-kiosk:dev # http://localhost:8088/
CI (.gitea/workflows/ci.yaml) builds the Dockerfile (which runs npm ci +
npm run build) and pushes git.bitasylum.org/audiobuzz/fleet-kiosk —
branch-aware tags, :latest only on main. Needs repo secrets PACKAGE_USER /
PACKAGE_TOKEN.
Hard contracts with the consuming fleet (do not break these)
These are the integration invariants the parent serving stack + the kiosk display depend on. Changing one means a coordinated change in the parent repo.
- Base path is
/kiosk/.vite.config.jssetsbase: '/kiosk/'because the main serving nginx reverse-proxies/kiosk/to this container and strips the prefix. Emitted asset URLs must be absolute under/kiosk/. If this ever changes, the parent'shttp/default.confproxy + thekiosk_urlPacker default must change with it. - Hash-based routing only (
src/lib/router.svelte.js,#/power,#/nav…). Deep links stay client-side so the proxy needs no SPA-fallback rewrite. Don't switch to history/path routing without adding that rewrite upstream. - Fully self-contained — no CDNs, no web fonts, no external calls. The boot LAN is air-gapped and the stack's dnsmasq sinkholes call-home (and the in-image Chromium policy blocks it). Bundle every asset; inline fonts. A request that escapes to the internet will silently fail on a real host.
- Static / read-only by nature. The display is a fixed kiosk (no keyboard
nav, locked-down Chromium). Telemetry is dummy/simulated (
src/lib/data.js,live.svelte.js,clock.svelte.js) — there is no backend. If a real data feed is ever added it must be reachable on the boot LAN and fail gracefully offline. - Reproducible bundle. Keep
package-lock.jsoncommitted; the Dockerfile usesnpm ci. Don't introduce build-time network fetches beyond npm deps.
Layout
Dockerfile 2-stage: Vite build (node:alpine) -> nginx:alpine static serve
nginx.conf serves the built assets at the container root (proxied as /kiosk/)
vite.config.js base '/kiosk/', self-contained bundle settings
src/App.svelte shell: navbar + routed page
src/lib/ Navbar, Panel, Radar, Stat, Bar, Tag, StatusBar + router/clock/data helpers
src/pages/ Dashboard, Power, Navigation, LifeSupport, Comms
.gitea/workflows/ CI: build + push the fleet-kiosk image
Conventions
- Svelte 5 runes; Tailwind v4 via
@tailwindcss/vite. - Keep components small and self-contained; shared sim helpers in
src/lib/. - Target
es2022(modern Chromium kiosk — no legacy transpile needed). - Versioning: bump
package.jsonversionper release; the image tag tracks it onmain(plus branch-name+sha tags for dev branches).