From 039e1313043a24444b55896c265d119ec072e07d Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Wed, 15 Jul 2026 19:55:59 +0200 Subject: [PATCH] Rebind char cycling to ,/. instead of [/] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ 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 --- README.md | 2 +- game/model.go | 4 ++-- game/model_test.go | 24 ++++++++++++++++++++++++ game/view.go | 4 ++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cfa0b37..5137dc0 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ fresh enough to reuse without re-learning it. - Arrow keys or `hjkl` — move your cursor - `space` / `enter` — paint - `backspace` / `delete` — erase -- `[` / `]` — cycle the ink character +- `,` / `.` — cycle the ink character (`[` / `]` also work) - `1`-`8` — pick a color - `c` — clear the whole canvas (yes, for everyone — it's a shared wall) - `q` / `ctrl+c` — quit diff --git a/game/model.go b/game/model.go index d7e0b54..2f6b591 100644 --- a/game/model.go +++ b/game/model.go @@ -80,9 +80,9 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { m.canvas.Paint(cur.X, cur.Y, ' ', "") } - case "[": + case ",", "[": m.charIdx = (m.charIdx - 1 + len(charPalette)) % len(charPalette) - case "]": + case ".", "]": m.charIdx = (m.charIdx + 1) % len(charPalette) case "1", "2", "3", "4", "5", "6", "7", "8": diff --git a/game/model_test.go b/game/model_test.go index 3a0de96..22169e1 100644 --- a/game/model_test.go +++ b/game/model_test.go @@ -118,3 +118,27 @@ func newTestRenderer() *lipgloss.Renderer { r.SetColorProfile(termenv.TrueColor) return r } + +func TestCommaPeriodCharCycling(t *testing.T) { + c := NewCanvas() + m := NewModel(c, "test-id", "tester", newTestRenderer()) + c.Join("test-id", "tester", nil) + + mi, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")}) // exit intro + m = mi.(Model) + + t.Logf("charIdx before: %d", m.charIdx) + mi, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(".")}) + m = mi.(Model) + t.Logf("charIdx after '.': %d", m.charIdx) + if m.charIdx != 1 { + t.Fatalf("expected charIdx=1 after '.', got %d", m.charIdx) + } + + mi, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(",")}) + m = mi.(Model) + t.Logf("charIdx after ',': %d", m.charIdx) + if m.charIdx != 0 { + t.Fatalf("expected charIdx=0 after ',', got %d", m.charIdx) + } +} diff --git a/game/view.go b/game/view.go index dafe7d2..b6c3d46 100644 --- a/game/view.go +++ b/game/view.go @@ -42,7 +42,7 @@ func (m Model) renderIntro() string { dim.Render("a shared doodle wall, one keystroke at a time") + "\n\n" + "Everyone connected right now is drawing on the same canvas.\n" + "Move with arrow keys or hjkl, space/enter to paint, backspace to\n" + - "erase. [ and ] cycle the ink character, 1-8 pick a color, c clears\n" + + "erase. , and . cycle the ink character, 1-8 pick a color, c clears\n" + "everything for everyone (yes, really — it's a shared wall).\n\n" + dim.Render("press any key to start") return m.boxStyle().Render(body) @@ -112,7 +112,7 @@ func (m Model) renderCanvas() string { inkSwatch := m.style().Foreground(lipgloss.Color(inkPalette[m.inkColorIdx])).Render(string(charPalette[m.charIdx])) dim := m.style().Foreground(lipgloss.Color("#777777")) - status := fmt.Sprintf("ink %s (1-8 color, [ ] char) peers: %d %s", + status := fmt.Sprintf("ink %s (1-8 color, , . char) peers: %d %s", inkSwatch, m.canvas.PeerCount(), dim.Render("space paint · c clear · q quit")) return m.boxStyle().Render(b.String() + "\n" + status)