Skip to content
vxvx
GitHubBlueskydev.toRSS

Console UI — design

Status: shipped (2026-11-04). Module page: docs/modules/ui.md.

vx’s output was written line by line from many places: verbs, the run screen, plugins. Each picked its own words, colours and layout, so the same fact looked different in vx run, vx why and a plugin’s verb.

One kit draws everything. src/ui holds a fixed set of components; nothing else writes to a stream, prints with console, names a colour or emits an escape. tests/ui-only-output.unsafe.test.ts holds core and every plugin package to it. Plugins draw through the façade’s ui.

The screen is panels stacked top to bottom, written into the scrollback. A panel is a title rule (─ Title ───) and its lines, with no box: a box cut a long line or wrapped it inside its sides, and a copied line carried the │. No input, no alternate screen, no columns side by side, no scrolling regions, no modals: everything stays visible. Only the Running panel at the bottom is redrawn; every other panel is final once written.

The run screen: Tasks (one row per finished task) → failure output (the task’s own bytes, never boxed, so they stay copyable) → Running (live) → Summary. Warnings get their own panel on stderr.

CI looks the same as the terminal: any CI (a truthy CI, or the variables of CIs that set none: TF_BUILD, JENKINS_URL, TEAMCITY_VERSION) gets the same coloured panels, minus only the live redraw of Running. A plain pipe (or NO_COLOR) gets the same layout without colour, so a test or grep reads bytes with no escapes.

  • Shopify cli-kit: a fixed component set as the only output API.
  • Cargo Shell: every status line goes through one writer with one verb column.
  • Ink <Static>: finished rows are written once above a live region that alone is redrawn.

An interactive TUI (alternate screen, keys, scrolling): output must be readable without touching it, and the same in CI.

Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.