package ui import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/gmgauthier/gralculator/internal/calc" ) // App is the root Bubble Tea model for the gralculator TUI. // Phase 1/3 skeleton only — real implementation in phase 3. type App struct { engine *calc.Engine width int height int // flash state etc. will be added during spike (see gostations patterns) } func NewApp() *App { return &App{ engine: calc.NewEngine(), } } func (a *App) Init() tea.Cmd { return nil } func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: a.width = msg.Width a.height = msg.Height case tea.KeyMsg: switch msg.String() { case "q", "ctrl+c", "esc": return a, tea.Quit case "tab": // Will drive engine.CycleBase() + possible CERR flash _ = a.engine.CycleBase() // TODO (phase 3): trigger flash on error, re-render small row } } return a, nil } func (a *App) View() string { // Placeholder view. Real two-row display + keypad grid in phase 3. // Will use lipgloss heavily for: // - large number area (tall + styled) // - small current-base row (highlighted label) // - centered content card (lipgloss.Place) // - button cells for keypad (including single "BASE") // - flash styles (140ms color 63) // - minimal hint row base := a.engine.CurrentBase() return lipgloss.NewStyle().Padding(1, 2).Render( "gralculator skeleton\n\n" + "Large number area (TODO)\n" + "Current base: " + string(base) + " (press Tab to cycle)\n\n" + "See docs/UI_DESIGN.md and spec.md", ) } // TODO (phase 3): full model with flashes, proper display rendering, // keypad grid, Tab + other key handling wired to engine, resize/centering.