feat: render the panel image as a 1-bit 800x480 BMP or PNG
With the move to BYOS there is no TRMNL cloud to interpret a Liquid template, so the application draws the screen itself. The pipeline is satori (flexbox to SVG) then resvg (SVG to pixels) then a hand-written 1-bit encoder. Chromium was the alternative and was rejected: half a gigabyte and a real memory appetite in the runtime image, for a screen of eight blocks. Playwright still handles the E2E tests, in its own pinned image, never in the application one. The payoff is testability. The layout is asserted on the element tree and the geometry on the SVG text, so a rendering regression shows up as a readable diff instead of a pixel comparison. The BMP and PNG encoders are verified field by field against their specifications, including an independent CRC-32 for the PNG: a device rejecting a malformed image is expensive to debug from a shop window. Two properties are pinned because the battery depends on them: the same payload must produce byte-identical output, and changed hours must produce different output. The filename handed to the device is a hash of these bytes, and the firmware skips the redraw when it is unchanged. The fonts are vendored into public/fonts and the logo into public/brand, both committed. Rendering must not depend on an install tree, a CDN or the network, or the bytes drift and the panel wakes for nothing. `npm run screen:preview` writes a real 1-bit image plus a magnified view, which is where clipping and thin strokes give themselves up. That is how the week strip was caught clipping and condensed. `npm run brand` rebuilds the assets: it locates the wordmark band in the shop logo rather than hard-coding offsets a future revision would break, and thresholds it with the panel's own encoder. sharp is a devDependency used only there; nothing at runtime needs it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 349 B |
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,20 @@
|
||||
# Fonts used to render the e-ink screen
|
||||
|
||||
These four files are committed on purpose.
|
||||
|
||||
The panel image is rendered server-side by satori, which needs the font
|
||||
binaries at render time. Reading them from `node_modules` would make the
|
||||
rendering depend on the install tree, and on what the production image happens
|
||||
to trace; reading them from a CDN would make it depend on the network. Both
|
||||
would let the rendered pixels drift, and the rendered pixels feed a content
|
||||
hash that decides whether the panel redraws at all.
|
||||
|
||||
So the exact binaries live here, next to the code that uses them.
|
||||
|
||||
| File | Source | Licence |
|
||||
|---|---|---|
|
||||
| `inter-400.woff`, `inter-600.woff`, `inter-700.woff` | `@fontsource/inter` (Inter, Rasmus Andersson) | SIL Open Font License 1.1 |
|
||||
| `source-serif-600.woff` | `@fontsource/source-serif-4` (Source Serif 4, Adobe) | SIL Open Font License 1.1 |
|
||||
|
||||
Both licences permit redistribution. To refresh them, re-copy from
|
||||
`node_modules/@fontsource/*/files/*-latin-*-normal.woff`.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user