Skip to content

fix(tui): correct the v5 UI where real use disagreed with the mock#150

Merged
fullstackjam merged 2 commits into
mainfrom
fix/palette-terminal-relative
Jul 17, 2026
Merged

fix(tui): correct the v5 UI where real use disagreed with the mock#150
fullstackjam merged 2 commits into
mainfrom
fix/palette-terminal-relative

Conversation

@fullstackjam

@fullstackjam fullstackjam commented Jul 17, 2026

Copy link
Copy Markdown
Member

Two corrections from actually running v0.65.0 in a VM. Both come from the same root cause: the Redesign v5 was drawn as an app against a mock, and openboot is a command that runs in someone else's terminal.

1. The palette assumed a background it didn't have

Reported: on Terminal.app with a translucent background over a wallpaper, pending pipeline rows, sidebar counts, the version and the status-bar key hints were invisible.

Measured, against that terminal's effective background (~#2b4247):

token hex vs the #08080a mock vs the real terminal
cDim3 #3f3f46 1.9:1 1.0:1
cDim4 #3a3a41 1.8:1 1.1:1
cFaint #2e2e34 1.5:1 1.3:1
cDim2 #52525b 2.6:1 1.4:1

1.0:1 is the background's own luminance — not "hard to read", impossible to render. Six of twelve ramp tokens were unreadable there.

Worth recording because it was the first instinct: reverting to the v0.63 UI would not have fixed this. v0.63's own greys measure 1.1:1 (#444), 1.4:1 (#555), 1.9:1 (#666) on the same terminal. It survived by leaving most text at the terminal's default foreground.

So the text ramp is now the terminal's own ANSI palette — 15 emphasis / 7 body and anything load-bearing / 8 decorative — which the user's theme guarantees against the background they actually chose. Brand hues stay hex (4.4–6.1:1 there). Pipeline rows keep a readable label with the glyph carrying the state; hover uses reverse video instead of a background colour we'd have had to guess.

2. The install screen destroyed its own output

The v5 redesign unified the scattered planning prompts into one full-screen flow — right, and kept. It also made the apply full-screen — wrong. Measured: 24 lines of install detail on screen, and after the program exits, a blank terminal. The footer's "everything is logged to ~/.openboot/logs" wasn't a convenience; it was the design apologising for throwing the record away.

The wizard now stops at the plan. Confirm returns the reviewed InstallPlan, the alt-screen tears down, and the CLI applies it on the normal terminal via installer.ApplyReviewedPlanConsoleReporter plus the sticky progress bar, which pins the current package to the bottom while results scroll into the scrollback. Live progress and a permanent record.

What fell out, all of it alt-screen workarounds:

  • plan.Silent is no longer forced on — it had gated out the npm-retry confirm and the screen-recording reminder as collateral
  • post-install runs in place as Step 8 with its own preview + confirm (splitPostInstall / RunPostInstallAfterTUI gone)
  • the "What was installed" breakdown is back — the wizard's log dropped Header/Info lines, so it had never been visible there
  • RunPipeline, PhasesForPlan, PlanForConfig and the progress-event bridge (~650 lines) are gone with the screen they fed; the wizard no longer imports the install engine

StickyProgress keeps the abort lifecycle the wizard had earned rather than reverting to os.Exit(130) from a signal goroutine: first ctrl+c stops the bar and lets the cancelled context unwind (ApplyContext gates every remaining step on it), second one force-quits — which must stay reachable, since signal.NotifyContext swallows later SIGINTs once fired.

Testing

  • go vet ./... passes
  • Relevant tests added or updated — TestTextRampIsTerminalRelative pins the ramp to ANSI indices; TestHoverUsesReverseVideo; TestStickyProgressInterruptIsTwoStage; TestConfigModeConfirmReturnsPlanWithoutInstalling. Install-screen tests were removed with the screen.
  • Tested locally — full L1 green (25 packages incl. archtest); golangci-lint v2.11.4 clean; on a real pty: emitted codes are now 37m/90m/32m for text; and after a wizard-confirmed run the banner, the per-step output, the failure and the completion summary are all still on screen once the shell returns (verified with brew off $PATH so nothing installed)

Cross-repo checklist

  • Docs/content update in openboot.dev? — worth a look: the install-page copy describing the slug flow
  • CLI ↔ server API contract change? — no

Notes for reviewer

The rule both halves encode, now in CLAUDE.md: we don't own the terminal. Don't hardcode a colour that has to contrast with a background we can't see, and don't take over a screen whose scrollback is the user's record. A TUI is right for browsing a 100+ package catalog; it's wrong for a 20-minute apply.

The Redesign v5 tokens were a 10-step hex grey ramp drawn against a
#08080a mock and hardcoded. Measured against a translucent terminal
(effective background lifted to ~#2b4247 by the wallpaper behind it),
the bottom half of that ramp collapsed into the background:

  cDim3 #3f3f46 -> 1.0:1   cDim2 #52525b -> 1.4:1
  cDim4 #3a3a41 -> 1.1:1   cFaint #2e2e34 -> 1.3:1

1.0:1 is the background's own luminance — no terminal can draw that as
visible text. Pending pipeline rows, sidebar counts and the status-bar
key hints were simply gone. (Rolling back would not have helped: v0.63's
own greys measure 1.1:1 / 1.4:1 / 1.9:1 on the same terminal. It only
looked better because it left most text at the terminal's default
foreground — which is exactly the property being restored here.)

The text ramp is now the terminal's own ANSI palette (15 emphasis /
7 body / 8 decorative), which the user's theme guarantees is legible
against the background they actually chose. Brand and status hues stay
hex — they measure 4.4-6.1:1 on the same terminal and they're the
product's identity.

Two follow-ons from the same rule:
- pipeline rows keep a readable label; the glyph and its hue carry the
  state. A label faded toward the background reads as missing, not as
  pending.
- hover uses reverse video instead of a painted background. Any colour
  we picked would be a guess about theirs; the design's #3d3d4a sat at
  1.2:1, so the highlight was a no-op exactly where it was needed.

A test pins the ramp to ANSI indices so a hex grey can't creep back in.
@github-actions github-actions Bot added tests Tests only ui Terminal UI labels Jul 17, 2026
The v5 redesign unified the scattered planning prompts into one
full-screen flow — that part was right, and stays. It then carried on and
made the *apply* full-screen too, which was wrong: an alt-screen install
takes its own output with it when it exits.

Measured on a real run: 24 lines of live install detail on screen, and
after the program exits, a blank terminal. Twenty minutes and forty-eight
packages, and the only remaining trace of which three failed is a log file.
The footer's "everything is logged to ~/.openboot/logs" was not a
convenience — it was the design apologising for having thrown the record
away. openboot is a command you run once per machine, not an app you live
in; its output belongs in the scrollback, where it can be scrolled back,
copied into an issue, and piped.

So the wizard now stops at the plan. Confirm returns the reviewed
InstallPlan, the alt-screen tears down, and the CLI applies it via
installer.ApplyReviewedPlan on the normal terminal — ConsoleReporter and
the sticky progress bar, which pins the current package to the bottom
while results scroll past above it. Live progress and a permanent record,
which is what the pre-v5 flow got right.

Falling out of it, all of which were alt-screen workarounds:

- plan.Silent is no longer forced on. It only existed to keep huh prompts
  from painting over the TUI, and it gated out the npm-retry confirm and
  the screen-recording reminder as collateral. They work inline again.
- post-install runs in place as Step 8 with its own preview and confirm.
  splitPostInstall / RunPostInstallAfterTUI are gone.
- ShowScreenRecordingReminderAfterTUI is gone: showCompletionFromPlan
  already does it, and now it's reached.
- the "What was installed" breakdown is back. The wizard's log dropped
  Header/Info lines, so it had never been visible there.
- RunPipeline, PhasesForPlan, PlanForConfig and the whole progress-event
  bridge (~650 lines) are gone with the screen they fed. The wizard no
  longer imports the install engine at all — only brew/npm's read-only
  probes for the boot scan.

StickyProgress keeps the abort lifecycle the wizard had earned, instead of
reverting to its old os.Exit(130) from a signal goroutine: the first ctrl+c
stops the bar and lets the cancelled context unwind the install (ApplyContext
gates every remaining step on it), and only a second one force-quits — which
must stay reachable, because signal.NotifyContext swallows later SIGINTs
once it has fired.
@fullstackjam fullstackjam changed the title fix(tui): make the wizard palette follow the terminal, not a design mock fix(tui): correct the v5 UI where real use disagreed with the mock Jul 17, 2026
@github-actions github-actions Bot added installer Package installation logic docs labels Jul 17, 2026
@fullstackjam
fullstackjam merged commit 8dc081c into main Jul 17, 2026
14 checks passed
@fullstackjam
fullstackjam deleted the fix/palette-terminal-relative branch July 17, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs installer Package installation logic tests Tests only ui Terminal UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant