Skip to content

fix(cli): render the progress spinner ourselves so a terminal resize cannot leave rows behind - #11

Merged
noobnooc merged 2 commits into
mainfrom
fix/own-spinner
Sep 15, 2026
Merged

noobnooc merged 2 commits into
mainfrom
fix/own-spinner

Conversation

@noobnooc

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #7. Resizing the terminal during a review made every spinner tick print a new row, not just the intermittent ones #7 fixed.

Cause: clack's spinner captures the terminal width once when it is created and computes how many rows to erase from the bare message. After a shrink, the row it already printed wraps, and its \r + erase only clears the last of those rows; the fitSpinnerMessage truncation from #7 only re-applied on the next message() call (the next budget/tool event), so between events every frame left a row behind. Upstream has no fix planned short of the stalled rework (bombshell-dev/clack#479), so this stops depending on their spinner for the live row.

rowSpinner in prompts.ts replaces clack.spinner inside clackPrompter:

  • same look (clack's glyphs and 80 ms cadence, cycling dots, ◇ done / ▲ failed end lines, hidden cursor while spinning);
  • re-fits the message to stdout.columns on every frame, so the row never exceeds columns - 1 and erasing it is \r + clear-line — no cursor-up arithmetic to get wrong;
  • restores the cursor on stop/error/clear and on process.exit (second Ctrl+C).

The TODO(clack) workaround note from #7 is gone with it; fitSpinnerMessage stays as the sizing helper.

Verification

  • test/spinner.test.ts drives it with fake timers, including a 120 → 55 column resize mid-run: every frame after the shrink is ≤ 54 columns and none contains a newline.
  • Also ran it in a real pty from Python, sending TIOCSWINSZ + SIGWINCH (120 → 55) 1.3 s in: 32 frames, widest after the shrink 54 columns, no newlines inside frames, ends with ◇ done.
  • pnpm lint, pnpm --filter gribble typecheck, CLI test suite (52 tests) pass.

Checklist

  • pnpm lint && pnpm typecheck && pnpm test pass
  • Added a changeset (pnpm changeset) if a published package changed
  • Docs updated (docs/) if behavior changed — n/a

🤖 Generated with Claude Code

…cannot leave rows behind

clack's spinner reads the terminal width once at creation and sizes its
erase from the bare message, so shrinking the window mid-run made every
frame wrap and leave a stale row; the truncation added in f4f7ea8 only
helped until the next resize. rowSpinner re-fits the message to the live
width on every frame and erases with \r + clear-line, so the row can
never wrap in the first place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

🐛 Gribble (apps/website): gate passed

The gribbles went hungry. Ship it.

new existing fixed critical error warn info
0 0 5 0 0 0 0

Full report

Gribble 0.3.0 · mode gate · http://localhost:4173

✅ Step passed: No new findings at or above error. (fail-on: error)

Check run with annotations

picocolors enables color when CI is set, so on GitHub the glyph is wrapped
in escape codes and the literal match failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@noobnooc
noobnooc merged commit 0cb92c5 into main Sep 15, 2026
7 checks passed
@noobnooc
noobnooc deleted the fix/own-spinner branch September 15, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant