ArrTop::Render
Pure, I/O-free rendering helpers for the TUI. Every function here takes plain
data (and, where colour is wanted, a Theme) and returns a String; nothing
touches the terminal, the clock, or the network, so the whole module is
unit-testable offline. The TUI loop is the only thing that turns these
strings into terminal writes.
ALIGNMENT CONTRACT: every column/box helper lays out on plain text and
applies colour only after the visible width is fixed (see Theme#colorize,
which never changes visible width). So with a disabled theme each helper
returns a string whose .size is exactly the width it was asked for — the
property the specs assert to guarantee the right border lines up.
Constants
Max cells inside a progress bar's brackets. The bar shrinks below this to fit a narrow terminal; it never grows past it.
Double-line box-drawing pieces (U+2550–U+2563).
Byte-size unit ladder (base 1024).
Column header labels.
Widest a whole progress cell (bar + space + percent) is allowed to get:
a full BAR_WIDTH bar (+2 brackets) + a space + the percent.
Fixed column widths (visible cells). Movie/Torrent are hard limits — their text is truncated to fit, never allowed to widen the layout.
Visible width of the percent readout, e.g. " 50.0%" / "100.0%".
Human-readable status label per State (also used in the queue summary).
Class methods
A bracketed two-tone block bar filled to percent (clamped 0–100) with
exactly width cells between the brackets: [ + filled cells + remaining
cells + ]. Both cell glyphs come from the theme (both default to █),
so the bar is a solid run distinguished only by colour — filled in the
theme's filled colour, the remainder in its remaining colour, the brackets
in the bracket colour. Filled count = round(pct/100 · width), clamped to
[0, width]. With a disabled theme the glyphs are emitted bare, so the
visible width is always width + 2. width <= 0 yields [].
The ╠═…═╣ divider between the header labels and the data rows, cols wide.
The column-header label row, aligned to the same columns as render_row,
exactly width visible cells wide.
Formats a byte count like 9.43 GB (base 1024). Whole bytes stay as
512 B; larger units get two decimals. Non-positive counts are 0 B.
Formats a span like 1h20m, 5m3s, or 42s. Non-positive spans are 0s;
anything past 99h caps at 99h+.
Formats a row's SIZE cell. When disk is nil the row is not actively
importing, so it shows just the size — a single human_bytes(total),
e.g. 2.90 GB. When disk is non-nil (an importing row) it shows a
disk / total pair where each side picks its own unit — so a small
on-disk value keeps a meaningful magnitude instead of rounding to 0 in the
total's larger unit: 0 B / 2.1 GB (just started), 44 MB / 2.1 GB
(copying), 1.5 GB / 2.1 GB, 2.1 GB / 2.1 GB (done). Each number uses up
to one decimal with a trailing .0 trimmed. The on-disk value is capped
at the total so the pair never shows a numerator larger than the
denominator (an import file that overshoots the per-episode estimate
displays as full, not 2.3 / 2.1 GB). total <= 0 → —.
Column widths {movie, torrent, status, size, progress} for a content row
of width visible cells. Columns are placed left-to-right at their fixed
widths with one-space gaps; when the row is too narrow the rightmost
columns shrink and then drop (width 0) rather than wrap. progress takes
whatever remains, capped at MAX_PROGRESS.
One data row laid out into width visible cells: Movie · Torrent · Status ·
Size · Progress. Movie is media_name (— when nil), Torrent is the release
title; both are truncated to their fixed widths. Status is the coloured
state label. Size is right-aligned via human_size_pair: an actively
importing row (disk_bytes non-nil, the copied bytes) shows the disk / total
pair (e.g. 0 B / 2.1 GB … 2.1 GB / 2.1 GB); every other row (disk_bytes nil)
shows just the size size_bytes (e.g. 2.9 GB). Progress is a
bar+percent — only for Downloading (from download_percent) and
Importing (from import's copy percent) rows; every other state (incl.
ImportPending) leaves the progress cell blank. The returned string is
exactly width visible cells wide.
display_state is the effective state to render (label + progress); it can
differ from row.state when the TUI reclassifies a Sonarr season-pack row
(e.g. an "importing" episode with no file yet is displayed as pending). It
defaults to row.state so callers that don't reclassify are unaffected.
One-line summary of the queue counts, e.g. 3 importing · 1 downloading,
or idle when every count is zero. Zero-count states are omitted.
The top border: ╔ … ╗ filled with ═, embedding the app title, the
queue-counts summary, and (when non-blank) the aggregated transfer speed
at the right. Exactly cols visible cells wide. Degrades by dropping the
speed, then truncating the summary, and finally to a plain rule when the
terminal is too narrow for any text.
Truncates str to at most width characters, ending with … when it
overflows. width <= 0 yields an empty string.