The web user-interface for the BTClock
  • TypeScript 61.6%
  • Svelte 33.1%
  • Python 2.9%
  • CSS 1.7%
  • JavaScript 0.5%
  • Other 0.1%
Find a file
Djuri Baars 772c1f934e
All checks were successful
/ check-changes (push) Successful in 7s
/ build (push) Successful in 10m46s
test(e2e): run Playwright integration tests on Firefox too
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.
2026-06-09 20:16:44 +02:00
.forgejo/workflows test(e2e): run Playwright integration tests on Firefox too 2026-06-09 20:16:44 +02:00
.github test(e2e): run Playwright integration tests on Firefox too 2026-06-09 20:16:44 +02:00
.vscode Added WiFi signal status and settings 2023-11-21 16:05:00 +01:00
doc docs(readme): refresh screenshots with live framebuffer + REV_B mocks 2026-05-14 12:52:08 +02:00
extra/icons Remove icons to save space in image 2024-09-03 12:37:35 +02:00
patches chore(deps): bump kit 2.64.0, svelte 5.56.3, daisyui 5.5.23, @types/node 24.13.1 2026-06-09 12:48:45 +02:00
project.inlang feat(i18n): add Italian, Polish, Turkish, Czech & Danish (15 locales) 2026-06-05 21:00:49 +02:00
scripts WebUI: priceSymMode, availableFonts hasBtcSymbol, Playwright API coverage 2026-05-09 19:39:21 +02:00
src fix(upload): POST firmware/WebUI as raw octet-stream, not multipart 2026-06-06 01:37:54 +02:00
static docs(openapi): realign spec with firmware control_server routes 2026-06-05 18:30:01 +02:00
tests test(e2e): run Playwright integration tests on Firefox too 2026-06-09 20:16:44 +02:00
.editorconfig chore: add .editorconfig 2026-04-30 03:18:20 +02:00
.gitignore chore(build): add opt-in bundle visualizer 2026-04-30 03:18:00 +02:00
.nvmrc feat(i18n): add French, Arabic, Portuguese & Russian with full RTL support 2026-06-05 20:32:44 +02:00
.prettierignore chore(prettier): ignore Paraglide-generated artifacts in project.inlang/ 2026-05-14 12:51:56 +02:00
.prettierrc feat(webui): promote webui-v2 to repo root 2026-04-17 02:06:52 +02:00
AGENTS.md docs: refresh AGENTS.md + README to match current codebase 2026-06-05 18:38:23 +02:00
Dockerfile chore(docker): bump base image to Node 24 LTS 2026-04-17 14:17:36 +02:00
eslint.config.js chore: ignore .pnpm-store for Prettier, ESLint, and git 2026-04-17 02:12:31 +02:00
gzip_build.py WebUI: priceSymMode, availableFonts hasBtcSymbol, Playwright API coverage 2026-05-09 19:39:21 +02:00
package.json chore(deps): bump kit 2.64.0, svelte 5.56.3, daisyui 5.5.23, @types/node 24.13.1 2026-06-09 12:48:45 +02:00
playwright.base.ts test(e2e): de-flake Playwright in CI (offline initMock, timeouts, retry) 2026-06-05 19:33:46 +02:00
playwright.config.ts test(e2e): run Playwright integration tests on Firefox too 2026-06-09 20:16:44 +02:00
playwright.doc-screenshot.config.ts docs(readme): refresh screenshots with live framebuffer + REV_B mocks 2026-05-14 12:52:08 +02:00
playwright.screenshot.config.ts chore(playwright): factor shared config into playwright.base.ts 2026-04-30 03:09:54 +02:00
pnpm-lock.yaml chore(deps): bump kit 2.64.0, svelte 5.56.3, daisyui 5.5.23, @types/node 24.13.1 2026-06-09 12:48:45 +02:00
pnpm-workspace.yaml chore(deps): bump kit 2.64.0, svelte 5.56.3, daisyui 5.5.23, @types/node 24.13.1 2026-06-09 12:48:45 +02:00
README.md feat(i18n): add Italian, Polish, Turkish, Czech & Danish (15 locales) 2026-06-05 21:00:49 +02:00
renovate.json Migrate config renovate.json 2024-12-26 14:29:21 +00:00
svelte.config.js docs(convert): document the firmware-imposed bundle constraint 2026-04-26 18:54:17 +02:00
tsconfig.json chore(tsconfig): turn on stricter checks 2026-04-30 03:08:16 +02:00
vite.config.test.ts perf(i18n): compile messages per-locale (outputStructure: locale-modules) 2026-06-09 12:09:33 +02:00
vite.config.ts perf(i18n): compile messages per-locale (outputStructure: locale-modules) 2026-06-09 12:09:33 +02:00

BTClock WebUI

Latest release BTClock CI

The web user-interface for the BTClock.

Screenshot Screenshot Dark

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 in src/lib/ui/, data access in src/lib/api/, and global state in src/lib/stores/ as Svelte 5 runes.
  • Discriminated-union stateSettingsState and StatusState encode loading | 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-static with SSR disabled; the WebUI is always mounted on-device by the firmware.
  • Minimal font footprint — only the latin-400 woff2 file for Ubuntu (plus compact sats-symbol subsets) is shipped, keeping build_gz/ well below the ~420 KB LittleFS partition.