react-chess-puzzle-kit 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,8 @@ export declare abstract class Position implements Traversable {
9
9
  protected i: number;
10
10
  constructor();
11
11
  getIndex(): number;
12
+ /** UCI of the move that produced the current position. */
13
+ getLastMoveUci(): string | null;
12
14
  next(): boolean;
13
15
  prev(): boolean;
14
16
  isFinished(): boolean;
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
- import { useBoardRevision, useCorrectMoveFeedback, useIncorrectMoveFeedback, useMissBoard, HighlightChessboard, DEFAULT_ANSWER_ARROW_COLOR, uciFromDrop, fenAtPlyFromStart, useSolutionLineRecap, ThemeProvider, AnalysisErrorBoundary, AnalysisBoardCore, AnalysisBoardLayout, AnalysisBoard, BoardCompleteCheckOverlay, DEFAULT_ANALYSIS_LAYOUT, AUTO_ADVANCE_ON_COMPLETE_DELAY_MS, lastMoveUciAtPly, evaluateExpectedMoveDrop, fenAfterUci, boardSquareHighlightColors, analysisBoardHighlightColors } from 'react-chess-core';
3
+ import { useBoardRevision, useCorrectMoveFeedback, useIncorrectMoveFeedback, useMissBoard, HighlightChessboard, DEFAULT_ANSWER_ARROW_COLOR, uciFromDrop, fenAtPlyFromStart, useSolutionLineRecap, lastMoveUciAtPly, ThemeProvider, AnalysisErrorBoundary, AnalysisBoardCore, AnalysisBoardLayout, AnalysisBoard, BoardCompleteCheckOverlay, DEFAULT_ANALYSIS_LAYOUT, AUTO_ADVANCE_ON_COMPLETE_DELAY_MS, evaluateExpectedMoveDrop, fenAfterUci, boardSquareHighlightColors, analysisBoardHighlightColors } from 'react-chess-core';
4
4
  export { DEFAULT_ANALYSIS_LAYOUT } from 'react-chess-core';
5
5
  import { Chess } from 'chess.js';
6
6
 
@@ -61,7 +61,7 @@ const EMPTY_BOARD_FEN = '8/8/8/8/8/8/8/8 w - - 0 1';
61
61
  * visible while the next position loads so layout and perspective do not flicker.
62
62
  */
63
63
  const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth, onResumeCorrect, revealAnswerOnIncorrect = false, showAnswerArrowOnIncorrect = false, allowRetryOnIncorrect = true, showRefutationOnIncorrect = false, autoShowWrongMoves = true, refutationEngine, answerArrowColor = DEFAULT_ANSWER_ARROW_COLOR, positionLocked = false, onMissFeedbackChange, recapBoard = null, }) => {
64
- var _a, _b, _c, _d, _e, _f;
64
+ var _a, _b, _c, _d, _e, _f, _g;
65
65
  const [showAnswerArrow, setShowAnswerArrow] = useState(false);
66
66
  const [incorrectActive, setIncorrectActive] = useState(false);
67
67
  const attemptMissedRef = useRef(false);
@@ -182,6 +182,11 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
182
182
  : useRefutation && incorrectActive
183
183
  ? missBoard.boardPosition
184
184
  : boardFen;
185
+ const lastMoveUci = isRecapping
186
+ ? recapBoard.lastMoveUci
187
+ : useRefutation && incorrectActive
188
+ ? missBoard.lastMoveUci
189
+ : ((_e = position === null || position === void 0 ? void 0 : position.getLastMoveUci()) !== null && _e !== void 0 ? _e : null);
185
190
  const missLocked = useRefutation &&
186
191
  incorrectActive &&
187
192
  (missBoard.boardAnimating ||
@@ -296,7 +301,7 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
296
301
  showCorrectMove(targetSquare, finishCorrectFeedback);
297
302
  return true;
298
303
  };
299
- return hasBoard ? (jsx(HighlightChessboard, { boardWidth: boardWidth, checkSquare: isRecapping ? '' : ((_e = position === null || position === void 0 ? void 0 : position.getCheckSquare()) !== null && _e !== void 0 ? _e : ''), hintSquare: isRecapping ? null : ((_f = position === null || position === void 0 ? void 0 : position.getHintSquare()) !== null && _f !== void 0 ? _f : null), incorrectMoveSquare: isRecapping ? null : overlayIncorrectSquare, refutationMoveSquare: isRecapping ? null : refutationMoveSquare, correctMoveSquare: isRecapping ? null : correctMoveSquare, customArrows: customArrows, lastMoveUci: isRecapping ? recapBoard.lastMoveUci : null, onPieceDrop: onPieceDrop, position: displayFen, boardOrientation: boardOrientation, arePiecesDraggable: arePiecesDraggable, areArrowsAllowed: false, promotionDialogVariant: "modal", animationDuration: isRecapping ? recapBoard.animationDuration : 0 }, revision)) : null;
304
+ return hasBoard ? (jsx(HighlightChessboard, { boardWidth: boardWidth, checkSquare: isRecapping ? '' : ((_f = position === null || position === void 0 ? void 0 : position.getCheckSquare()) !== null && _f !== void 0 ? _f : ''), hintSquare: isRecapping ? null : ((_g = position === null || position === void 0 ? void 0 : position.getHintSquare()) !== null && _g !== void 0 ? _g : null), incorrectMoveSquare: isRecapping ? null : overlayIncorrectSquare, refutationMoveSquare: isRecapping ? null : refutationMoveSquare, correctMoveSquare: isRecapping ? null : correctMoveSquare, customArrows: customArrows, lastMoveUci: lastMoveUci, onPieceDrop: onPieceDrop, position: displayFen, boardOrientation: boardOrientation, arePiecesDraggable: arePiecesDraggable, areArrowsAllowed: false, promotionDialogVariant: "modal", animationDuration: isRecapping ? recapBoard.animationDuration : 0 }, revision)) : null;
300
305
  };
