Rewrite TUI as full-screen Bubble Tea two-pane interactive browser

Replace the line-based stdin pagination loop with a charmbracelet/bubbletea
application providing a responsive two-pane layout: a filterable deal list
on the left and a scrollable detail viewport on the right.

Key capabilities:

- Async startup: spinner + skeleton while store/deals are fetched, then
  transition to the interactive view. Fatal load errors propagate cleanly.
- Grouped sections: deals are bucketed by category (BOGO first, then by
  count descending) with numbered section headers. Jump with [/] for
  prev/next and 1-9 for direct section access.
- Inline filter cycling: s (sort: relevance/savings/ending), g (bogo
  toggle), c (category cycle), a (department cycle), l (limit cycle),
  r (reset to CLI-start defaults). Filters rebuild the list while
  preserving cursor position via stable IDs.
- Fuzzy search: / activates bubbletea's built-in list filter. Section
  jumps are disabled while a fuzzy filter is active.
- Detail pane: full deal metadata with word-wrapped text, scrollable
  via j/k, u/d (half-page), b/f (full page). Tab switches focus.
- Terminal size awareness: minimum 92x24, graceful too-small message.
- JSON mode: tui --json fetches and filters without launching the
  interactive UI, consistent with other commands.

New files:
  cmd/tui_model.go      — Bubble Tea model, view, update, grouping logic
  cmd/tui_model_test.go — Tests for sort canonicalization, group ordering,
                           and category choice building

Dependencies added: charmbracelet/bubbles, charmbracelet/bubbletea.
Transitive deps upgraded: lipgloss, x/ansi, x/cellbuf, x/term, go-colorful,
go-runewidth, sys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 01:32:10 -05:00
parent f486150c06
commit da327fe759
5 changed files with 1352 additions and 125 deletions

28
go.mod
View File

@@ -3,28 +3,40 @@ module github.com/tayloree/publix-deals
go 1.24.4
require (
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.9
github.com/stretchr/testify v1.11.1
golang.org/x/term v0.30.0
)
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.9.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.3.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)