Skip to content

feat: standardize CLI output on charmbracelet/bubbles + bubbletea - #47

Open
ChristoPy wants to merge 1 commit into
mainfrom
feat/standardize-output-with-tui
Open

feat: standardize CLI output on charmbracelet/bubbles + bubbletea#47
ChristoPy wants to merge 1 commit into
mainfrom
feat/standardize-output-with-tui

Conversation

@ChristoPy

@ChristoPy ChristoPy commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

The CLI already lived in the Charm ecosystem (huh for forms, lipgloss for styling) but its dynamic/async output was inconsistent: a non-Charm spinner (briandowns/spinner), plain sequential prints in listen, raw fmt.Printf in upgrade, and no progress indication at all in payments simulate. This adopts bubbletea/bubbles (already indirect deps via huh) as the single rendering layer for that dynamic output, and fixes a couple of real bugs found along the way.

  • internal/tui: a bubbletea spinner-to-result flow (output.RunTask) used by login, logout, upgrade, and payments simulate — the spinner's final frame becomes the result, one continuous render instead of a bubbletea animation followed by a separately-printed box (which, for payments simulate, actually raced the still-running spinner and corrupted output). Also a full interactive dashboard for listen: scrollable event feed, connection status, key bindings (bubbles/viewport + key + help). Both fall back to today's plain sequential-line output when stdout isn't a terminal or -o isn't text, so piped/scripted/CI use is unaffected.
  • internal/webhook: listener display logic decoupled from the network loop via an optional Emit hook, so the interactive dashboard and the plain fallback share one source of truth instead of duplicating it.
  • internal/logger: the console handler now only surfaces diagnostics under --verbose. Routine Info/Warn logs ("Signed out", "WebSocket connected", ...) were leaking as raw level=INFO msg=... text right next to the styled output — file logging is unaffected, --verbose still shows everything.
  • internal/clierr: marks errors already displayed to the user (by RunTask, or by any handler that prints before returning) so the top-level command runner doesn't print them a second time. This was a real bug, not just cosmetic — login/payments/the websocket retry path could each show the same error box twice.
  • Dropped the bordered box around success/error output for flat styled text, and standardized upgrade on English (it was the only command in Portuguese).
  • Replaced briandowns/spinner with bubbles/spinner.

Test plan

  • go build ./..., go vet ./..., go test ./... all pass
  • Verified live under a real pty: upgrade (spinner → result, single continuous render), login via both --key and the real device-flow path (confirmed the duplicate-error-box fix against a real 400 response), logout, payments simulate (including --verbose and -o json)
  • Confirmed non-interactive fallback (| cat) still produces plain sequential output for listen
  • listen's interactive dashboard against a real forwarded webhook (spinner/connection-status/event-feed rendering was verified via the same bubbletea primitives proven out in the upgrade/login pty tests, but not the full dashboard end-to-end with live traffic)

Adopts bubbletea/bubbles (already indirect deps via huh) as the
consistent rendering layer for the CLI's dynamic output, replacing a mix
of ad-hoc fmt.Print*, a non-Charm spinner, and boxed lipgloss output.

- internal/tui: bubbletea-driven spinner-to-result flow (output.RunTask)
  used by login, logout, upgrade, and payments simulate - one continuous
  render instead of a spinner followed by a separately-printed result -
  plus a full interactive dashboard for `listen` (scrollable event feed,
  connection status, key bindings), degrading to today's plain
  sequential lines when stdout isn't a terminal or -o isn't text.
- internal/webhook: listener display logic decoupled from the network
  loop via an optional Emit hook, so the dashboard and the plain
  fallback share one source of data instead of duplicating it.
- internal/logger: console handler now only surfaces diagnostics under
  --verbose (routine Info/Warn logs were leaking as raw `level=INFO
  msg=...` text alongside the styled output); file logging is
  unaffected.
- internal/clierr: marks errors already shown to the user (by RunTask,
  or by a handler that prints before returning) so the top-level runner
  doesn't print them a second time - fixes a real duplicate-error-box
  bug in login/payments/ws, not just a cosmetic one.
- Dropped the bordered box around success/error output in favor of flat
  styled text, and standardized `upgrade` on English to match every
  other command.
- Replaced briandowns/spinner with bubbles/spinner; go.mod/go.sum
  updated accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4MKBc9TASEspQuCnsqZLf
@ChristoPy ChristoPy self-assigned this Aug 6, 2026
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