301
306
 
302
307
  const PuzzleBoard = ({ position, onFeedback, incInteractionNum, boardWidth, revealAnswerOnIncorrect = false, showAnswerArrowOnIncorrect = false, allowRetryOnIncorrect = true, answerArrowColor, }) => (jsx(PuzzlePlaySurface, { position: position, onFeedback: onFeedback, incInteractionNum: incInteractionNum, boardWidth: boardWidth, revealAnswerOnIncorrect: revealAnswerOnIncorrect, showAnswerArrowOnIncorrect: showAnswerArrowOnIncorrect, allowRetryOnIncorrect: allowRetryOnIncorrect, answerArrowColor: answerArrowColor }));
@@ -512,6 +517,10 @@ class Position {
512
517
  getIndex() {
513
518
  return this.i;
514
519
  }
520
+ /** UCI of the move that produced the current position. */
521
+ getLastMoveUci() {
522
+ return lastMoveUciAtPly(this.moves, this.i);
523
+ }
515
524
  // Common methods shared by all positions
516
525
  next() {
517
526
  if (this.i >= this.moves.length) {
@@ -1181,10 +1190,12 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
1181
1190
  window.clearTimeout(timer);
1182
1191
  };
1183
1192
  }, [completionCheckVisible]);
1193
+ const analysis = usePuzzleAnalysis(position, resultStatus, puzzleNum);
1184
1194
  const shouldAutoAdvance = autoAdvanceOnComplete &&
1185
1195
  resultStatus === 'complete' &&
1186
1196
  !(hasIncorrectAttempt && !autoAdvanceOnCompleteAfterIncorrect) &&
1187
- (!showCompletionRecap || completionRecapDone);
1197
+ (!showCompletionRecap || completionRecapDone) &&
1198
+ !analysis.isOpen;
1188
1199
  const autoAdvance = usePuzzleAutoAdvanceCountdown(shouldAutoAdvance, autoAdvanceOnCompleteDelayMs, handleNextPuzzle);
1189
1200
  const controlState = {
1190
1201
  canShowHint: position !== null &&
@@ -1194,7 +1205,6 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
1194
1205
  canShowSolution: position !== null &&
1195
1206
  (position.isSolutionRevealed() || !position.isFinished()),
1196
1207
  };
