Launcher grid with icons, themes, Settings in groups, the website's colours #97

Merged
twisla merged 6 commits from launcher-grid into main 2026-10-09 22:25:50 +00:00
6 Commits
Author SHA1 Message Date
twislaandClaude Opus 5.5 0c52613b72 Settings: its rows in five groups
Site / build (pull_request) Successful in 10s
CI / build (pull_request) Successful in 2m22s
Settings had grown to 21 rows in one list. It opens on five groups (This
device, Display, GNSS and radio, Network, System); Enter opens one, Back
returns to the groups, and Back from the groups leaves Settings.

SettingsMenu holds the groups and which one is open (host-tested: every
setting is in exactly one group). The guide, the how-tos, the README,
the scripts' messages and the firmware's own name the new paths, such as
"Settings > System > Firmware".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2026-10-10 00:11:52 +02:00
twislaandClaude Opus 5.5 37335b4829 UI: seven themes, each dark and light, chosen in the Settings
Settings > Theme and Settings > Light or dark pick the colours of every
screen (issue #10): roro9stack (the website's, the default), Catppuccin,
Dracula, Nord, ANSI terminal, Gruvbox and Solarized.

- A palette is 14 roles (lib/ui/src/palette.h). theme::kText and the
  others are now references into the one in use, so the Apps are
  unchanged; the main loop applies it when a Setting changes.
- Every colour is one the RGB332 frame buffer holds exactly: the themes'
  own were rounded, and those that fell together or lost contrast were
  picked by hand. test_palette checks exactness and 18 contrast ratios
  for each palette.
- The Sky view's constellation colours come from the theme.
- `theme [0-6] [light|dark]` on the consoles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2026-10-10 00:11:52 +02:00
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
twislaandClaude Opus 5.5 e2326e113b Launcher: a grid of icons, with badges; the list stays as a Setting
The Launcher shows the Apps as a 4 x 3 grid of 32 x 32 icons, the
selected one's name under it (issue #9). The icons are the website's
own, doubled, and two drawn the same way for SSH and the Shell; they
are PNGs in assets/icons, turned into arrays by scripts/make_icons.py,
which CI checks. The selected tile is the website's: dark blue on cyan,
with notched corners.

- GridModel (lib/ui, host-tested) holds the selection and the scrolling.
- App::badge(): a dot on the tile for unread IRC messages, a Track
  recording, a Capture running, an SSH session open.
- Settings > Launcher: Grid or List.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2026-10-09 23:14:37 +02:00
twislaandClaude Opus 5.5 aadfd8cb37 UI: the website's colours, on every screen
The theme takes the website's palette, each a colour the RGB332 frame
buffer holds exactly: cyan for the accent, a selected row and a chosen
button in cyan with dark blue text, a slate Status Bar (the grey it
asked for was shown as a dull olive), the website's orange for
warnings and its pink for messages. "Good" (a Fix, the quietest
channel) keeps a green of its own, which the website doesn't have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2026-10-09 23:14:37 +02:00
twislaandClaude Opus 5.5 b1c80c912f SSH App: its messages were cleared the moment they were set
The bottom-line message (a malformed address, not enough memory, a
session still closing) is timed from millis() when a key sets it, and
update() compared that with the pass's own time, read before the keys
were handled. The unsigned difference wrapped, so the message was
cleared before it was ever drawn: Enter on a new connection that was
refused looked like it did nothing.

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