- TypeScript 61.6%
- Svelte 33.7%
- Python 2.7%
- CSS 1.6%
- JavaScript 0.2%
- Other 0.1%
SvelteKit 3.1.0 clears the last three `pnpm audit` findings, which have no 2.x fix: GHSA-pfhg-q3qv-9q6r and GHSA-27mw-82g8-phjj in kit itself, and GHSA-pxg6-pf52-xh8x in the cookie 0.6 it bundled (kit 3 uses cookie 2). `pnpm audit` now reports no known vulnerabilities. The mechanical part ran through `sv migrate sveltekit-3`: `$lib` imports become `#lib/*.js` subpath imports declared in package.json, `$app/environment` becomes `$app/env`, and tsconfig.json extends `$app/tsconfig`. On top of that: - svelte.config.js is gone. Its options live in kit.config.ts, which both vite.config.ts and vite.config.test.ts pass to sveltekit(); the migration tool only rewired vite.config.ts, so the Playwright build would otherwise have run without the adapter, appDir and bundleStrategy: 'single'. - The LittleFS filename patch is rebased onto kit 3.1.0. Kit 3 moved the client entry/chunk/asset naming around; the output names are the same as before (build/immutable/<hash>.js, assets/<hash>.<ext>). - Kit 3 runs the adapter in a buildApp post hook, after closeBundle, so the bundle.html cleanup moved to buildApp (post, after sveltekit()). Without that the SPA fallback ended up in the LittleFS image again. - version.pollInterval is set to 0. Kit 3 polls build/version.json hourly by default; nothing reads updated.current and every poll hits the device. Kit 3 also checks on window focus/visibility, which has no switch; that is one small GET per focus. - PUBLIC_BASE_URL moves from the deprecated $env/static/public to a static public variable in src/env.ts, read via $app/env/public. The schema defaults it to '', which replaces the Object.hasOwn guard. An empty PUBLIC_BASE_URL still overrides .env (checked against a .env pointing at a device), so the Playwright and CI builds keep same-origin requests. Vitest gets a $app/env/public mock. - Source-regex specs that pin import lines expect the new `.js` specifiers. TypeScript stays on 6: kit 3 (^6.0.0), svelte-check and typescript-eslint do not accept 7 yet. Gzipped LittleFS payload: 322262 bytes (was 319200; limit 410000). |
||
|---|---|---|
| .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.
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.

