aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/superpowers/specs/2026-10-20-sb-builder-ui-design.md162
1 files changed, 162 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-20-sb-builder-ui-design.md b/docs/superpowers/specs/2026-10-20-sb-builder-ui-design.md
new file mode 100644
index 0000000..7c7b254
--- /dev/null
+++ b/docs/superpowers/specs/2026-10-20-sb-builder-ui-design.md
@@ -0,0 +1,162 @@
+# sb-builder: one UI frame for every tab
+
+Date: 2026-10-20
+Status: approved
+Builds on: phases 1, 2 and 4 (tabs, Jobs, Packages, Results, Hints)
+
+## Goal
+
+Every tab looks and works the same, and a multi-step task (check upstream,
+update hints, preview, build) never sends the user hunting for the next
+button in another place. Rules go into AGENTS.md so later tabs follow them.
+
+## What is wrong today
+
+- Action buttons sit at the bottom in Packages and Hints, in a side pane in
+ Results, at the top in Jobs.
+- The ribbon opens tabs; tabs open and close.
+- A step's follow-up lives elsewhere: Packages action -> auto switch to Jobs
+ -> Run it at the top of Jobs. Hints: Check upstream (bottom) -> tick ->
+ Update selected (bottom) -> diff dialog -> Build these -> Jobs -> Run it
+ (top).
+- The Jobs Start menu name says nothing of what it holds.
+
+## Window frame
+
+Top to bottom: main ribbon, small ribbon, tab bar, the tab's view (its
+filter bar first), status bar.
+
+- **Permanent tabs**: Dashboard (default), Packages, Hints, Results, Jobs,
+ created at startup, not closable. Each loads its data the first time it is
+ shown (Packages, Hints and Results call `inventory`/`results` then, not at
+ startup). Navigation: tab bar, Ctrl+PgUp/PgDn, Dashboard tiles (Packages,
+ Recent jobs, slackrepo results as today). The ribbon does not open tabs.
+ `File` menu unchanged.
+- **Main ribbon**: `QToolBar`, 32 px icons, text under icon, the current
+ tab's actions in workflow order. Refilled on tab change.
+- **Small ribbon**: `QToolBar` right under it, 16 px icons, text beside
+ icon, the tab's view controls. Hidden when the tab has none.
+- Each tab exposes `actions` and `view_actions`, lists of `QAction` it owns,
+ so enabled and checked state survive tab switches. Icons are freedesktop
+ theme names with a `QStyle` fallback, as the ribbon does now.
+- An action that does not apply is disabled with a tooltip saying why;
+ never hidden, so the ribbon does not shift.
+- Filter bars (search, chips, `N shown · M selected` or `N of M`) stay at
+ the top of the view; their Refresh buttons move to the small ribbon.
+
+## Selection
+
+- Change actions act on the **ticked** rows; view actions (Show build log)
+ on the **highlighted** row.
+- A click anywhere on a row toggles its tick (done for Packages and Hints).
+- Ctrl+A ticks the visible rows, Ctrl+Shift+A unticks all (done); the
+ small ribbon's Select visible and Clear carry the same shortcuts.
+
+## Ribbons per tab
+
+| Tab | Main ribbon | Small ribbon |
+|---|---|---|
+| Dashboard | Refresh | none |
+| Packages | Build, Update, Rebuild, Remove, Update all, New hint, Publish | Select visible, Clear, Refresh |
+| Hints | Check upstream, Update selected, Delete hint | Select visible, Clear, No download (checkable), Refresh |
+| Results | Show build log | Refresh |
+| Jobs | Pull overlay (menu: one entry per overlay), Check shadowing, Run it, Kill | none |
+
+Disabled reasons (tooltips):
+
+- Build, Update, Rebuild, Remove: "tick packages first".
+- New hint: "none of the ticked packages is without a hint".
+- Update selected: "tick outdated packages after Check upstream".
+- Delete hint: "none of the ticked packages has a hint".
+- Show build log: "select a result with a log".
+- Run it: "select a finished preview".
+- Kill: "no job running".
+- Check upstream while running: "checking…".
+
+Update all, Publish, Pull overlay and Check shadowing are always enabled
+(the helper refuses a second job with the busy answer, as today).
+
+Packages' detail pane keeps **Open in Hints** as a link (navigation, not an
+action). Its New hint button moves to the ribbon.
+
+### New hint on ticked packages
+
+`hint-new` takes one or more packages and no version: `hint-new <pkg>...
+[--no-dl]`. Each gets the `.info` pin; Update selected from upstream sets a
+version later. The GUI sends no `--no-dl` here (Update selected has the
+toggle); the helper keeps the flag. Every package must be without a hint, else the job is
+refused before it starts, as today for one. Helper `VERSION` 0.6.0 in both
+files, VM redeploy. The New hint dialog goes away.
+
+## Next-step dialogs
+
+- Shown only for jobs started from this GUI session: the main window keeps
+ the ids `job-start` returned. Jobs seen on reconnect or started elsewhere
+ only notify, as today.
+- Non-modal, one per job, never reopened by polling.
+- **Preview ended ok** (`slackrepo` with `--preview`): dialog titled
+ `Preview: <kind and args without --preview>`, with the whole job log
+ (`job-follow` from offset 0; the job has ended, so the stream closes by
+ itself), a **publish** checkbox (default `builds.publish`), **Run it**
+ (default button) and **Not now**. Run it goes through the signing-key
+ check (`start_signed`) and starts the real run.
+- **sbpull or sbshadow preview ended ok with a shadow plan** (`shadow:` or
+ `unshadow:` lines, as `SHADOW_PLAN` today): same dialog, **Apply
+ shadowing** (`sbshadow --yes`, after a confirm: it changes the repo) and
+ **Not now**. Without a plan, no dialog.
+- **Hint job ended** (`hint-update`, `hint-new`) or a delete: the existing
+ diff dialog; Build these starts `slackrepo update --preview`, whose end
+ opens the preview dialog.
+- A real run ending, a failed or killed job: notification only.
+- **Run it** in the Jobs ribbon, with a finished preview selected, reopens
+ that preview's dialog (also for a shadowing preview), so Not now loses
+ nothing. The Jobs top bar (Start, Apply shadowing, Run it, publish, Kill)
+ goes away.
+
+The hugo flow becomes: Hints, Check upstream -> Update selected -> diff
+dialog, Build these -> preview dialog, Run it. No tab switch.
+
+## Jobs tab signal
+
+- Starting a job no longer switches to Jobs; the user stays on the tab.
+- While a job runs, the Jobs tab shows a running icon (`system-run`) and
+ accent-colored text (palette highlight).
+- When it ends, the icon becomes the end status (`✔` ok, `✘` failed,
+ killed, lost) and stays until the Jobs tab is shown, then clears.
+- The status bar job button stays.
+
+## AGENTS.md: UI guidelines
+
+A new section, binding for every tab:
+
+1. Tabs are permanent; each loads lazily on first show.
+2. Main ribbon: the tab's actions in workflow order. Small ribbon: view
+ controls. Nothing actionable lives inside a view except navigation links.
+3. Change actions act on ticked rows, view actions on the highlighted row.
+ A row click toggles its tick; Ctrl+A / Ctrl+Shift+A select visible /
+ clear.
+4. An inapplicable action is disabled with a tooltip saying why, never
+ hidden.
+5. When a job this session started ends, its next step opens as a
+ non-modal dialog where the user is; the Jobs tab shows activity; a
+ dismissed step can be taken again from the ribbon.
+6. A modal confirm only for destructive or outward steps (delete hint,
+ publish, apply shadowing).
+
+## Testing
+
+`test_gui.py` with the fake remote, offscreen:
+
+- ribbon contents per tab (action texts in order) after switching tabs;
+- disabled states and tooltips (nothing ticked, no preview selected, no job
+ running);
+- tabs are not closable and present from the start; data loaded on first
+ show only (no `inventory` call before Packages or Hints is shown);
+- a preview this session started ends -> dialog -> Run it starts the real
+ run through the signing check; Not now, then Jobs' Run it reopens it;
+- a preview not started by this session ends -> no dialog;
+- Jobs tab icon running -> end status -> cleared on showing Jobs;
+- New hint on two ticked packages -> `hint-new a b`.
+
+`test_remote.py`: `hint-new` with several packages, refusing a version and
+a package that has a hint.