Files
scrawl/game/model.go
Fredrik Johansson 039e131304
All checks were successful
Docker / build-and-push (push) Successful in 50s
Rebind char cycling to ,/. instead of [/]
[ and ] needed a reach/shift on most layouts and felt awkward next to
the otherwise all-unshifted control scheme (hjkl, space, digits). ,
and . are adjacent, unshifted, easy to hit without looking away from
the canvas -- a natural prev/next pair. Old bindings kept as a fallback
rather than removed.

Verified both in isolation (Update() with a KeyMsg) and live over a
real SSH session -- the status-line ink swatch visibly cycles from █
to ▓ on a fresh connection. An initial live test after the rebind
appeared to fail, but that traced to a stale session left over from
the server restart, not an actual bug -- confirmed working correctly
on a fresh connection.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 19:55:59 +02:00

96 lines
2.2 KiB
Go

package game
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var inkPalette = []string{"#ff6b6b", "#feca57", "#1dd1a1", "#54a0ff", "#ff9ff3", "#48dbfb", "#f368e0", "#ffffff"}
var charPalette = []rune{'█', '▓', '▒', '░', '#', '*', '.', '@', 'o', '+'}
type state int
const (
stateIntro state = iota
stateCanvas
)
type Model struct {
canvas *Canvas
id string
label string
state state
renderer *lipgloss.Renderer
inkColorIdx int
charIdx int
width, height int
}
func NewModel(canvas *Canvas, id, label string, renderer *lipgloss.Renderer) Model {
return Model{canvas: canvas, id: id, label: label, state: stateIntro, renderer: renderer}
}
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.WindowSizeMsg:
m.width, m.height = msg.Width, msg.Height
return m, nil
case canvasUpdatedMsg:
// Someone (possibly this session) changed the shared state --
// nothing to do here but re-render; View() always reads a fresh
// snapshot from the canvas.
return m, nil
case tea.KeyMsg:
if m.state == stateIntro {
m.state = stateCanvas
return m, nil
}
return m.handleKey(msg)
}
return m, nil
}
func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "up", "k":
m.canvas.MoveCursor(m.id, 0, -1)
case "down", "j":
m.canvas.MoveCursor(m.id, 0, 1)
case "left", "h":
m.canvas.MoveCursor(m.id, -1, 0)
case "right", "l":
m.canvas.MoveCursor(m.id, 1, 0)
case " ", "enter":
if cur, ok := m.canvas.CursorsSnapshot()[m.id]; ok {
m.canvas.Paint(cur.X, cur.Y, charPalette[m.charIdx], inkPalette[m.inkColorIdx])
}
case "backspace", "delete":
if cur, ok := m.canvas.CursorsSnapshot()[m.id]; ok {
m.canvas.Paint(cur.X, cur.Y, ' ', "")
}
case ",", "[":
m.charIdx = (m.charIdx - 1 + len(charPalette)) % len(charPalette)
case ".", "]":
m.charIdx = (m.charIdx + 1) % len(charPalette)
case "1", "2", "3", "4", "5", "6", "7", "8":
m.inkColorIdx = int(msg.String()[0] - '1')
case "c":
m.canvas.Clear()
}
return m, nil
}