feat(tui): viewport dry-run + mouse nav + styled empty states (Tier 2)#45
Merged
Conversation
added 2 commits
June 27, 2026 12:52
Restore dry-run diff now renders inside a bubbles/viewport embedded in RestoreModel (REQ-TP-005 / tui-interactive-preview) instead of dumping the raw output to the screen body. The viewport is sized on WindowSizeMsg, content is set on restoreDryRunResultMsg, and renderDryRun renders viewport.View() so long diffs no longer wrap or push the help line off-screen. Scroll keys forward to viewport.Update (j/k, arrows, PgUp/PgDn via the viewport default keymap); g/G jump to top/bottom; q returns to the list. Adds the AGENTS.md too-small guard to restore.View(), negative/out-of-bounds cursor edge tests, and fixes the confirm modal to receive keyboard events (forward keypresses to the modal when active) plus an errors.New nit. Strict TDD: bounded-content + table-driven scroll + modal-forwarding tests written first (RED), then implementation (GREEN).
F6 mouse navigation: declarative v2 MouseModeCellMotion on the dashboard
View, MouseWheelMsg/MouseClickMsg handling in DashboardModel.Update with
row clamping, and a search.IsActive() suppression guard on the root Model
(wheel/click can't move the cursor while the search box is focused).
F7 styled empty states: stateless components.RenderEmptyState(icon, msg,
hint) with package-level Rose Pine styles (Love icon, italic text, muted
hint); wired into dashboard, restore, and cloud no-data branches.
Dashboard empty-state message aligned to spec REQ-TP-007 ('No backups yet'
+ 'Run bak backup to create one' hint). restore.go dry-run key handling
extracted to handleDryRunKey to stay within gocyclo budget.
NO-VERIFY: GGA pre-commit hook exceeds 120s agent shell timeout.
09afa11 to
0c3308d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tier 2 — TUI Personality (Phases 3 & 4)
SDD change:
tui-personality. Implements REQ-TP-005 (scrollable dry-run viewport), REQ-TP-006 (mouse navigation), REQ-TP-007 (styled empty states), and REQ-TIP-001 (interactive preview surface).F5 — Scrollable dry-run viewport (Phase 3, committed earlier on this branch)
bubbles/viewport.Modelinstead of dumping to screen.↑/↓,j/k,PgUp/PgDn,g/G(vim top/bottom);qreturns to the backup list.WindowSizeMsg;restoreDryRunResultMsgcallsviewport.SetContent.F6 — Mouse navigation (Phase 4)
v.MouseMode = tea.MouseModeCellMotionon the dashboardView()(notea.WithMouseCellMotionprogram option in v2).MouseWheelMsgadvances/retreats the table cursor withclampCursorbounds clamping.MouseClickMsg(left click) selects the row at the clicked Y coordinate (offset by the rendered header).search.IsActive()guard on the rootModel.Updatesuppresses mouse while the search box is focused — wheel/click can't move the cursor behind the search input.F7 — Styled empty states (Phase 4)
components.RenderEmptyState(icon, message, hint) stringmatching the existingRenderMenu/RenderHelp/RenderCheckboxpattern.EmptyStateIconStyle(Love, bold),EmptyStateMsgStyle(italic, Text),EmptyStateHintStyle(Muted).∅ No backups yet+ hintRun 'bak backup' to create one.Verification
go test ./...— all green.go test -cover ./internal/tui/...— tui 90.4%, components 92.3%, screens 85.1%, styles 95.7% (all ≥80%).golangci-lint run— 0 issues.go vet ./...— clean.Deviations from design
Model.View(), notDashboardModel.View(): the rootView()aggregates sub-model output and is the single place that returns the finaltea.View; gatingMouseModeonm.screen == ScreenDashboardthere keeps it declarative and avoids the sub-model needing to re-declare it.Model.Update, notDashboardModel.Update: the search component state lives on the rootModel;DashboardModelhas no search field. Documented in test comments.MouseModeisMouseModeNone, asserted inTestModel_View_MouseMode).No backups found: spec REQ-TP-007 pins an exact message only for the dashboard scenario (No backups yet); restore has no spec-pinned message, so the existing wording was preserved to minimize churn.Notes
restore.godry-run key handling extracted tohandleDryRunKeyto stay within the gocyclo budget.--no-verify(NO-VERIFY reason in commit body): the GGA pre-commit hook exceeds the 120s agent shell timeout (AGENTS.md §GGA technical-limitation bypass).