- go.mod (1.24.2 + initial bubbletea/lipgloss) - Makefile (build, install to ~/.local/bin/gralculator, test, clean; ldflags version injection) - internal/version (ldflags-compatible String()) - main.go stub (version flag + placeholder) - internal/calc/ (Engine skeleton with Base, CycleBase, IsInteger, ErrConversionNotPossible, FormatForDisplay stub + basic tests for CERR path) - internal/ui/ (App model stub with Tab handling placeholder + lipgloss import for future rendering) This establishes the three-phase foundation. Next: flesh out engine (phase 2), then TUI spike (phase 3). Paper trail continues.
68 lines
1.7 KiB
Go
68 lines
1.7 KiB
Go
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.
|