Files
roro9stack/docs/milestones/W1.md
T
twislaandClaude Sonnet 5.5 ba10ff4a5d
Site / build (pull_request) Successful in 8s
Site: how-tos and the FAQ (phase 3)
Eight how-to recipes and a FAQ page with a list of its questions, from the
README, the milestone documents and the Apps' source. The guide templates
become generic (the parent section gives the eyebrow, title and pager).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-06 21:00:34 +02:00

13 KiB

W1: Website

Status: phase 1 (home, Install, Downloads) is live at roro9stack.net; phase 2 (the user guide) is built, in a pull request. Issue #12.

Goal: a public home for the project at roro9stack.net, separate from the blog (stories) and from Gitea (developers): what it is, how to install it, how to use each App, and the docs.

The home page was designed on a canvas in a Claude chat (a dark and a light theme, built on the device's own 256-colour palette, pixel-notched corners, DM Mono and Hanken Grotesk). It is the starting point, not the final copy: it has to say only what the firmware does today.

What was found while planning (2026-10-06)

  • roro9stack.net already points at the server that hosts Gitea. Plain HTTP redirects to HTTPS; HTTPS has no certificate yet, which is the server's side to set up.
  • Release downloads from Gitea carry no CORS header, so a browser can't fetch the factory image from another origin as things are. Gitea is behind Caddy, which can add the header (below).
  • Zola can read JSON from a URL at build time (load_data), so the home page's "latest version" can come from the Gitea API.
  • There is no Gitea wiki: the design's "Wiki" links would 404.
  • The blog is published by pulling its repository on the web server and running zola build. The site does the same.

Decisions (design round 2026-10-06)

# Decision
Q175 The site lives in this repository, in site/, so the documentation is built from docs/, CONTEXT.md and the README instead of being copied.
Q176 Zola, like the blog. The design becomes a template, its tokens CSS custom properties. Dark and light follow the visitor's setting, with a visible switch. No JavaScript except the flasher's.
Q177 Domain: roro9stack.net. The blog stays at experiments.twis.la.
Q178 Publishing is the blog's way: the web server pulls main and runs zola build; that part is the maintainer's. Changes reach main through pull requests as everywhere. CI is split: a dedicated site job builds the site (zola build) when site/, docs/, README.md or CONTEXT.md change, and the firmware tests and builds skip a change that touches nothing else. A change that touches both runs both.
Q179 Phases, each its own pull request: 1. the CI split, the home page, an Install page with the browser flasher, downloads and the changelog. 2. a user guide page per App. 3. how-tos and the FAQ. 4. developer docs generated from the repository.
Q180 A browser flasher (ESP Web Tools), without copying the firmware. Caddy, in front of Gitea, adds Access-Control-Allow-Origin: https://roro9stack.net (and Vary: Origin) to GET and HEAD on /twisla/roro9stack/releases/download/* and /api/v1/repos/twisla/roro9stack/releases*: both are public already. The Install page asks the API for the latest release in the browser, finds the asset ending -factory.bin, and gives ESP Web Tools a manifest built on the spot, so it offers a new release as soon as it exists, with no rebuild. The library is vendored into site/static/ (Apache-2.0), not loaded from a CDN. The file's SHA-256 is shown on the page. Chrome or Edge on a desktop only; other browsers, and visitors without JavaScript, get the esptool steps on the same page.
Q181 Docs for the latest version only. The changelog is the Gitea releases, read at build time.
Q182 English only.
Q183 The FAQ starts from real questions: the README, and issues labelled kind/docs.
Q184 Fonts are self-hosted (no request to a third party). The hero keeps the design's illustrations, labelled as illustrations, and a section of real device screenshots is added.
Q185 The site says only what the firmware does today. Planned features are marked as planned, with their milestone. The mesh messenger is planned: the LoRa Scanner listens, nothing is sent.
Q186 Left out, each with its issue: a Gemini capsule mirror (#57), French (#58), docs per version (#59), search (#60).
Q187 The site has no version of its own. Contact is contact@roro9stack.net, and the issue tracker.

The design, reviewed

Kept as designed: the layout, the tokens, the nine App cards (their facts check out against the code: Probation 3 minutes, Safe Mode after 3 crashes, 60 seconds of typing before an update restarts the device).

Changed before it ships:

  • Install, not Download, is the first action. Downloads are for developers; a visitor wants to try it.
  • A "what you need" strip: Cardputer ADV, the Cap LoRa-1262 (only the radio needs it), a microSD card, Wi-Fi. And a plain status line: the version, and what isn't there yet.
  • The mesh card and the hero no longer promise sending and reading mesh messages.
  • The latest version is read from the API, not typed.
  • "Wiki" is replaced by Docs. The updates section gains what v0.11.0 added: the device installs releases from the project's server itself.
  • An independence line: not affiliated with or endorsed by M5Stack or Meshtastic.
  • No cookies, no analytics, no third-party requests, said on the page (fonts self-hosted).
  • The keyboard focus ring is invisible on the notched buttons: clip-path clips an outline. Another way to show focus is needed.
  • The wordmark SVGs carry an embedded C2PA content-credentials block: stripped from the site's copies.

Done when (phase 1)

  • Pushing a change under site/ runs the site job and not the firmware tests; a firmware change runs the firmware jobs and not the site's.
  • The home page renders in both themes, at phone width, with the keyboard, and says nothing the firmware doesn't do.
  • The latest version on the page is the latest release.
  • The browser flasher installs the latest release on a Cardputer ADV from Chrome (tried by hand), the page shows the file's SHA-256, and the esptool steps are on the same page.
  • A release published after the site was built is the one the Install page offers.
  • The home and Downloads pages make no request to another origin, and the Install page only asks git.twis.la.

Work breakdown

  1. CI split: a site workflow, path filters on the firmware workflow.
  2. The skeleton: site/ with the tokens, fonts, base template and the theme switch.
  3. The home page, from the design, with the changes above.
  4. Install and downloads: the flasher with its manifest built in the page, the esptool steps, the changelog. Needs the Caddy headers on the Gitea host (the maintainer's side); the page is tested against them once they're in.
  5. Checks, recorded here.

As built (phase 1)

  • CI is split. ci.yml (the firmware) has paths-ignore: site/**, docs/**, README.md, CONTEXT.md on pushes to main and on pull requests. site.yml runs zola check and zola build with a Zola pinned by its checksum, then site/tools/check_site.py, when those files change. Gitea's own source (v1.24, read, not run against the 1.27 server) shows that path filters count as matched for tag pushes, so a tag still releases. A change that touches both runs both.
  • The build output goes to public/ at the root of the repository, not into site/: output_dir = "../public" in site/config.toml, so zola build in site/ and zola --root site build from the root agree, and git ignores /public/.
  • The site is in site/: config.toml, templates (base, home, install, downloads, 404), data/ for the App cards and the screenshots' captions, static/ (stylesheet, theme switch, fonts, wordmark, icons, real screenshots, the vendored flasher). site/README.md says how to build it and what the server needs.
  • The home page follows the design. Changed from it: the hero and the mesh card promise nothing that isn't built (the mesh messenger is a "planned" card), an Install button first, a status box, a "what you need" row, a section of real screenshots, the updates section says the device installs releases itself, an independence line and a statement about cookies and third-party requests in the footer, the latest version read from the Gitea API at build time, and the nav's Wiki replaced. The two hero drawings are generated by site/tools/make_illustrations.py (a port of the design's scripted shapes) as inline SVG.
  • The focus ring. clip-path clips outlines, so a focused notched control drops its notches and shows square corners and its ring.
  • The Install page asks the API for the latest release in the browser, builds the ESP Web Tools manifest as a blob, shows the version, size and SHA-256, and only ever hands the flasher a download from the project's own server for this repository. The flasher library (ESP Web Tools 10.4.0, Apache-2.0) is vendored, trimmed to the ESP32-S3. Fonts (DM Mono, Hanken Grotesk, SIL OFL) are self-hosted.
  • The Downloads page lists the last 30 releases with their files, read at build time.
  • The wordmark SVGs from the design carried an embedded C2PA content-credentials block; it is removed from the site's copies.

As built (phase 2, the user guide)

  • /guide/ is a section of 11 pages, site/content/guide/, each with template from the section's page_template and an order from weight: the basics (keys, Launcher, Status Bar, first start, the card), then one page per App (LoRa Scanner, GNSS, Gemini, IRC, Wi-Fi tools, Notes, Storage, System), Settings and Updates. Pages with real screenshots list them in extra.screens, looked up in data/screens.toml.
  • Facts come from the README, the milestone documents and the Apps' own source (key handlers, labels, the Status Bar's drawing code), not from memory. Some wording was corrected against the source while writing: the Track folder is /gnss/tracks, the reasons a Track won't start, what the Status Bar shows.
  • Not covered: the mesh messenger (planned), the debug console and Debug Builds beyond a pointer to the README. How-tos and the FAQ are phase 3.

Checks (2026-10-06)

Check Result
The Site workflow's own commands, in a clean container with the pinned Zola zola check clean, build and page checks pass
tools/check_site.py 4 pages, 0 problems: titles, descriptions, a language, every image with alt text, every local file referenced exists, nothing loaded from another origin
Browser tests (Chromium, 16 checks) All pass: no request to another origin from the home, Downloads and 404 pages; no horizontal scroll at 1280 and 390 px; a visible focus ring on a notched button; the Install page reads the latest release, shows its version, size and SHA-256, builds a blob manifest naming an ESP32-S3 factory image at offset 0, and loads only git.twis.la; a download on another host is refused; the real server (no CORS header today) makes the page fall back to the esptool steps
The pages looked at Home in dark and light, at desktop and phone width; Install; Downloads
esptool against the real v0.11.0 factory image An ESP32-S3 image, bootloader at 0x0, partition table at 0x8000: flashing at offset 0 is right. The command's syntax was checked, not a flash

Not checked:

  • Flashing a real Cardputer from Chrome. It needs the device on a machine with a browser; the page's flasher logic is tested, the flashing itself isn't.
  • Caddy's headers on the real server (not applied yet), and HTTPS on roro9stack.net (the name resolves, the certificate isn't there).
  • The CI split on a change that touches only site/ or docs/. This pull request touches both the workflows and the site, so it runs both; the first docs-only pull request will show it.
  • Firefox and Safari rendering, screen readers, and a printed page.
  • The unverified wording in the Install text is kept to what's known: nothing about how long flashing takes, or what the screen shows in download mode.

As built (phase 3, how-tos and the FAQ)

  • /howto/ has eight short recipes: when flashing fails, find your files on the SD card, install an update from the card, use a network without DHCP, record a Track, capture LoRa packets for Wireshark, read Gemini pages offline, and what to do when a connection says "not enough memory". /faq/ is one page of questions with a list at the top. Both use the guide's templates (guide-index.html, guide-page.html, now generic: the page's parent section gives the eyebrow, the title and the pager).
  • The FAQ starts from the README and from the problems the project met (Q183): the flash troubles and the memory limit are the two that were hit most. The issues labelled kind/docs turned out to be design rounds for the mesh, not user questions, so they gave nothing to answer.
  • Every step comes from the README, the milestone documents or the Apps' source. The privacy answer says plainly that the device contacts the project's server once a day for the update check (on by default, one switch to turn it off).
  • Linked from the guide's index, not the navigation, which stays short.