gralculator/internal/ui/ui.go
Grok 2a26148c8a chore(skeleton): minimal Go project layout + build system
- 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.
2026-06-06 14:28:59 +01:00

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.