Skip to content

feat(tui): viewport dry-run + mouse nav + styled empty states (Tier 2)#45

Merged
danielxxomg merged 2 commits into
mainfrom
feat/tui-personality-tier2
Jun 27, 2026
Merged

feat(tui): viewport dry-run + mouse nav + styled empty states (Tier 2)#45
danielxxomg merged 2 commits into
mainfrom
feat/tui-personality-tier2

Conversation

@danielxxomg

Copy link
Copy Markdown
Owner

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)

  • Restore dry-run diff renders inside a bubbles/viewport.Model instead of dumping to screen.
  • Scroll keys: /, j/k, PgUp/PgDn, g/G (vim top/bottom); q returns to the backup list.
  • Viewport sized on WindowSizeMsg; restoreDryRunResultMsg calls viewport.SetContent.

F6 — Mouse navigation (Phase 4)

  • Declarative v2 v.MouseMode = tea.MouseModeCellMotion on the dashboard View() (no tea.WithMouseCellMotion program option in v2).
  • MouseWheelMsg advances/retreats the table cursor with clampCursor bounds clamping.
  • MouseClickMsg (left click) selects the row at the clicked Y coordinate (offset by the rendered header).
  • search.IsActive() guard on the root Model.Update suppresses mouse while the search box is focused — wheel/click can't move the cursor behind the search input.

F7 — Styled empty states (Phase 4)

  • Stateless components.RenderEmptyState(icon, message, hint) string matching the existing RenderMenu/RenderHelp/RenderCheckbox pattern.
  • Package-level Rose Pine styles: EmptyStateIconStyle (Love, bold), EmptyStateMsgStyle (italic, Text), EmptyStateHintStyle (Muted).
  • Wired into dashboard (no backups), restore (no backups to restore), and cloud (no provider) no-data branches.
  • Dashboard message aligned to spec REQ-TP-007: ∅ No backups yet + hint Run '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

  • MouseMode set on root Model.View(), not DashboardModel.View(): the root View() aggregates sub-model output and is the single place that returns the final tea.View; gating MouseMode on m.screen == ScreenDashboard there keeps it declarative and avoids the sub-model needing to re-declare it.
  • Search suppression guard on root Model.Update, not DashboardModel.Update: the search component state lives on the root Model; DashboardModel has no search field. Documented in test comments.
  • Mouse scoped to dashboard only: spec REQ-TP-006 mentions dashboard and restore list screens; tasks (4.1/4.2) and design scoped mouse handling to the dashboard. Restore stays keyboard-only (its MouseMode is MouseModeNone, asserted in TestModel_View_MouseMode).
  • Restore empty-state message kept as 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.go dry-run key handling extracted to handleDryRunKey to stay within the gocyclo budget.
  • Committed with --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).

danielxxomg 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.
@danielxxomg danielxxomg force-pushed the feat/tui-personality-tier2 branch from 09afa11 to 0c3308d Compare June 27, 2026 17:53
@danielxxomg danielxxomg merged commit cf902c9 into main Jun 27, 2026
2 of 3 checks passed
@danielxxomg danielxxomg deleted the feat/tui-personality-tier2 branch June 27, 2026 17:53
danielxxomg pushed a commit that referenced this pull request Jun 27, 2026
3 PRs merged (#44, #45, #46):
- Tier 1: window title, spinner indicators, status bar, gradient logo
- Tier 2: viewport dry-run, mouse nav, styled empty states
- Tier 3: paste support in wizard
Personality: 4/10 → 8/10
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