Progress indicators
The progress package covers the three shapes of “still working”: a spinner when you can’t measure, a progress bar when you can, and a multi-bar when several things run at once. All three render on the UI engine’s live region — diffed repaints, resize-aware — and animations auto-disable when output isn’t a TTY.
In a command, context.progress() returns an instance pre-wired to the command’s stdout, io, arena allocator, and theme:
// Pre-wired to the command's stdout, io, allocator, and theme.
const p = context.progress();
var spinner = try p.spinner(.{ .style = .dots });
spinner.start("Connecting to server...");
spinner.succeed("Synced successfully"); // or .fail() / .warn() / .info()
var bar = try p.progressBar(.{ .total = items.len, .show_eta = true });
for (items, 0..) |item, i| { process(item); bar.update(i + 1, null); }
Spinners
A spinner animates on its own — no tick loop in your command — and finishes by replacing itself with a result line:
start(msg)begins the animation with a messagesucceed(msg)/fail(msg)/warn(msg)/info(msg)stop it with the matching symbol (✔ ✖ ⚠ ℹ, ASCII fallbacks off-unicode), styled through the palette’ssuccess/err/warning/inforoles
Nine styles: dots (default), dots2, dots3, line, arrow, bounce, clock, moon, simple.
Progress bars
For a known total: update(current, message) advances the bar (with an optional message swap), finish() emits the final state as a static line. show_eta adds an estimated-time-remaining readout.
Multi-bars
For parallel work — downloads, builds, uploads — a multi-bar stacks one labeled bar per task:
var mb = try p.multiBar(.{});
const a = try mb.add("api", 100);
const b = try mb.add("web", 100);
// from your work loop:
mb.set(a, 42);
mb.set(b, 87);
mb.finish();
Labels align, each row shows its own percentage, and the whole block repaints as one frame.
Piped output
Off a TTY there is nothing to animate, so the indicators print what a log wants instead: a spinner prints its status line once, a bar stays silent until a single completion line, and escape codes never reach the file. Same code, clean logs.
Theming
Spinner glyphs and bar fill/track render through the theme’s progress tokens (spinner, bar_fill, bar_empty — accent and muted by default), and result symbols use the palette roles — so a rebrand in zcli_theme restyles every indicator at once.
Standalone use
Like every zcli package, progress works without the framework: construct a Progress with your own writer, io, allocator, and theme context. For progress rendered inside a larger live layout — a status frame with a spinner, task list, and gauges — use the UI engine’s progress widgets directly.