docs(rfcs): RFC 0001 proposes the Instrument Panel web UI redesign
Six explored directions, the chosen one (E) worked out in a light and a dark teal palette, with a repository strip previewing every served repository, the ten-repository and the phone case, and the backend it needs (/api/repos). Renderings of all artboards live next to the RFC; AGENTS.md registers docs/rfcs/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@@ -68,6 +68,7 @@ Keep sentences short.
|
||||
- `docs/deployment.md` — running Werkator as a systemd user service behind an existing reverse proxy (`init --systemd` generates the unit).
|
||||
- `docs/werkator-migrationsplan.md` — renaming a running installation from GitTally to Werkator: what the name fallback covers and what has to be moved by hand.
|
||||
- `docs/plan/` — the step-by-step rewrite plan; `docs/plan/README.md` explains how to execute a step, `docs/plan/00-legacy-analysis.md` summarizes the legacy bash script.
|
||||
- `docs/rfcs/` — requests for comments: proposals that are larger than one PR and not yet a decision (an accepted RFC becomes an ADR or a plan step).
|
||||
- `docs/prs/` — one document per pull request; every PR needs one. IMPORTANT: Before opening or finishing a pull request, load the [pr-doc skill](.claude/skills/pr-doc/SKILL.md) and write the PR-doc.
|
||||
|
||||
## Key Architectural Decisions
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
# RFC 0001: Web UI Redesign — the Instrument Panel
|
||||
|
||||
**Status:**
|
||||
- proposed: 2026-09-03
|
||||
- accepted: -
|
||||
- rejected: -
|
||||
|
||||
**Proposal:** The Werkator web UI adopts the **Instrument Panel** direction: a teal palette in a light and a dark mode, IBM Plex typography, a repository strip that previews the state of every served repository, a title hierarchy that names the view first and explains it second, and a tab bar at the foot that becomes the mobile navigation.
|
||||
The architecture does not change: server-rendered Thymeleaf, one `werkator.css`, one hand-written `werkator.js`, JSON polling, no framework, no frontend build pipeline.
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
The current UI is a functional port of the legacy generated pages: a table per view, pill badges, system font, blue links.
|
||||
It is correct and calm, but it looks like every other CI page and gives no hint of the other repositories an instance serves (ADR 0009).
|
||||
The brief for this RFC was "fancy, but serious and trustworthy", with two references from the same author for visual kinship:
|
||||
|
||||
- [werkbaum.javagil.de](https://werkbaum.javagil.de/) — light paper with a fine grid, IBM Plex, a petrol accent, panel labels in small caps.
|
||||
- [javagil.de/vibe-engineering](https://javagil.de/vibe-engineering) — a dark instrument panel: ink and petrol, clay for warnings, monospaced spaced labels, a tab bar at the foot.
|
||||
|
||||
A hard constraint of this RFC is honesty towards the data.
|
||||
The mockups show only what the API delivers today; nothing is invented to make a screen look richer.
|
||||
|
||||
### What the UI Has to Work With
|
||||
|
||||
Per build row (`BuildRowView`, `BuildResultDto`): status, branch name, commit (12-character abbreviation, full id for copying), started at (`yyyy-MM-dd HH:mm`), duration (`m:ss`; a pending build shows its wait time in italics), artifact key with the artifact, permalink and live-log links, and the actions restart and delete (history has no restart).
|
||||
Statuses: `pending`, `running`, `success`, `failed`, `interrupted`, `cancelled`, plus `unknown` for a never-built branch and the client-side `finished` on a card whose build has left the current list.
|
||||
Views: Latest (one build per name), Branches (every origin branch and its latest build), History (all stored builds), Current (running builds with their live log), System (seven metric rows with current/min/max/avg, warn from 80 %, critical from 90 %), the artifact page, and the release notes.
|
||||
Live state: the indicator is `static`, `live` or `error`; the watcher banner reports `watcher stopped`, `origin unreachable` or `poll cycle failed`.
|
||||
Multi-repo: the repository switcher is a server-rendered `<select>` of names; `WatcherState.repositories` already carries a per-repository watcher state that the UI does not show.
|
||||
|
||||
What does **not** exist, and therefore appears in no mockup: a commit subject line, a typical or expected duration, an ETA, a per-branch build history, test counts on a row, and any cross-repository status summary in the API.
|
||||
|
||||
## Considered Options
|
||||
|
||||
Six directions were sketched on a shared design canvas, two rounds of three, all with the same sample rows.
|
||||
|
||||
| Option | Idea | Why | Tradeoff |
|
||||
|---|---|---|---|
|
||||
| A · Quiet Console | Today's design refined: top bar, dot-plus-word statuses, hover actions | Smallest step, everything stays valid | Least distinctive |
|
||||
| B · Mission Board | Health tiles, one card per branch with a history strip, running build with progress | Answers "is everything fine?" at a glance | Needs data the API does not have (history, typical duration) |
|
||||
| C · Ledger | Warm paper, serif masthead, hairline rules, typographic status marks | The most "serious"; reads like a signed record | Leaves the system font, needs its own dark theme |
|
||||
| D · Paper Rail | Werkbaum's paper and grid, a repository rail on the left with per-branch dots | Family resemblance to Werkbaum; other repositories visible | 250 px of table width lost; empty with one repository |
|
||||
| **E · Instrument Panel** | Vibe-Engineering's dark panel, repositories as tiles, tab bar at the foot | Reads like a control room; failures in clay stay serious without alarm | Dark-only as drawn; needs a light palette |
|
||||
| F · Fleet Overview | A new landing page with one panel per repository, ledger typography on paper | One page answers the question for the whole instance | Becomes a list beyond five repositories |
|
||||
|
||||
Round one (A–C) still contained invented data; it is kept on the canvas for the visual ideas only.
|
||||
**E was chosen**, and round three worked out what it lacked: the light mode, the ten-repository case, the title hierarchy, and the phone layout.
|
||||
|
||||
## The Design
|
||||
|
||||
### Palette
|
||||
|
||||
Both modes are CSS custom properties on `:root`, switched by `prefers-color-scheme` as today (`color-scheme: light dark`).
|
||||
Failures use clay, not red, so they stay serious without shouting; the accent is teal in both modes.
|
||||
|
||||
| Token | Dark | Light | Used for |
|
||||
|---|---|---|---|
|
||||
| bg | `#061C1F` | `#EAF4F2` | page ground |
|
||||
| panel | `#0A2A2E` | `#FFFFFF` | tables, cards, chips |
|
||||
| panel-2 | `#0F3A3D` | `#D6ECE8` | the current repository, the active tab |
|
||||
| line | `#17474B` | `#C9DFDB` | borders and rules |
|
||||
| text | `#E4EEEC` | `#0B2B2E` | body text |
|
||||
| text-2 | `#B4CBC8` | `#35595B` | timestamps |
|
||||
| muted | `#7DA19E` | `#5E8583` | labels, footers |
|
||||
| accent | `#5FD3C7` | `#0E8079` | links, success, running, the live indicator |
|
||||
| accent-2 | `#1E9A93` | `#149A90` | underlines, the current repository's border |
|
||||
| clay | `#E09070` | `#B0563B` | failed, error, delete, watcher warnings |
|
||||
| clay-2 | `#C4664A` | `#C4664A` | the border of a failing repository chip |
|
||||
| ghost | `#4A7370` | `#BFD4D1` | cancelled, interrupted, unknown |
|
||||
|
||||
Tinted rows: a running row gets 16 % (dark) or 10 % (light) of accent-2 as background, a failed row 12 % or 10 % of clay-2.
|
||||
The reference's background grid was tried and dropped: it competes with the table, especially in light mode.
|
||||
|
||||
### Typography
|
||||
|
||||
IBM Plex Sans for text, IBM Plex Mono for commits, timestamps, durations and every label.
|
||||
Labels are 10 px Mono, uppercase, letter-spaced 0.12 em, in `muted`; statuses are 11 px Mono uppercase in their status color, each preceded by an 8 px dot (outlined for pending, pulsing for running).
|
||||
Fallback stacks: `"IBM Plex Sans", "Segoe UI", system-ui, sans-serif` and `"IBM Plex Mono", ui-monospace, Consolas, monospace`.
|
||||
Whether Plex is bundled under `static/` or the fallback stack is accepted is an open question below.
|
||||
|
||||
### Anatomy of a Page (desktop)
|
||||
|
||||
1. **Header**, 52 px: logo, `Werkator` with the Gitea repository name in accent, a small label `updated HH:mm:ss`; right: the live indicator as an outlined chip with a pulsing dot, the reload button.
|
||||
2. **Repository strip**: see below.
|
||||
3. **Panel** with the view's title: the view name at 22 px semibold with a 2 px accent-2 underline, followed by a one-line label that explains it (`Latest` — `one build per branch, newest first`; `Branches` — `every origin branch and its latest build`; `History` — `all stored builds, newest first`; `System` — `instance metrics since first start`); on the right a Mono line with the row count, the last poll and the watcher state.
|
||||
4. **Table**, columns as today (Status, Branch, Commit, Started, Duration, Artifacts, Actions), rows 9 px padding on a 1 px `line` rule; copy buttons as outlined 13 px icons; artifact links and actions as stroke icons (no emoji).
|
||||
5. **Footer**: version and copyright left, the navigation as a Mono tab bar in the middle (Latest, Branches, History, System with icons; the active tab in panel-2 with an accent underline), Impressum and Privacy right.
|
||||
|
||||
### The Repository Strip
|
||||
|
||||
The `<select>` switcher is replaced by a strip below the header that shows every served repository with its state, so a failure elsewhere is visible without leaving the page.
|
||||
|
||||
- Up to about three repositories: **tiles** (220 px), each with `current` or `repo` label, the name, one dot per branch in the branch's latest status, a summary line (`6 builds · 1 failed · 1 running`), and the watcher warning in clay when that repository's watcher reports an error.
|
||||
- More repositories: **chips** (30 px), each with one dot for the worst status in the repository, the name, an optional short finding (`1 failed`, `main`, `never built`), and a warning triangle when the watcher reports an error; the current repository has an accent-2 border on panel-2, a failing one a clay-2 border on the clay tint.
|
||||
- Order is *failing first*: the current repository, then failing, running, then green; a summary line above (`10 served · 2 failing · 1 unreachable · 2 running`) and a sort control on the right.
|
||||
- The strip **scrolls**: horizontally on the phone, and on the desktop it wraps to a second row up to about ten repositories and becomes a horizontally scrollable band beyond that, with the failing chips pinned at the front so they never scroll out of view.
|
||||
- Beyond roughly twenty repositories the strip shows only the conspicuous chips (failing, running, unreachable) plus a search field for the rest.
|
||||
- With a single served repository the strip is omitted, as the switcher is today.
|
||||
|
||||
### Phone (below 680 px)
|
||||
|
||||
The existing breakpoint behaviour is kept and restyled: rows become cards with the `data-label` captions, the live indicator collapses to a dot.
|
||||
The header stacks `Werkator` over the repository name; the repository strip scrolls horizontally under its summary line; the panel title keeps its hierarchy; each card carries the status line with the branch, then commit, started and duration, then the artifact icons and the actions as 44 px targets.
|
||||
The footer tab bar becomes a fixed bottom tab bar with icons — the same four entries as on the desktop.
|
||||
No painted status bar or keyboard; the device provides those.
|
||||
|
||||
### What Is Deliberately Not in the Proposal
|
||||
|
||||
- The `DE` language button in the mockups is a leftover of the reference; the UI stays English-only.
|
||||
- No commit subjects, typical durations, ETAs or history strips: they need data the server does not have, and each would be its own RFC with its own storage.
|
||||
- No manual theme toggle; `prefers-color-scheme` decides, as today.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Backend
|
||||
|
||||
- One new endpoint, `GET /api/repos`: for every served repository its name, its UI root (`/repos/<name>`), whether it is the current one, the latest status per build name (the Latest view's `latestPerName` reduced to counts, plus the worst status), and its `RepoWatcherState` (`lastFetchError`, `lastPollError`, `lastPollAt`).
|
||||
With one served repository the endpoint returns a list of one and the strip stays hidden.
|
||||
- `werkator.js` polls it on the table interval (10 s) and renders the strip; every fetch keeps the timeout and the explicit error badge.
|
||||
- `UiFormats` and `werkator.js` keep producing identical formats; the palette and the title labels are template and CSS only.
|
||||
|
||||
### Rollout, One Concern per Pull Request
|
||||
|
||||
1. Palette, typography and the title hierarchy in `werkator.css` and the fragments — no data change, both modes.
|
||||
2. Header and footer tab bar, including the phone tab bar.
|
||||
3. `GET /api/repos` and the repository strip, replacing the `<select>`.
|
||||
4. Card refinements on the phone and the System and artifact pages in the new vocabulary.
|
||||
|
||||
Each step leaves the UI usable, and the tests in `server` that assert on markup are adjusted with the step that changes it.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **Fonts:** bundle IBM Plex Sans and Mono under `static/fonts/` (about 100–150 KB in WOFF2 for the four faces), or accept the fallback stack on hosts without the font; the reference sites load Plex from a CDN, which the deployment behind a strict reverse proxy may not want.
|
||||
- **Current view:** it is reachable today only from a running row's live icon; the tab bar has room for it as a fifth entry with a count badge, or it stays a link from the row.
|
||||
- **Instance pages:** `/system` and `/releases` are instance-level; in the tab bar they sit next to the per-repository views, which the strip makes visible enough, or they move to the footer's right side.
|
||||
|
||||
## Design Sources
|
||||
|
||||
The design canvas with all eleven artboards (rounds one to three, desktop and phone) is a private Claude artifact of the author; its renderings live next to this RFC under `0001-web-ui-instrument-panel/`.
|
||||
The sample rows are real field shapes with invented values; the repositories other than `werkator` are invented.
|
||||
|
||||
The proposal:
|
||||
|
||||
- [E · dark, desktop](0001-web-ui-instrument-panel/e-dark-desktop.png) · [E · light, desktop](0001-web-ui-instrument-panel/e-light-desktop.png)
|
||||
- [E · dark, ten repositories](0001-web-ui-instrument-panel/e-dark-10-repos.png) · [E · light, ten repositories](0001-web-ui-instrument-panel/e-light-10-repos.png)
|
||||
- [E · dark, phone](0001-web-ui-instrument-panel/e-dark-phone.png) · [E · light, phone](0001-web-ui-instrument-panel/e-light-phone.png)
|
||||
|
||||
The alternatives, for the record:
|
||||
|
||||
- [A · Quiet Console](0001-web-ui-instrument-panel/a-quiet-console.png), [B · Mission Board](0001-web-ui-instrument-panel/b-mission-board.png), [C · Ledger](0001-web-ui-instrument-panel/c-ledger.png) — round one, still with invented data.
|
||||
- [D · Paper Rail](0001-web-ui-instrument-panel/d-paper-rail.png), [F · Fleet Overview](0001-web-ui-instrument-panel/f-fleet-overview.png) — round two.
|
||||
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 171 KiB |