Repository navigation
feat(cli): show friendly amounts and relative times, and color tables - #101
Conversation
tee8z
left a comment
There was a problem hiding this comment.
Requesting changes on 1ff530f (base #98 00bae89). The formatting is exact:
- Amounts are converted as strings, so nothing overflows or rounds, even at 19 digits.
- Cents are padded correctly ($0.05, $1.00), and
negativegives-$2.50. - Asset amounts read as base units.
- Only
Ztimestamps are converted, anddays_from_civilmatches the reference values. - JSON, NDJSON and
--output-filenever pass through the renderer. - Styles are applied after layout, so alignment counts visible width.
- Only fixed status words are painted.
P2: one amount column can mix sats and msats, which invites a 1,000x misreading. friendly (src/output/human.rs:490) shows a msat amount that is a whole number of sats as sats, and anything else as msats. In a payments list, 150,000 msats and 1,500 msats then show as 150 sats and 1,500 msats in the same column. The smaller payment shows the bigger number, and only an m tells them apart. The PR's own test asserts that mixed column (tests/cli_integration.rs:3753). It also invites a copy mistake, because --amount without --unit is in msats, so 150 sats pasted as --amount 150 asks for 150 msats. Show every BTC amount in one unit. Sats with up to three decimals (1.5 sats) stays exact.
Nonblocking:
relative(human.rs:425) turns a small negative difference intoin under a minute, and a few minutes intoin 2 minutes. So a local clock that runs behind the server shows a resource created just now as created in the future, and tables no longer show an absolute time to correct it. Treating anything within 60 seconds either way asjust nowavoids it.- The description says relative times are tested "at every unit boundary", but the cases never hit exactly 3,600, 86,400, 2,592,000 or 31,536,000 seconds. These early returns are untested:
paint_table'ssplit_first_mutelse return,char_at_column'sNone => continuethroughpaint_table, and the before-1970 fallback inView::plain. - The tests call
crate::style::paint(...)inside functions (human.rs:772,:775,:776). The style checklist asks for an import at the top of the test module.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (152 unit, 126 integration) and python3 scripts/check-coverage.py passed. The CRAP report did not finish: its instrumented run hit the known flaky terminal test from #75 (a_profile_saved_after_login_keeps_changes_made_while_its_prompt_was_open, at the 10-second prompt wait), which #104 addresses. I will rerun it and add the result. CI is green.
1ff530f to
c9b442e
Compare
Human output shows amounts in their friendliest exact unit with grouped digits (150,000 sats, $1,234.56), table timestamps relative to now (3 minutes ago), and single-resource timestamps with the relative time beside the UTC time. With color on, table headers and field names are bold and known statuses and outcomes are colored. Styles are added after comfy-table lays out plain text and stdout strips them when color is off, so columns never move and piped tables keep their layout. JSON is unchanged.
c9b442e to
2ac8b2c
Compare
|
Review-body notes, in 2ac8b2c:
|
tee8z
left a comment
There was a problem hiding this comment.
Approved 2ac8b2c against feat/cli-look-04-confirm (#98 b17d0ca). My findings are fixed:
- Every BTC amount is shown in sats, with up to three decimals for msats, so one column never mixes units. I checked the cases: 1 msat reads
0.001 sats, 1500 reads1.5 sats, 150000 reads150 sats, and 1234567 reads1,234.567 sats. The integration test now asserts150 satsbeside1.5 sats. - Anything within a minute either way reads
just now. - The nonblocking notes are addressed too: tests for the empty and short-table early returns and for the before-1970 fallback, and
paintis imported in the test module.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (166 unit, 133 integration) and python3 scripts/check-coverage.py passed. CRAP: 735 measured functions, none at or above 20, highest wait_for_payment at 17.05. CI's RustSec audit fails on RUSTSEC-2026-0385 (ctutils 0.4.2, published today), which master has too. That needs a ctutils bump of its own, not a change here. The other CI jobs pass.
Stack position: 5 of 13 in the look-and-feel stack. Base:
feat/cli-look-04-confirm(#98).Problem
Human tables showed amounts in API base units (
150000 msats,250 cents) and every timestamp as a full UTC string. Both are exact but slow to read. Tables and field views had no color, so the status of each row did not stand out.Behavior (human output only; JSON is unchanged)
150000000 msats150,000 sats(1 satsingular)1500 msats1.5 sats(every BTC amount in sats, up to 3 decimals, so a column never mixes units)123456 cents$1,234.561000 usd1,000 usd(grouped)2026-10-21 14:02:11 UTC1 day ago/in 2 days/just now(within 60 s either way)2025-10-28 16:51:09 UTC2025-10-28 16:51:09 UTC (11 months ago)With color on:
completed,succeeded,ready,active,paidgreen;failed,expired,error,rejectedred;sending,receiving,generating,approved,pending,processingyellow. Any other status stays plain.acceptedyellow,completedandreadygreen,dry_runbold yellow.Design notes
STATUSlabel's display column, usingunicode-width, so wide glyphs earlier in the row do not shift it. If any label beforeSTATUSwas shortened, statuses stay plain rather than risk a misplaced color.anstream::AutoStreamwith the--colorchoice. Tables are always rendered styled, and the stream strips the styles when stdout is not a colored terminal, so the piped bytes equal the plain render.Output::newtakes the choice.--output-fileand JSON never pass through the renderer.human::View { width, now, color }replaces the bare width argument, and tests use a fixednow. Timestamps are converted to epoch seconds with Hinnant'sdays_from_civil, so no time crate is added.unicode-width = "=0.2.2", which comfy-table already uses, soCargo.lockgains one dependency edge and no crate.amount 1,000 sats.Try it on staging
Setup: Try a pull request on staging (added by #97). Everything here is read-only. Find a wallet ID with
voltage wallets list --account me.Expect: bold headers,
CREATED ATandUPDATED ATas11 months agoand8 days ago, and statuses colored (expiredred,completedgreen).Expect: bold field names, and
created_at 2025-10-28 16:51:09 UTC (11 months ago).Expect, for this description (it sends nothing):
amount 1,500 satsin the summary and the details, and a bold yellowdry_runoutcome line.Expect: piped JSON, unchanged; a piped table with the same layout, friendly values, and no escape codes; and the plain table on a terminal.
Validation
cargo fmt --check,cargo clippy --locked --all-targets -- -D warnings,cargo machete --with-metadata,cargo test --locked(152 unit and 126 integration tests), andpython3 scripts/check-coverage.pyall pass.payments list --output tablewith--color alwayshas greencompletedand redfailed, strips to the--color autooutput, and--color neverequals it. The existing narrow-terminal test now expects150 sats.wallets list,payments list, andwallets getrender as described.src/cli.rsendpoint, untouched).human::tableis 17.00.Docs
docs/reference/output.mdandCHANGELOG.md.