package main import ( "fmt" "os" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) const ( numRows = 5 numCols = 7 cellW = 10 cellH = 1 ) var ( cellStyle = lipgloss.NewStyle().Width(cellW).Height(cellH).Align(lipgloss.Center) selectedStyle = cellStyle.Copy().Bold(true).Background(lipgloss.Color("12")).Foreground(lipgloss.Color("15")) unselectedStyle = cellStyle.Copy().Background(lipgloss.Color("236")).Foreground(lipgloss.Color("250")) ) type model struct { cursorRow int cursorCol int } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": return m, tea.Quit case "up": if m.cursorRow > 0 { m.cursorRow-- } case "down": if m.cursorRow < numRows-1 { m.cursorRow++ } case "left": if m.cursorCol > 0 { m.cursorCol-- } case "right": if m.cursorCol < numCols-1 { m.cursorCol++ } } } return m, nil } func (m model) View() string { var out string count := 1 for r := 0; r < numRows; r++ { for c := 0; c < numCols; c++ { label := fmt.Sprintf("Cell %02d", count) if r == m.cursorRow && c == m.cursorCol { out += selectedStyle.Render(label) } else { out += unselectedStyle.Render(label) } count++ } out += "\n" } out += "\nUse arrow keys to move, q to quit." return out } func main() { p := tea.NewProgram(model{}) if err := p.Start(); err != nil { fmt.Fprintf(os.Stderr, "error: %v", err) os.Exit(1) } }