Skip to content

Replace mpb progress bars with tview TUI#72

Open
RyanAMayers wants to merge 8 commits into
masterfrom
feat/tview-tui
Open

Replace mpb progress bars with tview TUI#72
RyanAMayers wants to merge 8 commits into
masterfrom
feat/tview-tui

Conversation

@RyanAMayers
Copy link
Copy Markdown

Summary

  • Replace mpb/v8 progress rendering with a full-screen tview TUI: 3-line header, scrollable per-table body, fixed-height log pane backed by a ring buffer; one 100ms render tick snapshots atomics and issues a single QueueUpdateDraw, fixing bar duplication caused by slog warnings interleaving with mpb cursor-up redraws.
  • Tee slog output to /tmp/swoof.log while the TUI is active (truncated per run) so retry details survive post-mortem; fall back to legacy slog-to-stderr under -no-progress, -verbose, or non-TTY stdout.
  • Add lifecycle logs (per-table starting/finished/recovered + run start/complete pair), version banner / update-available before setup, severity-colored log levels with dimmed attribute keys, and ColorDefault overrides so the TUI inherits the terminal's theme.
  • Distinguish user Ctrl+C/q (errInterrupted, exit 130, "swoof: interrupted") from worker Fatal (exit 1, real error); reset state.Begin on retry so rate/ETA reflect the current attempt.
  • Add -version flag, pre-TUI run header (source / destination / tables), and integer rounding in formatShort for 100+ values.

Test plan

  • Run a real source→dest swoof with progress on a TTY; confirm bars render once, log pane tails retry warnings, and header shows version + update banner before setup lines
  • Run with -no-progress, -verbose, and piped stdout; confirm fallback to slog-to-stderr in each
  • Trigger Ctrl+C mid-run; confirm "swoof: interrupted" with exit 130
  • Force a worker failure; confirm exit 1 with real error message (not "interrupted")
  • Trigger a retry on a long-running table; confirm rate/ETA reset and that /tmp/swoof.log contains full error chain
  • Run -version standalone
  • Visually verify TUI in a light-theme terminal (ColorDefault inheritance)

🤖 Generated with Claude Code

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the mpb progress bar library with a custom TUI implemented using tview and tcell. The new interface provides a comprehensive view of the migration process, including a status header, table-level progress tracking with ETA and transfer rates, and a scrollable log pane. The PR also introduces a --version flag, fixes several typos in flag names and descriptions, and refines the formatShort utility. Review feedback highlights the need for robust UTF-8 handling in string truncation and centering logic, and recommends using os.CreateTemp for the log file to prevent security risks and file collisions.

Comment thread ui.go Outdated
Comment thread ui.go
Comment thread ui.go
Comment thread ui.go Outdated
Copy link
Copy Markdown
Member

@BrianLeishman BrianLeishman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing an update to the readme video!

RyanAMayers and others added 8 commits May 29, 2026 09:18
Drops the mpb/v8-based progress rendering in favor of a full-screen
tview TUI. Per-table state flows through atomics on a shared struct;
a 100ms render tick snapshots state and issues one QueueUpdateDraw,
so worker goroutines never touch the UI directly. Fixes the bar
duplication caused by slog warnings interleaving with mpb cursor-up
redraws.

Layout is a vertical Flex:
- 3-line header (version, source/dest, rollup counters, elapsed)
- Scrollable body TextView rendering one row per table
- Fixed-height log pane (min(10, h*30%)) tailing a ring buffer

Per-row format: <glyph> <name> <pct> [=====>-----] counts rate state
with the bar absorbing any remaining terminal width. Name and state
text share the bar's status color.

Fallback paths keep the legacy slog-to-stderr behavior:
- -no-progress (explicit opt-out)
- -verbose (per-query spam would flood the log pane)
- non-TTY stdout (piped output)

slog output is tee'd to /tmp/swoof.log (truncated each run) whenever
the TUI is active, so transient retry details remain available for
post-mortem review without cluttering the screen.

Also in this commit:
- Add -version flag for quick version checks
- Pre-TUI run header listing source / destination / tables
- Setup status lines during pool connect / table resolution
- formatShort rounds to integers for 100+ values (100K, not 100.0K)
- Retry warnings log full error + chain to file, short cause in TUI

Deps: add rivo/tview, gdamore/tcell/v2, golang.org/x/term; drop
vbauerster/mpb/v8 and its sub-packages.
Add per-table lifecycle logs (starting/finished/recovered) and a
"swoof run started" / "table imports complete" pair so the log pane
shows continuous activity instead of just retry warnings. Log lines
in the pane render with severity-colored levels and dimmed attribute
keys.

