- TypeScript 61.6%
- Svelte 33.1%
- Python 2.9%
- CSS 1.7%
- JavaScript 0.5%
- Other 0.1%
Add a `firefox` project mirroring the desktop `chromium` specs. The mobile-navbar specs stay chromium-only since Playwright doesn't support the Pixel 7 profile's isMobile/touch emulation under Firefox. Assert ValidityState.rangeUnderflow instead of the browser-specific validationMessage text in the min-constraint spec, which otherwise fails on Firefox's differently-worded message. Install Firefox alongside Chromium in both the Forgejo and GitHub CI workflows so test:integration can run it. |
||
|---|---|---|
| .forgejo/workflows | ||
| .github | ||
| .vscode | ||
| doc | ||
| extra/icons | ||
| patches | ||
| project.inlang | ||
| scripts | ||
| src | ||
| static | ||
| tests | ||
| .editorconfig | ||
| .gitignore | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| Dockerfile | ||
| eslint.config.js | ||
| gzip_build.py | ||
| package.json | ||
| playwright.base.ts | ||
| playwright.config.ts | ||
| playwright.doc-screenshot.config.ts | ||
| playwright.screenshot.config.ts | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| renovate.json | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.test.ts | ||
| vite.config.ts | ||
BTClock WebUI
The web user-interface for the BTClock.
Getting started
This project is managed with pnpm. Install it with
corepack enable pnpm or npm i -g pnpm if you don't have it yet.
pnpm install # applies the SvelteKit filename-shortening patch via pnpm patchedDependencies
pnpm dev # start the dev server (http://localhost:5173)
Set PUBLIC_BASE_URL in .env to the address of a real BTClock (e.g.
http://btclock-d60b14.local) so the dev server talks to it. When unset
it falls back to an empty string — i.e. same-origin requests — which is
also what a build the firmware serves from LittleFS wants, so leave it
empty at build time.
Production build
pnpm build # produces dist/ with prerendered index.html + hashed JS/CSS
python3 gzip_build.py # gzips everything under dist/ into build_gz/
mklittlefs -c build_gz -s 409600 output/littlefs.bin
The firmware serves files literally out of /lfs/www/ (see
control_server.cpp), so the build keeps that directory minimal: only
/ is prerendered, and a tiny post-build step in vite.config.ts
deletes adapter-static's bundle.html SPA fallback (the firmware
doesn't route unknown paths to it). /api and /convert opt out of
prerendering and are reached only via SvelteKit client routing. The
filename-shortening patch in patches/ keeps asset names within
LittleFS's filename limit.
Tests
pnpm test:unit # vitest unit + component tests
pnpm test:integration # playwright end-to-end suite (chromium + mobile)
pnpm test:screenshots # device/locale screenshot suite
pnpm doc:update-screenshots
Languages
The WebUI ships 15 translations. Message catalogs live in
src/lib/locales/<code>.json and are compiled by Paraglide. The language
picker lists them sorted alphabetically by their localized name; Arabic is
rendered right-to-left (<html dir="rtl">), with technical identifiers
(IP/MAC/hostname, version and commit) kept left-to-right.
🇬🇧 English (en) · 🇳🇱 Nederlands (nl) · 🇩🇪 Deutsch (de) ·
🇪🇸 Español (es) · 🇫🇷 Français (fr) · 🇮🇹 Italiano (it) ·
🇵🇹 Português (pt) · 🇵🇱 Polski (pl) · 🇨🇿 Čeština (cs) ·
🇩🇰 Dansk (da) · 🇹🇷 Türkçe (tr) · 🇷🇺 Русский (ru) ·
🇸🇦 العربية (ar) · 🇨🇳 中文 (zh) · 🇯🇵 日本語 (ja)
To add a locale: copy src/lib/locales/en.json and translate the values,
add the tag to project.inlang/settings.json, and register the locale +
flag in src/lib/i18n.svelte.ts.
Key architectural choices
- Feature-based folders — UI is organised under
src/lib/features/{control,status,firmware,settings,convert}with colocated sub-components and spec tests. Generic primitives live insrc/lib/ui/, data access insrc/lib/api/, and global state insrc/lib/stores/as Svelte 5 runes. - Discriminated-union state —
SettingsStateandStatusStateencodeloading | error | ready, so every consumer handles each phase explicitly instead of falling through nullable props. - Valibot schemas — Runtime validation for every inbound API payload
lives in
src/lib/api/schemas.ts, protecting the UI from firmware drift. - Paraglide JS v2 — Message catalogs in
src/lib/locales/are compiled per-locale and tree-shaken by the Paraglide Vite plugin. See Languages for the 15 shipped locales. - Static output only —
@sveltejs/adapter-staticwith SSR disabled; the WebUI is always mounted on-device by the firmware. - Minimal font footprint — only the
latin-400woff2 file for Ubuntu (plus compact sats-symbol subsets) is shipped, keepingbuild_gz/well below the ~420 KB LittleFS partition.