1197
- const analysis = usePuzzleAnalysis(position, resultStatus, puzzleNum);
1198
1208
  const analysisSnapshot = analysis.isOpen && analysis.snapshot ? analysis.snapshot : null;
1199
1209
  const resolvedAnalysisBoardWidth = analysisBoardWidth !== null && analysisBoardWidth !== void 0 ? analysisBoardWidth : analysisLayout.boardWidth;
1200
1210
  const useHostAnalysisUi = Boolean(renderAnalysisSidebar &&
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ const EMPTY_BOARD_FEN = '8/8/8/8/8/8/8/8 w - - 0 1';
62
62
  * visible while the next position loads so layout and perspective do not flicker.
63
63
  */
64
64
  const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth, onResumeCorrect, revealAnswerOnIncorrect = false, showAnswerArrowOnIncorrect = false, allowRetryOnIncorrect = true, showRefutationOnIncorrect = false, autoShowWrongMoves = true, refutationEngine, answerArrowColor = reactChessCore.DEFAULT_ANSWER_ARROW_COLOR, positionLocked = false, onMissFeedbackChange, recapBoard = null, }) => {
65
- var _a, _b, _c, _d, _e, _f;
65
+ var _a, _b, _c, _d, _e, _f, _g;
66
66
  const [showAnswerArrow, setShowAnswerArrow] = react.useState(false);
67
67
  const [incorrectActive, setIncorrectActive] = react.useState(false);
68
68
  const attemptMissedRef = react.useRef(false);
@@ -183,6 +183,11 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
183
183
  : useRefutation && incorrectActive
184
184
  ? missBoard.boardPosition
185
185
  : boardFen;
186
+ const lastMoveUci = isRecapping
187
+ ? recapBoard.lastMoveUci
188
+ : useRefutation && incorrectActive
189
+ ? missBoard.lastMoveUci
190
+ : ((_e = position === null || position === void 0 ? void 0 : position.getLastMoveUci()) !== null && _e !== void 0 ? _e : null);
186
191
  const missLocked = useRefutation &&
187
192
  incorrectActive &&
188
193
  (missBoard.boardAnimating ||
@@ -297,7 +302,7 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
297
302
  showCorrectMove(targetSquare, finishCorrectFeedback);
298
303
  return true;
299
304
  };
300
- return hasBoard ? (jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: isRecapping ? '' : ((_e = position === null || position === void 0 ? void 0 : position.getCheckSquare()) !== null && _e !== void 0 ? _e : ''), hintSquare: isRecapping ? null : ((_f = position === null || position === void 0 ? void 0 : position.getHintSquare()) !== null && _f !== void 0 ? _f : null), incorrectMoveSquare: isRecapping ? null : overlayIncorrectSquare, refutationMoveSquare: isRecapping ? null : refutationMoveSquare, correctMoveSquare: isRecapping ? null : correctMoveSquare, customArrows: customArrows, lastMoveUci: isRecapping ? recapBoard.lastMoveUci : null, onPieceDrop: onPieceDrop, position: displayFen, boardOrientation: boardOrientation, arePiecesDraggable: arePiecesDraggable, areArrowsAllowed: false, promotionDialogVariant: "modal", animationDuration: isRecapping ? recapBoard.animationDuration : 0 }, revision)) : null;
305
+ return hasBoard ? (jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: isRecapping ? '' : ((_f = position === null || position === void 0 ? void 0 : position.getCheckSquare()) !== null && _f !== void 0 ? _f : ''), hintSquare: isRecapping ? null : ((_g = position === null || position === void 0 ? void 0 : position.getHintSquare()) !== null && _g !== void 0 ? _g : null), incorrectMoveSquare: isRecapping ? null : overlayIncorrectSquare, refutationMoveSquare: isRecapping ? null : refutationMoveSquare, correctMoveSquare: isRecapping ? null : correctMoveSquare, customArrows: customArrows, lastMoveUci: lastMoveUci, onPieceDrop: onPieceDrop, position: displayFen, boardOrientation: boardOrientation, arePiecesDraggable: arePiecesDraggable, areArrowsAllowed: false, promotionDialogVariant: "modal", animationDuration: isRecapping ? recapBoard.animationDuration : 0 }, revision)) : null;
301
306
  };
