react-chess-puzzle-kit 1.0.1 → 1.0.3
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.
- package/LICENSE +21 -21
- package/README.md +331 -331
- package/dist/features/board/LineBoard.d.ts +2 -1
- package/dist/features/board/PuzzleBoardWithControls.d.ts +7 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +83 -34
- package/dist/index.js +82 -33
- package/package.json +87 -87
package/dist/index.js
CHANGED
|
@@ -66,7 +66,9 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
|
|
|
66
66
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
67
67
|
const [showAnswerArrow, setShowAnswerArrow] = react.useState(false);
|
|
68
68
|
const [incorrectActive, setIncorrectActive] = react.useState(false);
|
|
69
|
+
const attemptMissedRef = react.useRef(false);
|
|
69
70
|
const { revision, bumpRevision } = reactChessCore.useBoardRevision();
|
|
71
|
+
const { correctMoveSquare, showCorrectMove, clearCorrectMoveFeedback, } = reactChessCore.useCorrectMoveFeedback();
|
|
70
72
|
const boardOrientationRef = react.useRef('white');
|
|
71
73
|
const boardFenRef = react.useRef(EMPTY_BOARD_FEN);
|
|
72
74
|
const notifyHost = () => {
|
|
@@ -92,7 +94,9 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
|
|
|
92
94
|
expectedUci: expectedUci || null,
|
|
93
95
|
positionFen,
|
|
94
96
|
answerArrowColor,
|
|
95
|
-
|
|
97
|
+
// Refutation + answer-arrow flows must run the full wrong→refutation→answer
|
|
98
|
+
// sequence; the replay "retry without arrow" setting does not apply here.
|
|
99
|
+
autoShowWrongMoves: useRefutation ? true : autoShowWrongMoves,
|
|
96
100
|
engineOptions: refutationEngine,
|
|
97
101
|
});
|
|
98
102
|
const missPhase = (_c = missBoard.missSequence.sequence) === null || _c === void 0 ? void 0 : _c.phase;
|
|
@@ -102,8 +106,10 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
|
|
|
102
106
|
react.useEffect(() => {
|
|
103
107
|
setShowAnswerArrow(false);
|
|
104
108
|
setIncorrectActive(false);
|
|
109
|
+
attemptMissedRef.current = false;
|
|
110
|
+
clearCorrectMoveFeedback();
|
|
105
111
|
onMissFeedbackChange === null || onMissFeedbackChange === void 0 ? void 0 : onMissFeedbackChange(null);
|
|
106
|
-
}, [onMissFeedbackChange, position]);
|
|
112
|
+
}, [clearCorrectMoveFeedback, onMissFeedbackChange, position]);
|
|
107
113
|
react.useEffect(() => {
|
|
108
114
|
var _a, _b;
|
|
109
115
|
if (!onMissFeedbackChange) {
|
|
@@ -161,7 +167,10 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
|
|
|
161
167
|
(missBoard.boardAnimating ||
|
|
162
168
|
missPhase === 'wrong' ||
|
|
163
169
|
missPhase === 'refutation');
|
|
164
|
-
const arePiecesDraggable = position !== null &&
|
|
170
|
+
const arePiecesDraggable = position !== null &&
|
|
171
|
+
!positionLocked &&
|
|
172
|
+
!missLocked &&
|
|
173
|
+
correctMoveSquare === null;
|
|
165
174
|
const onPieceDrop = (sourceSquare, targetSquare, piece) => {
|
|
166
175
|
if (!position || positionLocked || position.isSolutionRevealed()) {
|
|
167
176
|
return false;
|
|
@@ -183,6 +192,7 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
|
|
|
183
192
|
recordIfIncorrect: !(answerArrowVisible && !allowRetryOnIncorrect),
|
|
184
193
|
});
|
|
185
194
|
if (!guess.accepted) {
|
|
195
|
+
attemptMissedRef.current = true;
|
|
186
196
|
onFeedback({
|
|
187
197
|
index: position.getIndex(),
|
|
188
198
|
guess: { sourceSquare, targetSquare, piece },
|
|
@@ -225,38 +235,47 @@ const PuzzlePlaySurface = ({ position, onFeedback, incInteractionNum, boardWidth
|
|
|
225
235
|
setIncorrectActive(false);
|
|
226
236
|
missBoard.missSequence.clearSequence();
|
|
227
237
|
onMissFeedbackChange === null || onMissFeedbackChange === void 0 ? void 0 : onMissFeedbackChange(null);
|
|
228
|
-
|
|
238
|
+
clearCorrectMoveFeedback();
|
|
239
|
+
const assistedByAnswerArrow = answerArrowVisible && attemptMissedRef.current;
|
|
240
|
+
const guessPayload = {
|
|
229
241
|
index: position.getIndex(),
|
|
230
242
|
guess: { sourceSquare, targetSquare, piece },
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return true;
|
|
243
|
+
};
|
|
244
|
+
if (assistedByAnswerArrow) {
|
|
245
|
+
// Miss feedback for this ply is already saved; dragging along the answer
|
|
246
|
+
// arrow only continues the line — it must not count as a clean solve.
|
|
247
|
+
if (guess.finished) {
|
|
248
|
+
onFeedback(Object.assign(Object.assign({}, guessPayload), { isCorrect: false, isFinished: true }));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
onFeedback(Object.assign(Object.assign({}, guessPayload), { isCorrect: true, isFinished: guess.finished }));
|
|
242
253
|
}
|
|
243
254
|
position.next();
|
|
255
|
+
boardFenRef.current = position.fen();
|
|
244
256
|
notifyHost();
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
257
|
+
const finishCorrectFeedback = () => {
|
|
258
|
+
position.resetInteractions();
|
|
259
|
+
notifyHost();
|
|
260
|
+
if (position.isAlternativeCheckmate()) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (position.hasResumeConfig()) {
|
|
264
|
+
onResumeCorrect === null || onResumeCorrect === void 0 ? void 0 : onResumeCorrect(position);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
250
267
|
if (!position.isFinished()) {
|
|
251
268
|
position.next();
|
|
269
|
+
boardFenRef.current = position.fen();
|
|
252
270
|
}
|
|
253
271
|
notifyHost();
|
|
254
|
-
}
|
|
272
|
+
};
|
|
273
|
+
showCorrectMove(targetSquare, finishCorrectFeedback);
|
|
255
274
|
return true;
|
|
256
275
|
};
|
|
257
276
|
return (jsxRuntime.jsx(reactChessCore.ChessboardDnDProvider, { children: hasBoard ? (jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: (_e = position === null || position === void 0 ? void 0 : position.getCheckSquare()) !== null && _e !== void 0 ? _e : '', hintSquare: (_f = position === null || position === void 0 ? void 0 : position.getHintSquare()) !== null && _f !== void 0 ? _f : null, incorrectMoveSquare: showAnswerArrowOnIncorrect
|
|
258
277
|
? null
|
|
259
|
-
: ((_g = position === null || position === void 0 ? void 0 : position.getIncorrectMoveSquare()) !== null && _g !== void 0 ? _g : null), customArrows: customArrows, onPieceDrop: onPieceDrop, position: displayFen, boardOrientation: boardOrientation, arePiecesDraggable: arePiecesDraggable, areArrowsAllowed: false, promotionDialogVariant: "modal", animationDuration: 0 }, revision)) : null }));
|
|
278
|
+
: ((_g = position === null || position === void 0 ? void 0 : position.getIncorrectMoveSquare()) !== null && _g !== void 0 ? _g : null), correctMoveSquare: correctMoveSquare, customArrows: customArrows, onPieceDrop: onPieceDrop, position: displayFen, boardOrientation: boardOrientation, arePiecesDraggable: arePiecesDraggable, areArrowsAllowed: false, promotionDialogVariant: "modal", animationDuration: 0 }, revision)) : null }));
|
|
260
279
|
};
|
|
261
280
|
|
|
262
281
|
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 }));
|
|
@@ -721,7 +740,7 @@ const puzzlePositionFromFetch = (fen, moves, resume) => {
|
|
|
721
740
|
const AUTO_ADVANCE_ON_COMPLETE_DELAY_MS = 700;
|
|
722
741
|
const SOLUTION_STEP_MS = 500;
|
|
723
742
|
const RESUME_AUTO_STEP_MS = 500;
|
|
724
|
-
const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls = defaultRenderControls, renderAnalysisSidebar, renderAnalysisContainer, renderEngineEvaluation, renderBoardCaption, renderBoardFeedback, puzzleBoardWidth = DEFAULT_PUZZLE_BOARD_WIDTH, analysisLayout = reactChessCore.DEFAULT_ANALYSIS_LAYOUT, renderAnalysisMain, engine, autoAdvanceOnComplete = false, autoAdvanceOnCompleteAfterIncorrect = false, revealAnswerOnIncorrect = false, showAnswerArrowOnIncorrect = false, allowRetryOnIncorrect = true, showRefutationOnIncorrect, autoShowWrongMoves = true, refutationEngine, answerArrowColor, }) => {
|
|
743
|
+
const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls = defaultRenderControls, renderAnalysisSidebar, renderAnalysisContainer, renderEngineEvaluation, renderBoardCaption, renderBoardFeedback, puzzleBoardWidth = DEFAULT_PUZZLE_BOARD_WIDTH, analysisLayout = reactChessCore.DEFAULT_ANALYSIS_LAYOUT, analysisBoardWidth, renderAnalysisMain, engine, autoAdvanceOnComplete = false, autoAdvanceOnCompleteAfterIncorrect = false, revealAnswerOnIncorrect = false, showAnswerArrowOnIncorrect = false, allowRetryOnIncorrect = true, showRefutationOnIncorrect, autoShowWrongMoves = true, refutationEngine, answerArrowColor, }) => {
|
|
725
744
|
var _a, _b, _c, _d;
|
|
726
745
|
const refutationOnIncorrect = showRefutationOnIncorrect !== null && showRefutationOnIncorrect !== void 0 ? showRefutationOnIncorrect : showAnswerArrowOnIncorrect;
|
|
727
746
|
const stackControlsBelow = useStackPuzzleControlsBelow();
|
|
@@ -733,6 +752,7 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
733
752
|
const [loadingNextPuzzle, setLoadingNextPuzzle] = react.useState(true);
|
|
734
753
|
const [puzzleNum, setPuzzleNum] = react.useState(0);
|
|
735
754
|
const [hasIncorrectAttempt, setHasIncorrectAttempt] = react.useState(false);
|
|
755
|
+
const [hintUsed, setHintUsed] = react.useState(false);
|
|
736
756
|
const [puzzleComplete, setPuzzleComplete] = react.useState(false);
|
|
737
757
|
const [completedAfterMiss, setCompletedAfterMiss] = react.useState(false);
|
|
738
758
|
const [missFeedback, setMissFeedback] = react.useState(null);
|
|
@@ -758,6 +778,7 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
758
778
|
let cancelled = false;
|
|
759
779
|
setLoadingNextPuzzle(true);
|
|
760
780
|
setHasIncorrectAttempt(false);
|
|
781
|
+
setHintUsed(false);
|
|
761
782
|
setPuzzleComplete(false);
|
|
762
783
|
setCompletedAfterMiss(false);
|
|
763
784
|
setMissFeedback(null);
|
|
@@ -794,12 +815,18 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
794
815
|
const incorrectThisFeedback = feedbackData.hintRequested ||
|
|
795
816
|
feedbackData.solutionShown ||
|
|
796
817
|
feedbackData.isCorrect === false;
|
|
818
|
+
if (feedbackData.hintRequested) {
|
|
819
|
+
setHintUsed(true);
|
|
820
|
+
}
|
|
797
821
|
if (incorrectThisFeedback) {
|
|
798
822
|
setHasIncorrectAttempt(true);
|
|
799
823
|
}
|
|
800
824
|
if (feedbackData.isFinished) {
|
|
801
825
|
setPuzzleComplete(true);
|
|
802
|
-
setCompletedAfterMiss(
|
|
826
|
+
setCompletedAfterMiss((prev) => prev ||
|
|
827
|
+
hasIncorrectAttempt ||
|
|
828
|
+
incorrectThisFeedback ||
|
|
829
|
+
feedbackData.hintRequested === true);
|
|
803
830
|
}
|
|
804
831
|
onFeedback(feedbackData);
|
|
805
832
|
};
|
|
@@ -1003,10 +1030,11 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
1003
1030
|
};
|
|
1004
1031
|
const analysis = usePuzzleAnalysis(position, resultStatus, puzzleNum);
|
|
1005
1032
|
const analysisSnapshot = analysis.isOpen && analysis.snapshot ? analysis.snapshot : null;
|
|
1033
|
+
const resolvedAnalysisBoardWidth = analysisBoardWidth !== null && analysisBoardWidth !== void 0 ? analysisBoardWidth : analysisLayout.boardWidth;
|
|
1006
1034
|
const useHostAnalysisUi = Boolean(renderAnalysisSidebar &&
|
|
1007
1035
|
renderAnalysisContainer &&
|
|
1008
1036
|
(renderEngineEvaluation || (engine === null || engine === void 0 ? void 0 : engine.enabled) === false));
|
|
1009
|
-
return (jsxRuntime.jsx(reactChessCore.ThemeProvider, { theme: theme, boardTheme: boardTheme, children: analysisSnapshot ? (jsxRuntime.jsx(reactChessCore.AnalysisErrorBoundary, { onClose: analysis.closeAnalysis, children: useHostAnalysisUi ? (jsxRuntime.jsx(reactChessCore.AnalysisBoardCore, { analysisContext: analysisSnapshot, onClose: analysis.closeAnalysis, theme: theme, boardWidth:
|
|
1037
|
+
return (jsxRuntime.jsx(reactChessCore.ThemeProvider, { theme: theme, boardTheme: boardTheme, children: analysisSnapshot ? (jsxRuntime.jsx(reactChessCore.AnalysisErrorBoundary, { onClose: analysis.closeAnalysis, children: useHostAnalysisUi ? (jsxRuntime.jsx(reactChessCore.AnalysisBoardCore, { analysisContext: analysisSnapshot, onClose: analysis.closeAnalysis, theme: theme, boardWidth: resolvedAnalysisBoardWidth, engine: engine, renderMain: renderAnalysisMain !== null && renderAnalysisMain !== void 0 ? renderAnalysisMain : (({ board, sidebar, model }) => (jsxRuntime.jsx(reactChessCore.AnalysisBoardLayout, { layout: analysisLayout, model: model, board: board, sidebar: sidebar }))), renderSidebar: renderAnalysisSidebar, renderContainer: renderAnalysisContainer, renderEngineEvaluation: renderEngineEvaluation !== null && renderEngineEvaluation !== void 0 ? renderEngineEvaluation : (() => null) })) : (jsxRuntime.jsx(reactChessCore.AnalysisBoard, { analysisContext: analysisSnapshot, onClose: analysis.closeAnalysis, theme: theme, layout: analysisLayout, engine: engine, renderMain: renderAnalysisMain, renderSidebar: renderAnalysisSidebar, renderContainer: renderAnalysisContainer, renderEngineEvaluation: renderEngineEvaluation })) })) : (jsxRuntime.jsxs("div", { style: puzzlePlayRowStyle(controlsPlacement), children: [jsxRuntime.jsxs("div", { style: puzzleBoardColumnStyle(puzzleBoardWidth, controlsPlacement), children: [jsxRuntime.jsx("div", { style: puzzleBoardSlotWrapperStyle(), children: jsxRuntime.jsx("div", { style: puzzleBoardSlotStyle(), children: jsxRuntime.jsx(PuzzlePlaySurface, { position: position, boardWidth: puzzleBoardWidth, onFeedback: handleFeedback, incInteractionNum: incInteractionNum, onResumeCorrect: runResumeAutoAdvance, revealAnswerOnIncorrect: revealAnswerOnIncorrect, showAnswerArrowOnIncorrect: showAnswerArrowOnIncorrect, allowRetryOnIncorrect: allowRetryOnIncorrect, showRefutationOnIncorrect: refutationOnIncorrect, autoShowWrongMoves: autoShowWrongMoves, refutationEngine: refutationEngine !== null && refutationEngine !== void 0 ? refutationEngine : engine, answerArrowColor: answerArrowColor, positionLocked: loadingNextPuzzle, onMissFeedbackChange: setMissFeedback }) }) }), renderBoardCaption && (jsxRuntime.jsx("div", { style: puzzleBoardCaptionSlotStyle(), children: renderBoardCaption({
|
|
1010
1038
|
sideToMove: (_a = position === null || position === void 0 ? void 0 : position.getSideToMove()) !== null && _a !== void 0 ? _a : null,
|
|
1011
1039
|
playerColor: position
|
|
1012
1040
|
? position.getPlayerColor()
|
|
@@ -1018,6 +1046,7 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
1018
1046
|
missPhase: (_c = missFeedback === null || missFeedback === void 0 ? void 0 : missFeedback.phase) !== null && _c !== void 0 ? _c : null,
|
|
1019
1047
|
answerArrowVisible: (_d = missFeedback === null || missFeedback === void 0 ? void 0 : missFeedback.answerArrowVisible) !== null && _d !== void 0 ? _d : false,
|
|
1020
1048
|
completedAfterMiss,
|
|
1049
|
+
hintUsed,
|
|
1021
1050
|
}) }))] }), jsxRuntime.jsxs("div", { style: puzzleControlsSlotStyle(controlsPlacement), children: [renderControls(handleHintRequest, handleShowSolution, handleNextPuzzle, resultStatus, {
|
|
1022
1051
|
visible: analysis.canOpen,
|
|
1023
1052
|
openAnalysis: analysis.openAnalysis,
|
|
@@ -1026,6 +1055,7 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
1026
1055
|
cleanSolve: !hasIncorrectAttempt,
|
|
1027
1056
|
refutationSan: missFeedback === null || missFeedback === void 0 ? void 0 : missFeedback.refutationSan,
|
|
1028
1057
|
missPhase: missFeedback === null || missFeedback === void 0 ? void 0 : missFeedback.phase,
|
|
1058
|
+
hintUsed,
|
|
1029
1059
|
}) }))] })] })) }));
|
|
1030
1060
|
};
|
|
1031
1061
|
|
|
@@ -1034,7 +1064,7 @@ const PuzzleBoardWithControls = ({ theme, boardTheme, apiProxy, renderControls =
|
|
|
1034
1064
|
* side's pieces be dragged. Move validation and sequencing live in
|
|
1035
1065
|
* {@link LineBoardWithControls}.
|
|
1036
1066
|
*/
|
|
1037
|
-
const LineBoard = ({ fen, orientation, trainSide, draggable, onPieceDrop, boardWidth, }) => (jsxRuntime.jsx(reactChessCore.ChessboardDnDProvider, { children: jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: "", hintSquare: null, incorrectMoveSquare: null, position: fen, boardOrientation: orientation, arePiecesDraggable: draggable, isDraggablePiece: ({ piece }) => piece[0] === trainSide, onPieceDrop: (source, target, piece) => onPieceDrop(source, target, piece), autoPromoteToQueen: true, areArrowsAllowed: false, customBoardStyle: { borderRadius: 4 } }) }));
|
|
1067
|
+
const LineBoard = ({ fen, orientation, trainSide, draggable, correctMoveSquare = null, onPieceDrop, boardWidth, }) => (jsxRuntime.jsx(reactChessCore.ChessboardDnDProvider, { children: jsxRuntime.jsx(reactChessCore.HighlightChessboard, { boardWidth: boardWidth, checkSquare: "", hintSquare: null, incorrectMoveSquare: null, correctMoveSquare: correctMoveSquare, position: fen, boardOrientation: orientation, arePiecesDraggable: draggable, isDraggablePiece: ({ piece }) => piece[0] === trainSide, onPieceDrop: (source, target, piece) => onPieceDrop(source, target, piece), autoPromoteToQueen: true, areArrowsAllowed: false, customBoardStyle: { borderRadius: 4 } }) }));
|
|
1038
1068
|
|
|
1039
1069
|
/** Library default line-drill status controls (unstyled). */
|
|
1040
1070
|
const DefaultLineControls = ({ moveNumber, total, finished, isUserTurn, feedback, }) => (jsxRuntime.jsxs("div", { style: rowStyle, children: [jsxRuntime.jsx("span", { style: statusStyle, children: finished ? 'Line complete' : `Move ${moveNumber} of ${total}` }), feedback && !finished && (jsxRuntime.jsx("span", { style: Object.assign(Object.assign({}, statusStyle), { color: feedback.isCorrect ? '#2e7d32' : '#c62828' }), children: feedback.isCorrect
|
|
@@ -1081,6 +1111,8 @@ const LineBoardWithControls = ({ theme, boardTheme, line, onComplete, onMove, re
|
|
|
1081
1111
|
const [currentIndex, setCurrentIndex] = react.useState(0);
|
|
1082
1112
|
const [finished, setFinished] = react.useState(false);
|
|
1083
1113
|
const [feedback, setFeedback] = react.useState(null);
|
|
1114
|
+
const [displayFen, setDisplayFen] = react.useState(null);
|
|
1115
|
+
const { correctMoveSquare, showCorrectMove, clearCorrectMoveFeedback, isShowingCorrectMove, } = reactChessCore.useCorrectMoveFeedback();
|
|
1084
1116
|
const total = line.movesUci.length;
|
|
1085
1117
|
const orientation = boardOrientationForLine(line.trainSide);
|
|
1086
1118
|
const applyMove = react.useCallback((index) => {
|
|
@@ -1105,7 +1137,7 @@ const LineBoardWithControls = ({ theme, boardTheme, line, onComplete, onMove, re
|
|
|
1105
1137
|
}, [line.movesUci]);
|
|
1106
1138
|
// Auto-play opponent moves and detect the end of the line.
|
|
1107
1139
|
react.useEffect(() => {
|
|
1108
|
-
if (finished) {
|
|
1140
|
+
if (finished || isShowingCorrectMove) {
|
|
1109
1141
|
return;
|
|
1110
1142
|
}
|
|
1111
1143
|
if (currentIndex >= total) {
|
|
@@ -1124,6 +1156,7 @@ const LineBoardWithControls = ({ theme, boardTheme, line, onComplete, onMove, re
|
|
|
1124
1156
|
line.trainSide,
|
|
1125
1157
|
applyMove,
|
|
1126
1158
|
opponentMoveDelayMs,
|
|
1159
|
+
isShowingCorrectMove,
|
|
1127
1160
|
]);
|
|
1128
1161
|
// Emit the completion event exactly once.
|
|
1129
1162
|
react.useEffect(() => {
|
|
@@ -1135,15 +1168,16 @@ const LineBoardWithControls = ({ theme, boardTheme, line, onComplete, onMove, re
|
|
|
1135
1168
|
}, [finished]);
|
|
1136
1169
|
const handleDrop = (source, target, piece) => {
|
|
1137
1170
|
var _a, _b, _c;
|
|
1138
|
-
if (finished) {
|
|
1171
|
+
if (finished || isShowingCorrectMove) {
|
|
1139
1172
|
return false;
|
|
1140
1173
|
}
|
|
1141
|
-
|
|
1174
|
+
const setupFen = displayFen !== null && displayFen !== void 0 ? displayFen : chessRef.current.fen();
|
|
1175
|
+
if (turnFromFen(setupFen) !== line.trainSide) {
|
|
1142
1176
|
return false;
|
|
1143
1177
|
}
|
|
1144
1178
|
const index = currentIndex;
|
|
1145
1179
|
const expected = line.movesUci[index];
|
|
1146
|
-
const dropResult = reactChessCore.evaluateExpectedMoveDrop(
|
|
1180
|
+
const dropResult = reactChessCore.evaluateExpectedMoveDrop(setupFen, source, target, piece, expected, true);
|
|
1147
1181
|
if (dropResult.kind === 'illegal' || dropResult.kind === 'ignored') {
|
|
1148
1182
|
return false;
|
|
1149
1183
|
}
|
|
@@ -1153,16 +1187,31 @@ const LineBoardWithControls = ({ theme, boardTheme, line, onComplete, onMove, re
|
|
|
1153
1187
|
const moveFeedback = { index, isCorrect, expectedSan };
|
|
1154
1188
|
setFeedback(moveFeedback);
|
|
1155
1189
|
(_c = onMoveRef.current) === null || _c === void 0 ? void 0 : _c.call(onMoveRef, moveFeedback);
|
|
1156
|
-
|
|
1190
|
+
if (isCorrect) {
|
|
1191
|
+
const nextFen = reactChessCore.fenAfterUci(setupFen, dropResult.uci);
|
|
1192
|
+
if (nextFen) {
|
|
1193
|
+
setDisplayFen(nextFen);
|
|
1194
|
+
}
|
|
1195
|
+
showCorrectMove(target, () => {
|
|
1196
|
+
setDisplayFen(null);
|
|
1197
|
+
setFeedback(null);
|
|
1198
|
+
clearCorrectMoveFeedback();
|
|
1199
|
+
applyMove(index);
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1157
1202
|
return isCorrect;
|
|
1158
1203
|
};
|
|
1204
|
+
const boardFen = displayFen !== null && displayFen !== void 0 ? displayFen : fen;
|
|
1159
1205
|
const moveNumber = Math.min(currentIndex + 1, total);
|
|
1160
|
-
const isUserTurn = !finished &&
|
|
1206
|
+
const isUserTurn = !finished &&
|
|
1207
|
+
!isShowingCorrectMove &&
|
|
1208
|
+
turnFromFen(boardFen) === line.trainSide &&
|
|
1209
|
+
currentIndex < total;
|
|
1161
1210
|
const stackControlsBelow = useStackPuzzleControlsBelow();
|
|
1162
1211
|
const controlsPlacement = stackControlsBelow
|
|
1163
1212
|
? 'below'
|
|
1164
1213
|
: 'beside';
|
|
1165
|
-
return (jsxRuntime.jsx(reactChessCore.ThemeProvider, { theme: theme, boardTheme: boardTheme, children: jsxRuntime.jsxs("div", { style: puzzlePlayRowStyle(controlsPlacement), children: [jsxRuntime.jsx("div", { style: puzzleBoardColumnStyle(boardWidth, controlsPlacement), children: jsxRuntime.jsx("div", { style: puzzleBoardSlotStyle(), children: jsxRuntime.jsx(LineBoard, { fen:
|
|
1214
|
+
return (jsxRuntime.jsx(reactChessCore.ThemeProvider, { theme: theme, boardTheme: boardTheme, children: jsxRuntime.jsxs("div", { style: puzzlePlayRowStyle(controlsPlacement), children: [jsxRuntime.jsx("div", { style: puzzleBoardColumnStyle(boardWidth, controlsPlacement), children: jsxRuntime.jsx("div", { style: puzzleBoardSlotStyle(), children: jsxRuntime.jsx(LineBoard, { fen: boardFen, orientation: orientation, trainSide: line.trainSide, draggable: isUserTurn, correctMoveSquare: correctMoveSquare, onPieceDrop: handleDrop, boardWidth: boardWidth }) }) }), jsxRuntime.jsx("div", { style: puzzleControlsSlotStyle(controlsPlacement), children: renderControls({
|
|
1166
1215
|
trainSide: line.trainSide,
|
|
1167
1216
|
moveNumber,
|
|
1168
1217
|
total,
|
package/package.json
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-chess-puzzle-kit",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "React chess puzzle kit: play, controls, analysis, and browser Stockfish for endchess.training and other apps",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Robert Blackwell",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"module": "dist/index.esm.js",
|
|
9
|
-
"types": "dist/index.d.ts",
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/reblackwell3/react-chess-puzzle-kit.git"
|
|
13
|
-
},
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/reblackwell3/react-chess-puzzle-kit/issues"
|
|
16
|
-
},
|
|
17
|
-
"homepage": "https://github.com/reblackwell3/react-chess-puzzle-kit#readme",
|
|
18
|
-
"keywords": [
|
|
19
|
-
"react",
|
|
20
|
-
"chess",
|
|
21
|
-
"chess.js",
|
|
22
|
-
"chess puzzle",
|
|
23
|
-
"puzzle",
|
|
24
|
-
"analysis",
|
|
25
|
-
"stockfish",
|
|
26
|
-
"react-chessboard",
|
|
27
|
-
"chessboard",
|
|
28
|
-
"drag and drop"
|
|
29
|
-
],
|
|
30
|
-
"files": [
|
|
31
|
-
"dist"
|
|
32
|
-
],
|
|
33
|
-
"scripts": {
|
|
34
|
-
"build": "rollup -c",
|
|
35
|
-
"copy:stockfish": "node scripts/copy-stockfish.mjs",
|
|
36
|
-
"prepare": "rollup -c",
|
|
37
|
-
"prestorybook": "npm run copy:stockfish",
|
|
38
|
-
"test": "jest --config jest.config.cjs",
|
|
39
|
-
"storybook": "storybook dev -p 6006",
|
|
40
|
-
"build-storybook": "storybook build",
|
|
41
|
-
"serve-storybook": "serve storybook-static",
|
|
42
|
-
"prepublishOnly": "npm run build"
|
|
43
|
-
},
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"chess.js": "^1.0.0-beta.8",
|
|
46
|
-
"rollup": "^4.22.2",
|
|
47
|
-
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
48
|
-
"rollup-plugin-typescript2": "^0.36.0",
|
|
49
|
-
"typescript": "^5.6.2"
|
|
50
|
-
},
|
|
51
|
-
"peerDependencies": {
|
|
52
|
-
"chess.js": "^1.0.0-beta.8",
|
|
53
|
-
"react": "^18.3.1",
|
|
54
|
-
"react-chess-core": "^0.1.1",
|
|
55
|
-
"react-chessboard": "^4.7.1"
|
|
56
|
-
},
|
|
57
|
-
"devDependencies": {
|
|
58
|
-
"@chromatic-com/storybook": "^1.9.0",
|
|
59
|
-
"@rollup/plugin-commonjs": "^26.0.1",
|
|
60
|
-
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
61
|
-
"@rollup/plugin-terser": "^0.4.4",
|
|
62
|
-
"@storybook/addon-essentials": "^8.2.9",
|
|
63
|
-
"@storybook/addon-interactions": "^8.2.9",
|
|
64
|
-
"@storybook/addon-links": "^8.2.9",
|
|
65
|
-
"@storybook/addon-onboarding": "^8.2.9",
|
|
66
|
-
"@storybook/blocks": "^8.2.9",
|
|
67
|
-
"@storybook/preset-typescript": "^3.0.0",
|
|
68
|
-
"@storybook/react": "^8.2.9",
|
|
69
|
-
"@storybook/react-vite": "^8.2.9",
|
|
70
|
-
"@storybook/test": "^8.2.9",
|
|
71
|
-
"@types/jest": "^29.5.12",
|
|
72
|
-
"@types/react": "^18.3.12",
|
|
73
|
-
"@types/react-dom": "^18.3.1",
|
|
74
|
-
"chess.js": "^1.0.0-beta.8",
|
|
75
|
-
"jest": "^29.7.0",
|
|
76
|
-
"react": "^18.3.1",
|
|
77
|
-
"react-chess-core": "^0.1.1",
|
|
78
|
-
"react-chessboard": "^4.7.1",
|
|
79
|
-
"storybook": "^8.2.9",
|
|
80
|
-
"ts-jest": "^29.2.4",
|
|
81
|
-
"vite": "^5.4.11",
|
|
82
|
-
"vite-tsconfig-paths": "^5.0.1"
|
|
83
|
-
},
|
|
84
|
-
"optionalDependencies": {
|
|
85
|
-
"stockfish": "^18.0.7"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "react-chess-puzzle-kit",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "React chess puzzle kit: play, controls, analysis, and browser Stockfish for endchess.training and other apps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Robert Blackwell",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.esm.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/reblackwell3/react-chess-puzzle-kit.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/reblackwell3/react-chess-puzzle-kit/issues"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/reblackwell3/react-chess-puzzle-kit#readme",
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react",
|
|
20
|
+
"chess",
|
|
21
|
+
"chess.js",
|
|
22
|
+
"chess puzzle",
|
|
23
|
+
"puzzle",
|
|
24
|
+
"analysis",
|
|
25
|
+
"stockfish",
|
|
26
|
+
"react-chessboard",
|
|
27
|
+
"chessboard",
|
|
28
|
+
"drag and drop"
|
|
29
|
+
],
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "rollup -c",
|
|
35
|
+
"copy:stockfish": "node scripts/copy-stockfish.mjs",
|
|
36
|
+
"prepare": "rollup -c",
|
|
37
|
+
"prestorybook": "npm run copy:stockfish",
|
|
38
|
+
"test": "jest --config jest.config.cjs",
|
|
39
|
+
"storybook": "storybook dev -p 6006",
|
|
40
|
+
"build-storybook": "storybook build",
|
|
41
|
+
"serve-storybook": "serve storybook-static",
|
|
42
|
+
"prepublishOnly": "npm run build"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"chess.js": "^1.0.0-beta.8",
|
|
46
|
+
"rollup": "^4.22.2",
|
|
47
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
48
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
49
|
+
"typescript": "^5.6.2"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"chess.js": "^1.0.0-beta.8",
|
|
53
|
+
"react": "^18.3.1",
|
|
54
|
+
"react-chess-core": "^0.1.1",
|
|
55
|
+
"react-chessboard": "^4.7.1"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@chromatic-com/storybook": "^1.9.0",
|
|
59
|
+
"@rollup/plugin-commonjs": "^26.0.1",
|
|
60
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
61
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
62
|
+
"@storybook/addon-essentials": "^8.2.9",
|
|
63
|
+
"@storybook/addon-interactions": "^8.2.9",
|
|
64
|
+
"@storybook/addon-links": "^8.2.9",
|
|
65
|
+
"@storybook/addon-onboarding": "^8.2.9",
|
|
66
|
+
"@storybook/blocks": "^8.2.9",
|
|
67
|
+
"@storybook/preset-typescript": "^3.0.0",
|
|
68
|
+
"@storybook/react": "^8.2.9",
|
|
69
|
+
"@storybook/react-vite": "^8.2.9",
|
|
70
|
+
"@storybook/test": "^8.2.9",
|
|
71
|
+
"@types/jest": "^29.5.12",
|
|
72
|
+
"@types/react": "^18.3.12",
|
|
73
|
+
"@types/react-dom": "^18.3.1",
|
|
74
|
+
"chess.js": "^1.0.0-beta.8",
|
|
75
|
+
"jest": "^29.7.0",
|
|
76
|
+
"react": "^18.3.1",
|
|
77
|
+
"react-chess-core": "^0.1.1",
|
|
78
|
+
"react-chessboard": "^4.7.1",
|
|
79
|
+
"storybook": "^8.2.9",
|
|
80
|
+
"ts-jest": "^29.2.4",
|
|
81
|
+
"vite": "^5.4.11",
|
|
82
|
+
"vite-tsconfig-paths": "^5.0.1"
|
|
83
|
+
},
|
|
84
|
+
"optionalDependencies": {
|
|
85
|
+
"stockfish": "^18.0.7"
|
|
86
|
+
}
|
|
87
|
+
}
|