Move the `swoof vX.Y.Z` title and the update-available banner ahead
of the "connecting to source" setup status lines so the user sees
what's running before any work starts.

Override tview's color theme to use ColorDefault everywhere so the
TUI inherits the terminal's bg/fg (works with light themes, custom
palettes, transparent terminals). Explicit color tags continue to
override per-text.

Bug fixes from the pre-merge audit:
- Worker-driven u.Fatal no longer prints "swoof: interrupted" with
  exit 130; an errInterrupted sentinel distinguishes user Ctrl+C/q
  from a permanent worker failure (the latter exits 1 with the real
  error).
- state.Begin resets StartedAt and FinishedAt every call so retry
  rate/ETA reflects the current attempt rather than time accumulated
  across earlier tries; total wall-clock duration is still tracked
  via a local in the goroutine.
- Drop the dead seed loop in newUI (left over from when body was a
  Table primitive).
- Drop the write-only stopTick field from the ui struct.
- Consolidate Fatal and requestStop; Ctrl+C/q now calls
  Fatal(errInterrupted) directly.
- Color-tag syntax in the header summary made consistent: every
  bold value uses `[white::b]VALUE[-:-:-]` then `[gray]` resumes for
  the next static run.
Use os.CreateTemp for the TUI log instead of a hardcoded path in /tmp
to avoid symlink and multi-user collision risks, and switch the
header centering plus truncateRight/truncateMiddle to rune-based
counting and slicing so multi-byte names render correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a per-table statusFinalized state alongside statusDone so the
post-workers swap pass shows live progress (blue "imported" → green
"done"), and holds the TUI open with a centered "Press Q to exit"
prompt and green pane borders so the user can scan the final state
and scroll the log before dismissing.

Required refactoring the finalization phase (delayed temp-table swap,
funcs/views/procs/clipboard imports) to return errors instead of
calling os.Exit — a hard exit while tview still owns the screen
would leave the alt-screen on top with no visible error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tightens the post-tview-migration UX a number of small ways:

- Three-line title with copyright + license, kept in scrollback
- TUI starts before connection setup so connect/resolve logs land in
  the log pane; setup statuses + table list + source/dest summary
  replay into terminal scrollback after the user dismisses
- Per-table finalize state: blue "imported" while waiting on the
  delayed temp-table swap, green "done" once swapped + triggers copied
- Body shows a centered "Resolving tables on X..." placeholder during
  the resolve phase, with a 1-2-3-dot ellipsis animation
- Both body and log panes carry a one-column right-edge scrollbar
  (custom Draw overlay since tview v0.42.0 has no built-in for
  TextView). Tab / Shift-Tab cycle focus between them; ▸ marker on
  the focused pane's title; log auto-scroll-to-end pauses when the
  log is focused
- Yellow→green border blink on completion before settling on green
- "Press Q to exit" pinned bold-yellow at the bottom of the log pane
- Slog messages tied to per-row lifecycle ("starting table",
  "finished table", "finalized table") tinted to match the body's
  status palette
- Finalization phase refactored to return errors so failures surface
  cleanly instead of os.Exit'ing through the live alt-screen
- Re-renders keep firing in the completed state so terminal resizes
  reflow correctly; SetText preserves scroll position

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The retrying glyph was U+23F3 ⏳ (hourglass), which is emoji-class and
renders double-width on macOS Terminal and iTerm2 — pushing the row
layout one column off alignment whenever a table was retrying. Replaced
with U+21BB ↻ (clockwise open-circle arrow), narrow on every terminal.

To keep the running and retrying states visually distinct, also swapped
the running glyph from U+27F3 ⟳ (also a clockwise circle arrow, easily
confused with the new retrying glyph) to U+25B6 ▶ (right-pointing
triangle, the universal "play" symbol).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a desktop notification on swoof completion via gen2brain/beeep
(silently no-ops on hosts without a notification service, including
SSH-to-headless and CI). Trims comments across both files: removes
docstrings that just restate function names, drops layout walkthroughs
that the code itself shows, and tightens the remaining ones to keep
only the non-obvious WHY (race avoidance, hidden constraints, MySQL
quirks, etc.). Also drops postCompletionRendered now that the render
loop runs forever post-completion — TextView.SetText preserves scroll
so the gate was unnecessary, and removing it lets terminal resizes
reflow correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@RyanAMayers RyanAMayers requested review from dcrowdev and removed request for dcrowdev May 29, 2026 13:20
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.

2 participants