commit e15e4042b3e7d02b47475a82843345224a076444
parent afd967245f8b5ff626d547ea218180f364f39f16
Author: Michael Camilleri <[email protected]>
Date: Sun, 7 Jun 2026 00:44:57 +0900
Hoist clue-at-position lookup onto Puzzle
Both ClueBar and ClueList carried an identical private
clue(atRow:col:direction:) helper for resolving the replay playhead's
cursor to a clue. The two copies were character-for-character the same
and would have invited a third the next time a surface needed
replay-aware clue resolution. This commit moves the resolver onto Puzzle
and routes both views through session.puzzle.clue(...).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Diffstat:
3 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/Crossmate/Models/Puzzle.swift b/Crossmate/Models/Puzzle.swift
@@ -357,6 +357,16 @@ struct Puzzle: Sendable {
return result.count > 1 ? result : []
}
+ /// Returns the clue for the word containing `(row, col)` in the given
+ /// direction, or `nil` if the cell isn't part of a numbered word.
+ func clue(atRow row: Int, col: Int, direction: Direction) -> Clue? {
+ guard let number = wordCells(atRow: row, col: col, direction: direction).first?.number else {
+ return nil
+ }
+ let clues = direction == .across ? acrossClues : downClues
+ return clues.first { $0.number == number }
+ }
+
/// Returns the canonical cursor track for a focused cell: the start cell
/// of the answer slot in `direction`, paired with that direction. This is
/// the low-frequency collaborative presence value persisted to CloudKit;
diff --git a/Crossmate/Views/ClueList.swift b/Crossmate/Views/ClueList.swift
@@ -197,7 +197,7 @@ struct ClueList: View {
let direction = replayFrame?.cursorDirection
else { return nil }
return ClueDisplay(
- clue: clue(atRow: position.row, col: position.col, direction: direction),
+ clue: session.puzzle.clue(atRow: position.row, col: position.col, direction: direction),
direction: direction
)
}
@@ -211,14 +211,6 @@ struct ClueList: View {
}
}
- private func clue(atRow row: Int, col: Int, direction: Puzzle.Direction) -> Puzzle.Clue? {
- guard let number = session.puzzle.wordCells(atRow: row, col: col, direction: direction).first?.number else {
- return nil
- }
- let clues = direction == .across ? session.puzzle.acrossClues : session.puzzle.downClues
- return clues.first { $0.number == number }
- }
-
@ViewBuilder
private func row(
for clue: Puzzle.Clue,
diff --git a/Crossmate/Views/PuzzleView.swift b/Crossmate/Views/PuzzleView.swift
@@ -1556,7 +1556,7 @@ private struct ClueBar: View {
let position = replayClueTarget.position
guard let direction = replayClueTarget.direction else { return nil }
return ClueDisplay(
- clue: clue(atRow: position.row, col: position.col, direction: direction),
+ clue: session.puzzle.clue(atRow: position.row, col: position.col, direction: direction),
direction: direction
)
}
@@ -1570,14 +1570,6 @@ private struct ClueBar: View {
}
}
- private func clue(atRow row: Int, col: Int, direction: Puzzle.Direction) -> Puzzle.Clue? {
- guard let number = session.puzzle.wordCells(atRow: row, col: col, direction: direction).first?.number else {
- return nil
- }
- let clues = direction == .across ? session.puzzle.acrossClues : session.puzzle.downClues
- return clues.first { $0.number == number }
- }
-
private func label(for clue: Puzzle.Clue?, direction: Puzzle.Direction) -> String {
let direction = direction == .across ? "Across" : "Down"
if let clue {