302
307
 
303
308
  const PuzzleBoard = ({ position, onFeedback, incInteractionNum, boardWidth, revealAnswerOnIncorrect = false, showAnswerArrowOnIncorrect = false, allowRetryOnIncorrect = true, answerArrowColor, }) => (jsxRuntime.jsx(PuzzlePlaySurface, { position: position, onFeedback: onFeedback, incInteractionNum: incInteractionNum, boardWidth: boardWidth, revealAnswerOnIncorrect: revealAnswerOnIncorrect, showAnswerArrowOnIncorrect: showAnswerArrowOnIncorrect, allowRetryOnIncorrect: allowRetryOnIncorrect, answerArrowColor: answerArrowColor }));
@@ -513,6 +518,10 @@ class Position {
513
518
  getIndex() {
514
519
  return this.i;
515
520
  }
521
+ /** UCI of the move that produced the current position. */
522
+ getLastMoveUci() {
523
+ return reactChessCore.lastMoveUciAtPly(this.moves, this.i);
524
+ }
516
525
  // Common methods shared by all positions
517
526
  next() {
518
527
  if (this.i >= this.moves.length) {
@@ -1182,10 +1191,12 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
1182
1191
  window.clearTimeout(timer);
1183
1192
  };
1184
1193
  }, [completionCheckVisible]);
1194
+ const analysis = usePuzzleAnalysis(position, resultStatus, puzzleNum);
1185
1195
  const shouldAutoAdvance = autoAdvanceOnComplete &&
1186
1196
  resultStatus === 'complete' &&
1187
1197
  !(hasIncorrectAttempt && !autoAdvanceOnCompleteAfterIncorrect) &&
1188
- (!showCompletionRecap || completionRecapDone);
1198
+ (!showCompletionRecap || completionRecapDone) &&
1199
+ !analysis.isOpen;
1189
1200
  const autoAdvance = usePuzzleAutoAdvanceCountdown(shouldAutoAdvance, autoAdvanceOnCompleteDelayMs, handleNextPuzzle);
1190
1201
  const controlState = {
1191
1202
  canShowHint: position !== null &&
@@ -1195,7 +1206,6 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
1195
1206
  canShowSolution: position !== null &&
1196
1207
  (position.isSolutionRevealed() || !position.isFinished()),
1197
1208
  };
1198
- const analysis = usePuzzleAnalysis(position, resultStatus, puzzleNum);
1199
1209
  const analysisSnapshot = analysis.isOpen && analysis.snapshot ? analysis.snapshot : null;
1200
1210
  const resolvedAnalysisBoardWidth = analysisBoardWidth !== null && analysisBoardWidth !== void 0 ? analysisBoardWidth : analysisLayout.boardWidth;
1201
1211
  const useHostAnalysisUi = Boolean(renderAnalysisSidebar &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-chess-puzzle-kit",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "React chess puzzle kit: play, controls, analysis, and browser Stockfish for endchess.training and other apps",
5
5
  "license": "MIT",
6
6
  "author": "Robert Blackwell",
@@ -68,11 +68,13 @@
68
68
  "@storybook/react": "^8.2.9",
69
69
  "@storybook/react-vite": "^8.2.9",
70
70
  "@storybook/test": "^8.2.9",
71
+ "@testing-library/react": "^16.0.1",
71
72
  "@types/jest": "^29.5.12",
72
73
  "@types/react": "^18.3.12",
73
74
  "@types/react-dom": "^18.3.1",
74
75
  "chess.js": "^1.0.0-beta.8",
75
76
  "jest": "^29.7.0",
77
+ "jest-environment-jsdom": "^29.7.0",
76
78
  "react": "^18.3.1",
77
79
  "react-chess-core": "^0.1.8",
78
80
  "react-chessboard": "^4.7.1",