package ui // gridRows reconstructs the dashboard's visual grid as rows of global cursor // indices, walking sections exactly as bodyLines renders them (one sub-grid of // a.columns() per section). This is what makes Up/Down move to the card the // user actually sees above/below, rather than ±1 through the flat list. func (a App) gridRows() [][]int { cols := a.columns() var rows [][]int idx := 0 for _, s := range a.sections() { for i := 0; i < len(s.games); i += cols { var row []int for j := i; j < i+cols && j < len(s.games); j++ { row = append(row, idx+j) } rows = append(rows, row) } idx += len(s.games) } return rows } // cursorRC locates cursor within rows as (row, col); (0,0) if not found. func cursorRC(rows [][]int, cursor int) (r, c int) { for ri, row := range rows { for ci, gi := range row { if gi == cursor { return ri, ci } } } return 0, 0 } // moveCursorVert moves the cursor up/down one grid row, keeping the column (or // the nearest column when the target row is shorter, e.g. a partial last row or // a different section width). func (a *App) moveCursorVert(d int) { rows := a.gridRows() if len(rows) == 0 { return } r, c := cursorRC(rows, a.cursor) r2 := r + d if r2 < 0 { r2 = 0 } if r2 >= len(rows) { r2 = len(rows) - 1 } if c >= len(rows[r2]) { c = len(rows[r2]) - 1 } a.cursor = rows[r2][c] }