Skip to content

Feat/implement phase c#2

Merged
nixrajput merged 22 commits into
mainfrom
feat/implement-phase-c
May 31, 2026
Merged

Feat/implement phase c#2
nixrajput merged 22 commits into
mainfrom
feat/implement-phase-c

Conversation

@nixrajput

@nixrajput nixrajput commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Complete interactive dashboard with three panels (Profiles, Dumps, Jobs), keyboard navigation, and live job progress
    • Backup and Restore interactive modals with profile & dump selection; improved error overlays
  • Documentation

    • Updated roadmap/Phase B–C wording and command description to reference the interactive multi-panel dashboard
  • Tests

    • Added dashboard snapshot and panel/unit tests to lock UI behavior

nixrajput added 18 commits May 30, 2026 02:26
Adds internal/tui/panels/jobs.go implementing the Jobs panel that
bridges jobs.Runner Event channels into Bubble Tea's update loop via
a re-issuing SubscribeCmd pump. Also adds the charmbracelet/harmonica
indirect dependency required by bubbles/progress.
Add dashboard_snapshot_test.go with two teatest golden-snapshot tests
(Initial, FocusDumps). Generate testdata/*.golden via -update flag.
These are directly imported by the TUI (huh forms, teatest snapshots,
termenv in the focus test) but were still listed as indirect. tidy
reclassifies them; no version changes.
The dashboard owns error-overlay dismissal (intercepts esc/enter/q itself),
so ErrorModel.Update never drove quitting — but its docstring claimed it
'quits the program' and the dead tea.Quit path was a trap for any future
caller that routed input to it. Update is now an inert tea.Model stub that
only absorbs the window-size hint; docstring reflects the real lifecycle.
Phase B+C both ship: status table C → Complete, warning + commands +
features reflect the now-working interactive dashboard.
@nixrajput nixrajput self-assigned this May 29, 2026
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e465f71-8a3e-434d-899f-2cd7955ff5ac

📥 Commits

Reviewing files that changed from the base of the PR and between 082e7cb and c35b08b.

📒 Files selected for processing (6)
  • internal/tui/dashboard_snapshot_test.go
  • internal/tui/modals/errormodal.go
  • internal/tui/modals/errormodal_test.go
  • internal/tui/panels/jobs.go
  • internal/tui/panels/jobs_test.go
  • internal/tui/panels/profiles.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/tui/modals/errormodal.go
  • internal/tui/panels/profiles.go

📝 Walkthrough

Walkthrough

This PR implements a full Bubble Tea TUI dashboard (Profiles, Dumps, Jobs), modal backup/restore forms, job event subscriptions and error overlay handling, keybindings and styles, CLI wiring to pass initialized deps into the TUI, and comprehensive unit + snapshot tests with golden files.

Changes

Phase C Bubble Tea Dashboard Implementation

Layer / File(s) Summary
Dependencies and CLI entry point
go.mod, internal/cli/root.go, internal/tui/run.go
Adds Charmbracelet UI libs and other indirect dependencies; root command now builds app.Deps and calls tui.Run(deps); Run(deps) starts the Bubble Tea program.
Core types, messages, keys, styles
internal/tui/panel.go, internal/tui/msg.go, internal/tui/keys.go, internal/tui/styles/styles.go
Introduces Panel interface, TUI message types, centralized key mappings (KeyMap), and shared Lipgloss styles.
Modal forms and error overlay
internal/tui/modals/backup.go, internal/tui/modals/restore.go, internal/tui/modals/errormodal.go, internal/tui/modals/errormodal_test.go
Adds Backup and Restore Huh forms (restore adapts dump-ID control), an ErrorModel overlay, and tests validating non-panicking rendering and width handling.
Panel implementations
internal/tui/panels/profiles.go, internal/tui/panels/dumps.go, internal/tui/panels/jobs.go, internal/tui/panels/jobs_test.go
Profiles: list of profiles (filtering disabled); Dumps: table of dump catalog entries with reload and human-readable sizes; Jobs: subscribes to job event stream, maintains deterministic first-seen ordering, renders progress and errors; includes deterministic-order test.
Dashboard root model
internal/tui/dashboard.go
Adds exported Dashboard type with Init/Update/View, orchestrates three panels, manages focus/layout, dispatches modal forms to app.Backup/app.Restore, handles job channels and error overlay, and maintains panel state across updates.
Unit tests and golden snapshots
internal/tui/dashboard_test.go, internal/tui/dashboard_snapshot_test.go, internal/tui/testdata/*.golden
Tests cover rendering, focus cycling, form hints, error modal behavior, adaptive restore form, Esc-cancel behavior, and snapshot-based visual regression (initial and post-Tab views).
Documentation updates
README.md
Updates Phase B/C wording, documents that siphon (bare) launches the interactive multi-panel Bubble Tea dashboard, and notes snapshot tests for the dashboard.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI (root.go)
  participant TUI as TUI Run
  participant Dashboard as Dashboard
  participant Profiles as Profiles Panel
  participant Dumps as Dumps Panel
  participant Jobs as Jobs Panel
  participant Forms as Backup/Restore Forms
  participant App as app.Backup/app.Restore

  CLI->>TUI: Run(deps)
  TUI->>Dashboard: NewDashboard(deps) / Start
  Dashboard->>Profiles: Init()
  Dashboard->>Dumps: Init()
  Dashboard->>Jobs: Init()

  User->>Dashboard: KeyMsg (e.g., 'b' backup / 'r' restore / Tab)
  Dashboard->>Forms: NewBackup/NewRestore(...)
  Forms-->>Dashboard: Form completed -> BackupResult/RestoreResult
  Dashboard->>App: app.Backup/profile or app.Restore(...) (runs)
  App-->>Dashboard: JobChannelMsg (job events) or ErrorMsg
  Dashboard->>Jobs: SubscribeCmd(job channel)
  Jobs->>Dashboard: RefreshMsg on completion
  Dashboard->>Dumps: Reload() after completion
  Dashboard->>Dashboard: Render updated view or ErrorModel overlay
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • nixrajput/siphon#1: Introduced the earlier Phase A TUI placeholder that this PR replaces with the multi-panel Dashboard.

Poem

🐰 Three panels hop into view with cheer,
Profiles, Dumps, and Jobs all near,
Forms ask kindly, keys move focus right,
Golden snapshots glow in the night,
A rabbit claps — Phase C’s here!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/implement phase c' is vague and does not clearly convey what was implemented. While it references Phase C from the project roadmap, it lacks specificity about the actual changes (multi-panel Bubble Tea dashboard, modals, job management, etc.). Consider a more descriptive title like 'Implement Phase C dashboard with multi-panel Bubble Tea TUI and modal forms' to better communicate the main feature being added.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/implement-phase-c

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
internal/tui/panels/dumps.go (2)

64-71: 💤 Low value

Simplify SetFocusFocus() is called even when blurring.

When b is false the table is focused then immediately blurred. The end state is correct, but the unconditional Focus() is misleading.

♻️ Proposed tweak
 func (p *Dumps) SetFocus(b bool) {
 	p.focused = b
-	p.tbl.Focus()
-	if !b {
+	if b {
+		p.tbl.Focus()
+	} else {
 		p.tbl.Blur()
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/panels/dumps.go` around lines 64 - 71, The SetFocus method
currently always calls p.tbl.Focus() then conditionally p.tbl.Blur(), which
unnecessarily focuses before blurring; update Dumps.SetFocus to set p.focused =
b and call p.tbl.Focus() only when b is true, otherwise call p.tbl.Blur() so the
table isn't focused when b is false and the behavior/readability matches intent.

82-98: 💤 Low value

Reload silently swallows List() errors.

A failed catalog read leaves the table empty with no signal to the user. Since Reload has no return value, the dashboard can't surface this via its error overlay (e.g. after a completed job at dashboard.go Line 145). Consider returning the error (or a status flag) so callers can decide whether to display it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/panels/dumps.go` around lines 82 - 98, The Reload method
currently swallows errors from p.deps.Dumps.List(), leaving the UI unaware;
change Reload to return an error (i.e., func (p *Dumps) Reload() error),
propagate and return the error if p.deps.Dumps.List() fails, and on success
continue building rows and call p.tbl.SetRows(rows) then return nil; update
callers to check the returned error and surface it via the dashboard overlay as
needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/tui/dashboard_snapshot_test.go`:
- Around line 34-56: The snapshot test TestDashboard_FocusDumps_Snapshot is not
verifying a different focused state after sending the Tab key; ensure the test
deterministically captures a focused rendering by either (A) asserting the
dashboard model reports the Dumps panel focused before snapshot (e.g., inspect
the Dashboard instance returned by NewDashboard or tm.Model() for a FocusedPanel
/ IsFocused("dumps") property and fail if not) and waiting until that becomes
true, or (B) making the Dashboard's Render output include a deterministic focus
marker for Dumps (e.g., a distinct string or visual marker emitted when the
Dumps panel is focused) so the golden differs; update the test to perform the
explicit assertion/wait after tm.Send(tea.KeyMsg{Type: tea.KeyTab}) and before
reading tm.FinalOutput and keep the existing teatest.RequireEqualOutput call.

In `@internal/tui/modals/errormodal.go`:
- Around line 35-42: In ErrorModel.View guard against a nil m.err and clamp the
computed width before rendering: check m.err and use a safe fallback message
(e.g. "unknown error") when nil, compute an availableWidth variable from m.width
(e.g. available := m.width - 4) and clamp it to a sensible minimum (so you never
pass a negative or too-small value into min/Width), then call min with that
clamped availableWidth and use that when constructing the lipgloss box; update
references in ErrorModel.View (m.err, min call and Width(...)) accordingly.

In `@internal/tui/panels/jobs.go`:
- Around line 100-110: The View currently iterates the map p.jobs which is
non-deterministic and causes jumping rows; add a deterministic ordering by
tracking insertion order: add a slice field (e.g., jobOrder []string or []jobID)
to the same struct that holds p.jobs, append the job's ID to jobOrder inside the
absorb method when a new job is first seen, and then change View to iterate
jobOrder (looking up each job in p.jobs and skipping missing entries) instead of
ranging over the map; also ensure you remove IDs from jobOrder when jobs are
completed/removed to avoid stale entries.

In `@internal/tui/panels/profiles.go`:
- Around line 44-52: loadItems currently discards the error returned by
d.Profiles.Get(n), which can yield empty profile fields if a profile is removed
between List() and Get(); update loadItems to check the error from
d.Profiles.Get(n) and skip that entry (or log the error) instead of using the
zero-value ProfileConfig, ensuring only valid profileItem instances (created
from successful d.Profiles.Get calls) are appended to items; reference the
loadItems function, d.Profiles.Get, and profileItem when making the change.

---

Nitpick comments:
In `@internal/tui/panels/dumps.go`:
- Around line 64-71: The SetFocus method currently always calls p.tbl.Focus()
then conditionally p.tbl.Blur(), which unnecessarily focuses before blurring;
update Dumps.SetFocus to set p.focused = b and call p.tbl.Focus() only when b is
true, otherwise call p.tbl.Blur() so the table isn't focused when b is false and
the behavior/readability matches intent.
- Around line 82-98: The Reload method currently swallows errors from
p.deps.Dumps.List(), leaving the UI unaware; change Reload to return an error
(i.e., func (p *Dumps) Reload() error), propagate and return the error if
p.deps.Dumps.List() fails, and on success continue building rows and call
p.tbl.SetRows(rows) then return nil; update callers to check the returned error
and surface it via the dashboard overlay as needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 982ec39b-de9b-4d16-a65c-9453185ab092

📥 Commits

Reviewing files that changed from the base of the PR and between fe535f1 and 082e7cb.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (19)
  • README.md
  • go.mod
  • internal/cli/root.go
  • internal/tui/dashboard.go
  • internal/tui/dashboard_snapshot_test.go
  • internal/tui/dashboard_test.go
  • internal/tui/keys.go
  • internal/tui/modals/backup.go
  • internal/tui/modals/errormodal.go
  • internal/tui/modals/restore.go
  • internal/tui/msg.go
  • internal/tui/panel.go
  • internal/tui/panels/dumps.go
  • internal/tui/panels/jobs.go
  • internal/tui/panels/profiles.go
  • internal/tui/run.go
  • internal/tui/styles/styles.go
  • internal/tui/testdata/TestDashboard_FocusDumps_Snapshot.golden
  • internal/tui/testdata/TestDashboard_Initial_Snapshot.golden

Comment thread internal/tui/dashboard_snapshot_test.go Outdated
Comment thread internal/tui/modals/errormodal.go Outdated
Comment thread internal/tui/panels/jobs.go
Comment thread internal/tui/panels/profiles.go
nixrajput added 4 commits May 31, 2026 14:35
The focus snapshot's golden is byte-identical to the initial one (teatest
renders off-TTY in ASCII, so the focus border-color difference is stripped),
so it didn't actually verify focus moved. Add an explicit assertion on the
final model's focusIdx (==1, Dumps) after Tab, and document why the golden
can't prove focus. The snapshot remains a layout/content regression guard;
focus movement is also visually verified in TestDashboard_Tab_MovesFocus.
NewError is exported, so a nil error could reach View → m.err.Error() panic;
now falls back to 'unknown error'. And the dashboard doesn't forward a
WindowSizeMsg to the overlay, so m.width is typically 0 → min(80, -4) = -4
passed to Width(); now clamps available width to a sensible minimum (20).
Adds errormodal_test.go covering both edge cases (no prior test in modals).
Jobs.View ranged over the jobs map, so with >1 job the rows reordered
randomly between frames. Track first-seen order in a new 'order []string'
field appended in absorb, and iterate that in View (skipping any id without a
backing view). Adds jobs_test.go asserting stable B/A/C ordering across
updates.

Skipped the review's 'remove IDs on completion' suggestion: the panel never
removes jobs by design (completed/failed jobs keep their final state visible;
jobsChannelClosedMsg is a documented no-op), so there are no stale entries to
prune. Removal would land with a future 'clear completed' feature.
loadItems discarded the error from d.Profiles.Get(n) and appended a
zero-value profileItem on failure. Now it skips the entry. Unreachable today
(List and Get read the same in-memory map, single-threaded TUI) but removes a
latent footgun if List/Get ever diverge. No test: the error path can't be
triggered through the public Store API without contorting internals.
@nixrajput nixrajput merged commit 66a5d07 into main May 31, 2026
5 checks passed
@nixrajput nixrajput deleted the feat/implement-phase-c branch May 31, 2026 09:45
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