Repository navigation
feat(cli): animate progress and show payment waits as a timeline - #91
Conversation
tee8z
left a comment
There was a problem hiding this comment.
Requesting changes on 7b13df4 (base #86 1db2da6). The animation is well contained:
- The
select!dropsexecutebefore the Ctrl-C handler runs.Dropaborts the task and takes theclearedlock, so no frame follows the clear. - No guard spans a stdout write or a prompt.
- Redirected stderr keeps the same sentences as before, byte for byte.
NO_COLOR,--color neverandTERM=dumbremove color from timeline lines.- Status text and IDs are scrubbed.
One fix is needed.
P2: --quiet no longer hides the failure line of a payment wait. Timeline::unsuccessful (src/timeline.rs:100) writes with important_line, which ignores --quiet, while reached uses notice_line. Before this PR, a failed wait returned the error with nothing before it. Now payments get ID --wait completed --quiet --json, run with stderr on a terminal (an agent under tmux or expect), writes ✗ Payment ID failed · 0s before the JSON error. That contradicts --quiet, which "hides optional progress and notices, not errors, results, or recovery IDs". The error that follows already names the payment and carries its state. Use notice_line, and add a --quiet case to the timeline tests.
Nonblocking:
StatusLine::draw(src/progress.rs:104) callsterminal::stderr_width()on every 100 ms frame while it holds theclearedmutex. When/dev/ttycannot be opened, crossterm falls back to runningtput, which is two child processes per frame, andclearwaits behind it. Reading the width once inshow()avoids both.- Frames are written as raw
\x1b[...tostd::io::stderr(), not through anstream. On a classic Windows console without VT processing, the colored, repeated frames could print literal escape codes. Please check on conhost, or route frames throughAutoStream. VOLTAGE_SPINNERmatches only0(src/terminal.rs:375), sofalseoroffsilently keeps the animation.VOLTAGE_EXECUTEaccepts0andfalse, and rejects anything else.- The progress label is passed through unscrubbed. Every caller passes static text today, but taking
&'static str, or scrubbing instatus(), would keep the claim that escapes come only from the palette true by construction. TERM=dumbnow gets\r\x1b[2Kbefore the still label as well as on clear.output.mddoes not say thatTERM=dumborTERM=linuxselects ASCII symbols.- The unit test at
src/progress.rs:232writes escape codes to the real stderr, which bypasses libtest's capture. docs/quality.mdstill says progress lives interminal, though it now has its ownprogressandtimelinemodules.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (143 unit, 124 integration) and python3 scripts/check-coverage.py passed. CRAP: 660 measured functions, none at or above 20, highest endpoint at 18.00; 179 functions were unmeasured. CI is green. Like #86, this stack needs a rebase onto current master.
7b13df4 to
ecf6761
Compare
On an interactive stderr, progress is a spinner redrawn every 100 ms with the time waited after two seconds, cut to the terminal's width and cleared when the wait ends or is cancelled. VOLTAGE_SPINNER=0 or TERM=dumb keeps it still. A payment wait prints one line per status change and closes with a check mark or a cross; redirected stderr keeps its plain sentences. Symbols fall back to ASCII outside a UTF-8 locale.
tee8z
left a comment
There was a problem hiding this comment.
Approved ecf6761, a rebase onto #86 6c8a7f6 with fixes. My finding is fixed: Timeline::unsuccessful now writes through notice_line, so --quiet hides the failure line and keeps the error, and a new terminal test covers it. StatusLine also reads the terminal width once, when it is first drawn, as I suggested. The other notes from my last review are nonblocking.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked (155 unit, 127 integration) and python3 scripts/check-coverage.py passed. CRAP: 687 measured functions, none at or above 20, highest endpoint at 18.00; 182 functions were unmeasured. CI is green.
|
Correction to my approval of |
ecf6761 to
2f36b1c
Compare
|
Review-body notes, in 2f36b1c:
|
tee8z
left a comment
There was a problem hiding this comment.
Approved 2f36b1c, a rebase onto #86 97e4ea1. My earlier findings stay fixed: --quiet hides the failure line, and the width is read once. This head also takes up my nonblocking notes:
- The quality guide now names the
progress,timelineandstylemodules. VOLTAGE_SPINNERacceptsfalse.- Progress labels are scrubbed.
- Unit tests no longer write escape codes to stderr.
- A Windows console without ANSI support gets no spinner.
Validation on this head, in nix develop: cargo fmt --check, cargo clippy --locked --all-targets -- -D warnings, cargo machete --with-metadata, cargo test --locked and python3 scripts/check-coverage.py passed. CRAP: 690 measured functions, none at or above 20, highest wait_for_payment at 17.05. CI is green.
Stack position: 2 of 13 in the look-and-feel stack. Base:
feat/cli-look-01-color(#86), whosestylemodule and--colorchoice this PR uses.Problem
Progress on a terminal was a still label,
Waiting for API response.... During a long wait it looked frozen. A payment wait repeatedPayment ID status: sending; waiting for completed.and gave no clear end.Behavior
On an interactive stderr:
(IDs are shortened here for the table only; the CLI prints them in full.)
VOLTAGE_SPINNER=0(screen readers read every redraw) orTERM=dumb: the label is drawn once, as before.›linecompletedgreen,failedandexpiredred. A status the API added later stays plain.✓ Payment ID completed · 4s(green), or✗ Payment ID failed · 4s(red) followed by the error. A final status gets no›line of its own.LC_ALL,LC_CTYPE,LANG) is UTF-8 andTERMis notdumborlinux; on Windows, in Windows Terminal. Otherwise ASCII:ok,x,>,- \ | /.Redirected stderr is unchanged: there is no spinner, and the same
Payment ID status: …sentences repeat every 15 s.Design notes
progress::Progressis an in-house spinner of about 120 lines, so noindicatif, as the plan decided. Atokiotask redraws the line. A mutex-guardedclearedflag means that a frame already running when the guard drops cannot draw after the clear.style::Lineis theCleanidea from the plan. It can be built only from painted&'static strlabels andscrub_linetext, so a status line's only escape sequences come from the palette.timeline::Timelinetakes the status reporting out ofwait_for_payment, which drops its CRAP score from 19.60 to 17.05.Terminalnow carries--color(with_color). The two internal quiet terminals useglobal.terminal().quieted(), so they follow--colortoo.Validation
cargo fmt --check,cargo clippy --locked --all-targets -- -D warnings,cargo machete --with-metadata,cargo test --locked(143 unit and 124 integration tests), andpython3 scripts/check-coverage.pyall pass.VOLTAGE_SPINNERandTERM=dumbrules, glyph detection,Linescrubbing, and status roles.› receivingline →✓ completed), and a failed payment's✗line before the error with--color neverand no› failedline. The existing wait test now also checks the spinner frame and the green✓.TERM=xterm-256colorandLANG=C.UTF-8and removesLC_ALL,LC_CTYPE, andVOLTAGE_SPINNER, so the runner's locale cannot change which symbols the tests see.src/cli.rsendpoint, untouched);wait_for_paymentis 17.05.Docs
docs/reference/output.md(spinner and timeline),environment.md(VOLTAGE_SPINNER), andCHANGELOG.md.