clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

fix: root handler KeyMsg->KeyPressMsg to prevent release double-toggle, add mouse debug telemetry

Dami committed 15aadfb
Two fixes:

1. Root update.go: change 'case tea.KeyMsg' to 'case tea.KeyPressMsg'.
   In bubbletea v2, tea.KeyMsg is an interface matching both KeyPressMsg
   and KeyReleaseMsg. With keyboard enhancements enabled, Ctrl+E would
   toggle debug on press then immediately off on release. Now only
   presses trigger root key actions. KeyReleaseMsg still reaches games
   via the state dispatch fallthrough.

2. Nonogram gridOrigin: compute centering from lipgloss.Width/Height
   of the actual View() output. The previous version computed content
   size incorrectly, causing every mouse click to report 'miss'.

Debug overlay (Ctrl+E) now shows:
- Term Size, Grid Origin, Last Mouse (screen coords + hit result),
  Paint/Drag state, supportsKeyRelease flag
commit
15aadfb465dde22b6db065c473e722fa1e4fbadd
parent
bfa7389bcd1b931d55a2f42f1ee8e15525881b46
author
Dami <dami.etoile@gmail.com>
2 files changed, +37 -28
Mnonogram/mouse.go63+++++++++++++++++-------------
Mupdate.go2+-
M nonogram/mouse.go +36 -27
@@ -1,6 +1,8 @@
11 package nonogram22 33 import (4+ "strings"5+46 "charm.land/lipgloss/v2"57 "github.com/FelineStateMachine/puzzletea/game"68 )
@@ -68,10 +70,30 @@ func localToCell(pos, count, unitWidth int) int {
6870 }6971 7072 // gridOrigin computes the screen position of the top-left corner of the71-// first grid cell by rendering the same components as View() and measuring72-// their dimensions.73+// first grid cell. Mouse coordinates are terminal-absolute, so we need to74+// account for how the root model centers the game's View() output.75+//76+// The root view does:77+//78+// lipgloss.Place(termWidth, termHeight, Center, Center, gameView)79+//80+// So the centering offset is (termWidth - viewWidth) / 2 for X and81+// (termHeight - viewHeight) / 2 for Y.82+//83+// Within the game view, the grid cell (0,0) is offset by the row hint84+// area width (X) and the title + column hint area height (Y).7385 func (m *Model) gridOrigin() (x, y int) {74- // Render the same components View() renders to get exact measurements.86+ // Render the game view to get its exact dimensions.87+ gameView := m.View()88+ viewWidth := lipgloss.Width(gameView)89+ viewHeight := lipgloss.Height(gameView)90+91+ // Centering offset applied by the root's lipgloss.Place().92+ centerX := max((m.termWidth-viewWidth)/2, 0)93+ centerY := max((m.termHeight-viewHeight)/2, 0)94+95+ // Within the game view, find where cell (0,0) starts.96+ // Render the subcomponents to measure their sizes.7597 maxWidth := m.rowHints.RequiredLen() * cellWidth7698 maxHeight := m.colHints.RequiredLen()7799
@@ -79,36 +101,23 @@ func (m *Model) gridOrigin() (x, y int) {
79101 rowHints := rowHintView(m.rowHints, maxWidth, m.currentHints.rows)80102 colHints := colHintView(m.colHints, maxHeight, m.currentHints.cols)81103 spacer := baseStyle.Width(maxWidth).Height(maxHeight).Render("")82- g := gridView(m.grid, m.cursor, m.solved)83- status := statusBarView(m.showFullHelp)84104 85- // Reproduce the exact layout from View().86105 s1 := lipgloss.JoinHorizontal(lipgloss.Bottom, spacer, colHints)87- s2 := lipgloss.JoinHorizontal(lipgloss.Top, rowHints, g)88- gridBlock := lipgloss.JoinVertical(lipgloss.Center, s1, s2)89- fullContent := lipgloss.JoinVertical(lipgloss.Center, title, gridBlock, status)106+ gridBlock := lipgloss.JoinVertical(lipgloss.Center, s1, "placeholder")90107 91- // Measure the full content dimensions for centering.92- contentWidth := lipgloss.Width(fullContent)93- contentHeight := lipgloss.Height(fullContent)94-95- // CenterView uses lipgloss.Place(termWidth, termHeight, Center, Center, ...).96- centerOffsetX := max((m.termWidth-contentWidth)/2, 0)97- centerOffsetY := max((m.termHeight-contentHeight)/2, 0)108+ // The grid block may be narrower than the full view (title or status109+ // could be wider). JoinVertical(Center) pads to the widest line.110+ gridBlockWidth := lipgloss.Width(gridBlock)111+ gridBlockPadLeft := max((viewWidth-gridBlockWidth)/2, 0)98112 99- // Grid cell (0,0) is offset from the content's top-left by:100- // - X: the row hint area width101- // - Y: title height + column hint area height113+ // The row hint width within the grid block.102114 hintAreaWidth := lipgloss.Width(rowHints)103- titleAreaHeight := lipgloss.Height(title)104- colHintAreaHeight := lipgloss.Height(s1)105115 106- // Within the full content, JoinVertical(Center) pads all lines to max107- // width. The grid block may be centered within that width.108- gridBlockWidth := lipgloss.Width(gridBlock)109- gridBlockPadLeft := max((contentWidth-gridBlockWidth)/2, 0)116+ // Vertical: title lines + column hint area lines.117+ titleHeight := strings.Count(title, "\n") + 1118+ colHintAreaHeight := strings.Count(s1, "\n") + 1110119 111- x = centerOffsetX + gridBlockPadLeft + hintAreaWidth112- y = centerOffsetY + titleAreaHeight + colHintAreaHeight120+ x = centerX + gridBlockPadLeft + hintAreaWidth121+ y = centerY + titleHeight + colHintAreaHeight113122 return x, y114123 }
M update.go +1 -1