Files
roro9stack/docs/milestones/U1.md
T
twislaandClaude Opus 5.5 10627e6e18 Launcher: the selected icon in slate, the name in white
The selected icon was dark blue on its cyan tile, which read as black:
it is the Status Bar's slate now. The App's name under the grid is
white instead of cyan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2026-10-09 23:23:20 +02:00

10 KiB
Raw Blame History

U1 — Look and feel

Status: in progress. Shipped: the help key (issue #69) and the key tables the website shares with it (#72), both in v0.13.0; the screenshot key (#83, v0.17.0). The Launcher as a grid of icons (#9) is done, not released yet. Not started: screen recording (#17), themes (#10).

Goal: the interface is consistent and uncrowded: the same thing is done the same way on every screen, and the 135 pixels of height go to content.

The help key (issue #69)

Every screen used to say something about its keys, differently: a footer of abbreviations in one place (c x v:paste r:name d:del n:new i:info s:sort), a line under a text field in another (Enter: save \: cancel), Tab: sky` in a corner, and nothing at all in several. About 30 such strings, each costing a line of a small screen, and none of them complete.

Decisions (design round 2026-10-07)

# Decision
Q196 Fn+h, on every screen, text fields included (Fn is held, so nothing is typed). ? too, outside Text Entry.
Q197 It opens a panel over the content area, titled with where you are: the screen's own keys, then an "Everywhere" group (Back, Home, the arrows, the help key). The arrows scroll it; any other key closes it and is not passed on.
Q198 Each App answers "what are your keys right now?" for the state it is in; pages, viewers, dialogs and text fields answer for themselves, with shared lists for dialogs, lists and text entry. The lists are constants; the panel's rows exist only while it is open.
Q199 Every hint that names a key goes, text fields included. What stays is state: REC 12 points, LOG 42, sort:signal, typing/saved, what is waiting to be pasted, the Sweep's floor. Messages were reworded where they named a key ("v pastes a copy of…" is "Copied …: paste it where you like").
Q200 The first-start Setup keeps its hints, and is the one place that does: someone in their first minute doesn't know the help key yet. It tells them about it on its first and last screens.
Q201 Loud everywhere else: the user guide opens with it, the FAQ has it first, and a device set up before this firmware gets one Toast, once: "Fn+h: the keys of any screen".
Q202 key help over the consoles. Generating the website's key tables from the same lists is a follow-up, not this issue.
Q203 The key and the panel first, host-tested; then one App at a time, declaring its keys and losing its hints in the same step; then every screen looked at on the device.

As built

  • Key::Help from the key mapper: Fn+h in both modes, ? only outside Text Entry (lib/input, 3 tests).
  • App::help() and App::helpTitle() (lib/core/src/app.h), KeyHelp rows and HelpModel (key_help.h). The App manager opens the panel, appends the "Everywhere" group, and while it is open takes every key: nothing reaches the App, Home included. It closes when the App changes (5 tests).
  • Every App declares its keys by state: the Launcher, IRC (chat, settings, a field), Wi-Fi Tools (4 views), GNSS, Gemini (page, saved page, address, answer, dialogs), the LoRa Scanner (4 views), Storage (browse, details, a name, the viewer's 6 modes, the editor, Maintenance, busy), Notes (list, editor, a file name), System (5 views), Settings (menu, text, choice, and the Wi-Fi, Firmware and Debug Console pages with their own states), Setup and the widget demo.
  • The hints are gone from all of them. The footers that remain say state only.
  • It costs 8.5 KB of flash and 40 bytes of static RAM.

Checks

Check Result
Host tests 476 pass (468 before)
On the device, key help and a screenshot The Launcher and all nine Apps, and these states: Storage scrolled, System's tasks, a Settings text field, Wi-Fi Tools' networks. The panel is titled with the scope, lists the right keys, scrolls, and closes on Tab
The one-time Toast notification: Fn+h: the keys of any screen on the first start after the update

One source for the device and the website (issue #72)

The lists first lived in each App's help(), as code. They are now data, in one file: lib/core/src/app_keys.h, 52 constant tables, each under a comment // id: Title. An App's help() picks the table of the state it is in. site/tools/gen_dev_docs.py reads the same file and writes site/data/keys.toml; the keys shortcode shows a screen's tables on its guide page, and /guide/keys/ shows all of them. The Site job fails when the data file is out of date, or when a page asks for a table that doesn't exist, and it now runs when app_keys.h changes. A key added to an App shows up on the website without anyone editing a page.

Three rows lost their second wording on the way, since a table is constant: GNSS's Tab and r, and the Scanner's c, now say both things they do ("record a Track, or stop it") instead of the one that applies. The guide pages keep their written tables too, where they say more than a key list can; those can still drift, and the generated ones under them are the reference.

Not checked: the real Fn+h and ? on the keyboard (the mapper is host-tested; the device was driven with key help); the Setup screens, which only a device that was never set up shows, so their new text has not been seen on a screen; and the states that need something to happen first (a dialog, a copy in progress, a Gemini prompt, a packet's details): their lists were read against the key handling, not looked at.

The screenshot key (issue #83)

A screenshot could only be taken by typing screenshot in the Shell, where "now" is a picture of the Shell.

  • Fn+p, on every screen, text fields included, saves the screen as it is (a dialog, the help panel or a Toast if one is showing) as a PNG in /screenshots, the way the Shell's command does. A Toast says so once the file is written, so it is never in the picture.
  • It never reaches an App. Key::Screenshot comes out of the key mapper and is handled before the App manager, so the help panel stays open and a dialog keeps its selection.
  • Not on Settings > Debug Console: that page shows the token, and a picture of it is a copy of the token in a file. App::showsSecret() says so, and the key answers with a Toast instead.
  • No card: a Toast says so.
  • No setting to switch it off: Fn with a letter isn't pressed by accident.
  • It is in the "Everywhere" group of the help panel, and so in the website's key tables. key shot presses it over the consoles.

Checked on the device (2026-10-07, with key shot): in the Launcher, a 33,383-byte PNG appears in /screenshots and is the Launcher; with the help panel open, the picture is of the panel (which now lists Fn p) and the panel stays open; on Settings > Debug Console, no file is written; back on the Settings list, one is. The test pictures were removed.

Not checked: the real Fn+p on the keyboard (the mapper is host-tested); the two Toasts that refuse, which weren't looked at (one of them is on the page that mustn't be photographed); a device with no card.

The Launcher as a grid (issue #9)

The Launcher was a list of names. It is now a grid of icons.

Question in the issue Decided
Layout 4 × 3 tiles of 60 × 36, so the 11 Apps are on one page. Only the selected App's name is written, on a line under the grid: a name under every tile doesn't fit 60 pixels ("LoRa Scanner"). More than 12 Apps: the rows scroll
Icons The website's own: its nine 16 × 16 pictures (site/static/img/icons-dark.svg), doubled to 32 × 32, and two drawn the same way for SSH and the Shell. 1 bit a pixel (128 bytes each, in flash). The pictures are PNGs in assets/icons/, named after the App's id; scripts/make_icons.py writes src/ui/icons.cpp from them and CI checks the two match
Colours The website's: cyan (#00dbff) on black, and the selected tile slate (#494955, the Status Bar's) on a cyan fill with notched corners. Both are RGB332 colours. The name under the grid is white
Live state A dot on the tile, from App::badge(): IRC (unread messages), GNSS (a Track recording), the LoRa Scanner (a Capture running), SSH (a session open). The Launcher asks every App on each pass and redraws when an answer changes
Order As registered, as before
Shortcuts None for now
The list Kept: Settings > Launcher, Grid or List. Both keep the same selection
  • GridModel (lib/ui) holds the selection and the scrolling, host-tested: left and right go through every item and wrap; up and down stay in the column, and land on the last item where the last row is short.
  • An App registered without an icon shows its first letter in a frame.
  • No animation: not asked for, and not measured.

The website's colours, on every screen

Every screen takes its colours from src/ui/theme.h, so the palette changed there, to the website's (site/static/css/site.css, dark side). Each is a colour the RGB332 frame buffer holds exactly.

Was Is
Accent teal-blue cyan #00dbff
A selected row, a dialog's chosen button darker teal, white text cyan, dark blue text (#000055)
Status Bar #202020, which the frame buffer showed as a dull olive slate #494955; what is idle on it in light grey #b6b6aa
Muted text grey, as the frame buffer rounded it the same grey, exact: #9292aa
Warning orange the website's orange #ff9200
Someone wrote (unread count, a mention, a message Toast) green pink #ff92ff
Good (a Fix, the quietest channel, a live task) the same green green #49db55, the one colour that isn't the website's: it has no green
Text on a Toast black dark blue

Looked at on the device, by screenshot: the Launcher, IRC, Wi-Fi Tools (menu, channel occupancy), GNSS (position, sky), Gemini, the LoRa Scanner (Sniffer, Sweep, presets), Storage, Notes, SSH, the Shell, System, Settings and the help panel. Not looked at: dialogs, a warning or a message Toast, the SSH terminal (which keeps its own 16 ANSI colours), the Setup screens.