- TypeScript 61.9%
- Svelte 33.5%
- Python 2.6%
- CSS 1.6%
- JavaScript 0.2%
- Other 0.1%
|
||
|---|---|---|
| .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 | ||
| kit.config.ts | ||
| 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 | ||
| 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.
The device rejects API calls from other origins unless corsDisabled is
on, so enable it first (Settings → System on the device's own WebUI, or
curl -X PATCH -H 'Content-Type: application/json' -d '{"corsDisabled":true}' http://<device>/api/settings). Switch it off again when you're done.
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.

