@react-chess-tools/react-chess-puzzle 1.0.0 → 1.0.2
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/CHANGELOG.md +14 -0
- package/README.md +570 -0
- package/dist/index.cjs +132 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +118 -93
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/components/ChessPuzzle/ChessPuzzle.stories.tsx +46 -0
- package/src/components/ChessPuzzle/parts/Hint.tsx +32 -23
- package/src/components/ChessPuzzle/parts/PuzzleBoard.tsx +28 -27
- package/src/components/ChessPuzzle/parts/Reset.tsx +56 -36
- package/src/components/ChessPuzzle/parts/Root.tsx +14 -2
- package/src/components/ChessPuzzle/parts/__tests__/Hint.test.tsx +158 -0
- package/src/components/ChessPuzzle/parts/__tests__/PuzzleBoard.test.tsx +140 -0
- package/src/components/ChessPuzzle/parts/__tests__/Reset.test.tsx +341 -0
- package/src/components/ChessPuzzle/parts/__tests__/Root.test.tsx +42 -0
- package/src/hooks/__tests__/reducer.test.ts +134 -0
- package/src/hooks/reducer.ts +13 -1
- package/src/hooks/useChessPuzzle.ts +2 -0
- package/src/utils/__tests__/index.test.ts +0 -17
- package/src/utils/index.ts +1 -11
- package/README.MD +0 -344
package/dist/index.d.ts
CHANGED
|
@@ -33,15 +33,23 @@ type Puzzle = {
|
|
|
33
33
|
makeFirstMove?: boolean;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
interface HintProps {
|
|
36
|
+
interface HintProps extends Omit<React__default.ButtonHTMLAttributes<HTMLButtonElement>, "onClick"> {
|
|
37
37
|
asChild?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The puzzle statuses in which the hint button should be visible.
|
|
40
|
+
* @default ["not-started", "in-progress"]
|
|
41
|
+
*/
|
|
38
42
|
showOn?: Status[];
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
interface ResetProps {
|
|
45
|
+
interface ResetProps extends Omit<React__default.ButtonHTMLAttributes<HTMLButtonElement>, "onReset"> {
|
|
42
46
|
asChild?: boolean;
|
|
43
47
|
puzzle?: Puzzle;
|
|
44
48
|
onReset?: (puzzleContext: ChessPuzzleContextType) => void;
|
|
49
|
+
/**
|
|
50
|
+
* The puzzle statuses in which the reset button should be visible.
|
|
51
|
+
* @default ["failed", "solved"]
|
|
52
|
+
*/
|
|
45
53
|
showOn?: Status[];
|
|
46
54
|
}
|
|
47
55
|
|
|
@@ -68,13 +76,19 @@ interface RootProps {
|
|
|
68
76
|
onFail?: (puzzleContext: ChessPuzzleContextType) => void;
|
|
69
77
|
/** Optional theme configuration. Supports partial themes - only override the colors you need. */
|
|
70
78
|
theme?: PartialChessPuzzleTheme;
|
|
79
|
+
/** When true, any checkmate move solves the puzzle (not just the canonical solution). Defaults to true. */
|
|
80
|
+
solveOnCheckmate?: boolean;
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
declare const ChessPuzzle: {
|
|
74
84
|
Root: React.FC<React.PropsWithChildren<RootProps>>;
|
|
75
|
-
Board: React.
|
|
76
|
-
Reset: React.
|
|
77
|
-
|
|
85
|
+
Board: React.ForwardRefExoticComponent<Omit<PuzzleBoardProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
86
|
+
Reset: React.ForwardRefExoticComponent<ResetProps & {
|
|
87
|
+
children?: React.ReactNode | undefined;
|
|
88
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
89
|
+
Hint: React.ForwardRefExoticComponent<HintProps & {
|
|
90
|
+
children?: React.ReactNode | undefined;
|
|
91
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
78
92
|
};
|
|
79
93
|
|
|
80
94
|
declare const useChessPuzzleContext: () => ChessPuzzleContextType;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// src/components/ChessPuzzle/parts/Root.tsx
|
|
2
|
-
import
|
|
2
|
+
import React3 from "react";
|
|
3
3
|
|
|
4
4
|
// src/utils/index.ts
|
|
5
5
|
import { Chess } from "chess.js";
|
|
6
|
-
import React from "react";
|
|
7
6
|
import _ from "lodash";
|
|
8
7
|
|
|
9
8
|
// src/theme/defaults.ts
|
|
@@ -26,7 +25,6 @@ var getOrientation = (puzzle) => {
|
|
|
26
25
|
}
|
|
27
26
|
return game.turn();
|
|
28
27
|
};
|
|
29
|
-
var isClickableElement = (element) => React.isValidElement(element);
|
|
30
28
|
var getCustomSquareStyles = (status, hint, isPlayerTurn, game, nextMove, theme = defaultPuzzleTheme) => {
|
|
31
29
|
const customSquareStyles = {};
|
|
32
30
|
const lastMove = _.last(game.history({ verbose: true }));
|
|
@@ -131,7 +129,17 @@ var reducer = (state, action) => {
|
|
|
131
129
|
status: "in-progress"
|
|
132
130
|
};
|
|
133
131
|
case "PLAYER_MOVE": {
|
|
134
|
-
const { move } = action.payload;
|
|
132
|
+
const { move, game, solveOnCheckmate } = action.payload;
|
|
133
|
+
if (move && solveOnCheckmate !== false && game.isCheckmate()) {
|
|
134
|
+
return {
|
|
135
|
+
...state,
|
|
136
|
+
status: "solved",
|
|
137
|
+
nextMove: null,
|
|
138
|
+
hint: "none",
|
|
139
|
+
isPlayerTurn: false,
|
|
140
|
+
onSolveInvoked: false
|
|
141
|
+
};
|
|
142
|
+
}
|
|
135
143
|
const isMoveRight = [move == null ? void 0 : move.san, move == null ? void 0 : move.lan].includes(
|
|
136
144
|
(state == null ? void 0 : state.nextMove) || ""
|
|
137
145
|
);
|
|
@@ -183,7 +191,7 @@ var reducer = (state, action) => {
|
|
|
183
191
|
|
|
184
192
|
// src/hooks/useChessPuzzle.ts
|
|
185
193
|
import { useChessGameContext } from "@react-chess-tools/react-chess-game";
|
|
186
|
-
var useChessPuzzle = (puzzle, onSolve, onFail) => {
|
|
194
|
+
var useChessPuzzle = (puzzle, onSolve, onFail, solveOnCheckmate = true) => {
|
|
187
195
|
var _a;
|
|
188
196
|
const gameContext = useChessGameContext();
|
|
189
197
|
const [state, dispatch] = useReducer(reducer, { puzzle }, initializePuzzle);
|
|
@@ -262,7 +270,8 @@ var useChessPuzzle = (puzzle, onSolve, onFail) => {
|
|
|
262
270
|
payload: {
|
|
263
271
|
move: ((_c = (_b = gameContext == null ? void 0 : gameContext.game) == null ? void 0 : _b.history({ verbose: true })) == null ? void 0 : _c.pop()) ?? null,
|
|
264
272
|
puzzleContext,
|
|
265
|
-
game
|
|
273
|
+
game,
|
|
274
|
+
solveOnCheckmate
|
|
266
275
|
}
|
|
267
276
|
});
|
|
268
277
|
dispatch({
|
|
@@ -289,10 +298,10 @@ var useChessPuzzle = (puzzle, onSolve, onFail) => {
|
|
|
289
298
|
import { ChessGame } from "@react-chess-tools/react-chess-game";
|
|
290
299
|
|
|
291
300
|
// src/hooks/useChessPuzzleContext.ts
|
|
292
|
-
import
|
|
293
|
-
var ChessPuzzleContext =
|
|
301
|
+
import React from "react";
|
|
302
|
+
var ChessPuzzleContext = React.createContext(null);
|
|
294
303
|
var useChessPuzzleContext = () => {
|
|
295
|
-
const context =
|
|
304
|
+
const context = React.useContext(ChessPuzzleContext);
|
|
296
305
|
if (!context) {
|
|
297
306
|
throw new Error(
|
|
298
307
|
`useChessPuzzleContext must be used within a ChessPuzzle component. Make sure your component is wrapped with <ChessPuzzle.Root> or ensure the ChessPuzzle component is properly rendered in the component tree.`
|
|
@@ -302,7 +311,7 @@ var useChessPuzzleContext = () => {
|
|
|
302
311
|
};
|
|
303
312
|
|
|
304
313
|
// src/theme/context.tsx
|
|
305
|
-
import
|
|
314
|
+
import React2, { createContext, useContext } from "react";
|
|
306
315
|
var ChessPuzzleThemeContext = createContext(defaultPuzzleTheme);
|
|
307
316
|
var useChessPuzzleTheme = () => {
|
|
308
317
|
return useContext(ChessPuzzleThemeContext);
|
|
@@ -311,7 +320,7 @@ var PuzzleThemeProvider = ({
|
|
|
311
320
|
theme,
|
|
312
321
|
children
|
|
313
322
|
}) => {
|
|
314
|
-
return /* @__PURE__ */
|
|
323
|
+
return /* @__PURE__ */ React2.createElement(ChessPuzzleThemeContext.Provider, { value: theme }, children);
|
|
315
324
|
};
|
|
316
325
|
|
|
317
326
|
// src/theme/utils.ts
|
|
@@ -328,131 +337,147 @@ var PuzzleRootInner = ({
|
|
|
328
337
|
puzzle,
|
|
329
338
|
onSolve,
|
|
330
339
|
onFail,
|
|
340
|
+
solveOnCheckmate,
|
|
331
341
|
children
|
|
332
342
|
}) => {
|
|
333
|
-
const context = useChessPuzzle(puzzle, onSolve, onFail);
|
|
334
|
-
return /* @__PURE__ */
|
|
343
|
+
const context = useChessPuzzle(puzzle, onSolve, onFail, solveOnCheckmate);
|
|
344
|
+
return /* @__PURE__ */ React3.createElement(ChessPuzzleContext.Provider, { value: context }, children);
|
|
335
345
|
};
|
|
336
346
|
var Root = ({
|
|
337
347
|
puzzle,
|
|
338
348
|
onSolve,
|
|
339
349
|
onFail,
|
|
340
350
|
theme,
|
|
351
|
+
solveOnCheckmate = true,
|
|
341
352
|
children
|
|
342
353
|
}) => {
|
|
343
|
-
const mergedTheme =
|
|
344
|
-
return /* @__PURE__ */
|
|
354
|
+
const mergedTheme = React3.useMemo(() => mergePuzzleTheme(theme), [theme]);
|
|
355
|
+
return /* @__PURE__ */ React3.createElement(
|
|
345
356
|
ChessGame.Root,
|
|
346
357
|
{
|
|
347
358
|
fen: puzzle.fen,
|
|
348
359
|
orientation: getOrientation(puzzle),
|
|
349
360
|
theme: mergedTheme
|
|
350
361
|
},
|
|
351
|
-
/* @__PURE__ */
|
|
362
|
+
/* @__PURE__ */ React3.createElement(PuzzleThemeProvider, { theme: mergedTheme }, /* @__PURE__ */ React3.createElement(
|
|
363
|
+
PuzzleRootInner,
|
|
364
|
+
{
|
|
365
|
+
puzzle,
|
|
366
|
+
onSolve,
|
|
367
|
+
onFail,
|
|
368
|
+
solveOnCheckmate
|
|
369
|
+
},
|
|
370
|
+
children
|
|
371
|
+
))
|
|
352
372
|
);
|
|
353
373
|
};
|
|
374
|
+
Root.displayName = "ChessPuzzle.Root";
|
|
354
375
|
|
|
355
376
|
// src/components/ChessPuzzle/parts/PuzzleBoard.tsx
|
|
356
|
-
import
|
|
377
|
+
import React4 from "react";
|
|
357
378
|
import {
|
|
358
379
|
ChessGame as ChessGame2,
|
|
359
380
|
deepMergeChessboardOptions,
|
|
360
381
|
useChessGameContext as useChessGameContext2
|
|
361
382
|
} from "@react-chess-tools/react-chess-game";
|
|
362
|
-
var PuzzleBoard = (
|
|
363
|
-
options
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
383
|
+
var PuzzleBoard = React4.forwardRef(
|
|
384
|
+
({ options, ...rest }, ref) => {
|
|
385
|
+
const puzzleContext = useChessPuzzleContext();
|
|
386
|
+
const gameContext = useChessGameContext2();
|
|
387
|
+
const theme = useChessPuzzleTheme();
|
|
388
|
+
if (!puzzleContext) {
|
|
389
|
+
throw new Error("PuzzleContext not found");
|
|
390
|
+
}
|
|
391
|
+
if (!gameContext) {
|
|
392
|
+
throw new Error("ChessGameContext not found");
|
|
393
|
+
}
|
|
394
|
+
const { game } = gameContext;
|
|
395
|
+
const { status, hint, isPlayerTurn, nextMove } = puzzleContext;
|
|
396
|
+
const mergedOptions = deepMergeChessboardOptions(options || {}, {
|
|
397
|
+
squareStyles: getCustomSquareStyles(
|
|
398
|
+
status,
|
|
399
|
+
hint,
|
|
400
|
+
isPlayerTurn,
|
|
401
|
+
game,
|
|
402
|
+
stringToMove(game, nextMove),
|
|
403
|
+
theme
|
|
404
|
+
)
|
|
405
|
+
});
|
|
406
|
+
return /* @__PURE__ */ React4.createElement(ChessGame2.Board, { ref, ...rest, options: mergedOptions });
|
|
374
407
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const mergedOptions = deepMergeChessboardOptions(options, {
|
|
378
|
-
squareStyles: getCustomSquareStyles(
|
|
379
|
-
status,
|
|
380
|
-
hint,
|
|
381
|
-
isPlayerTurn,
|
|
382
|
-
game,
|
|
383
|
-
stringToMove(game, nextMove),
|
|
384
|
-
theme
|
|
385
|
-
)
|
|
386
|
-
});
|
|
387
|
-
return /* @__PURE__ */ React5.createElement(ChessGame2.Board, { ...rest, options: mergedOptions });
|
|
388
|
-
};
|
|
408
|
+
);
|
|
409
|
+
PuzzleBoard.displayName = "ChessPuzzle.PuzzleBoard";
|
|
389
410
|
|
|
390
411
|
// src/components/ChessPuzzle/parts/Reset.tsx
|
|
391
|
-
import
|
|
412
|
+
import React5 from "react";
|
|
413
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
392
414
|
var defaultShowOn = ["failed", "solved"];
|
|
393
|
-
var Reset = (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (isClickableElement(child)) {
|
|
415
|
-
return React6.cloneElement(child, {
|
|
416
|
-
onClick: handleClick
|
|
417
|
-
});
|
|
418
|
-
} else {
|
|
419
|
-
throw new Error("Change child must be a clickable element");
|
|
415
|
+
var Reset = React5.forwardRef(
|
|
416
|
+
({
|
|
417
|
+
children,
|
|
418
|
+
asChild,
|
|
419
|
+
puzzle,
|
|
420
|
+
onReset,
|
|
421
|
+
showOn = defaultShowOn,
|
|
422
|
+
className,
|
|
423
|
+
...rest
|
|
424
|
+
}, ref) => {
|
|
425
|
+
const puzzleContext = useChessPuzzleContext();
|
|
426
|
+
if (!puzzleContext) {
|
|
427
|
+
throw new Error("PuzzleContext not found");
|
|
428
|
+
}
|
|
429
|
+
const { changePuzzle, puzzle: contextPuzzle, status } = puzzleContext;
|
|
430
|
+
const handleClick = React5.useCallback(() => {
|
|
431
|
+
changePuzzle(puzzle || contextPuzzle);
|
|
432
|
+
onReset == null ? void 0 : onReset(puzzleContext);
|
|
433
|
+
}, [changePuzzle, puzzle, contextPuzzle, puzzleContext, onReset]);
|
|
434
|
+
if (!showOn.includes(status)) {
|
|
435
|
+
return null;
|
|
420
436
|
}
|
|
437
|
+
return asChild ? /* @__PURE__ */ React5.createElement(Slot, { ref, onClick: handleClick, className, ...rest }, children) : /* @__PURE__ */ React5.createElement(
|
|
438
|
+
"button",
|
|
439
|
+
{
|
|
440
|
+
ref,
|
|
441
|
+
type: "button",
|
|
442
|
+
className,
|
|
443
|
+
onClick: handleClick,
|
|
444
|
+
...rest
|
|
445
|
+
},
|
|
446
|
+
children
|
|
447
|
+
);
|
|
421
448
|
}
|
|
422
|
-
|
|
423
|
-
|
|
449
|
+
);
|
|
450
|
+
Reset.displayName = "ChessPuzzle.Reset";
|
|
424
451
|
|
|
425
452
|
// src/components/ChessPuzzle/parts/Hint.tsx
|
|
426
|
-
import
|
|
453
|
+
import React6 from "react";
|
|
454
|
+
import { Slot as Slot2 } from "@radix-ui/react-slot";
|
|
427
455
|
var defaultShowOn2 = ["not-started", "in-progress"];
|
|
428
|
-
var Hint = ({
|
|
429
|
-
children,
|
|
430
|
-
asChild,
|
|
431
|
-
showOn = defaultShowOn2
|
|
432
|
-
}) => {
|
|
456
|
+
var Hint = React6.forwardRef(({ children, asChild, showOn = defaultShowOn2, className, ...rest }, ref) => {
|
|
433
457
|
const puzzleContext = useChessPuzzleContext();
|
|
434
458
|
if (!puzzleContext) {
|
|
435
459
|
throw new Error("PuzzleContext not found");
|
|
436
460
|
}
|
|
437
461
|
const { onHint, status } = puzzleContext;
|
|
438
|
-
const handleClick = () => {
|
|
462
|
+
const handleClick = React6.useCallback(() => {
|
|
439
463
|
onHint();
|
|
440
|
-
};
|
|
464
|
+
}, [onHint]);
|
|
441
465
|
if (!showOn.includes(status)) {
|
|
442
466
|
return null;
|
|
443
467
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
};
|
|
468
|
+
return asChild ? /* @__PURE__ */ React6.createElement(Slot2, { ref, onClick: handleClick, className, ...rest }, children) : /* @__PURE__ */ React6.createElement(
|
|
469
|
+
"button",
|
|
470
|
+
{
|
|
471
|
+
ref,
|
|
472
|
+
type: "button",
|
|
473
|
+
className,
|
|
474
|
+
onClick: handleClick,
|
|
475
|
+
...rest
|
|
476
|
+
},
|
|
477
|
+
children
|
|
478
|
+
);
|
|
479
|
+
});
|
|
480
|
+
Hint.displayName = "ChessPuzzle.Hint";
|
|
456
481
|
|
|
457
482
|
// src/components/ChessPuzzle/index.ts
|
|
458
483
|
var ChessPuzzle = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ChessPuzzle/parts/Root.tsx","../src/utils/index.ts","../src/theme/defaults.ts","../src/hooks/useChessPuzzle.ts","../src/hooks/reducer.ts","../src/hooks/useChessPuzzleContext.ts","../src/theme/context.tsx","../src/theme/utils.ts","../src/components/ChessPuzzle/parts/PuzzleBoard.tsx","../src/components/ChessPuzzle/parts/Reset.tsx","../src/components/ChessPuzzle/parts/Hint.tsx","../src/components/ChessPuzzle/index.ts"],"sourcesContent":["import React from \"react\";\nimport { Puzzle, getOrientation } from \"../../../utils\";\nimport {\n ChessPuzzleContextType,\n useChessPuzzle,\n} from \"../../../hooks/useChessPuzzle\";\nimport { ChessGame } from \"@react-chess-tools/react-chess-game\";\nimport { ChessPuzzleContext } from \"../../../hooks/useChessPuzzleContext\";\nimport { PuzzleThemeProvider } from \"../../../theme/context\";\nimport { mergePuzzleTheme } from \"../../../theme/utils\";\nimport type { PartialChessPuzzleTheme } from \"../../../theme/types\";\n\nexport interface RootProps {\n puzzle: Puzzle;\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void;\n onFail?: (puzzleContext: ChessPuzzleContextType) => void;\n /** Optional theme configuration. Supports partial themes - only override the colors you need. */\n theme?: PartialChessPuzzleTheme;\n}\n\ninterface PuzzleRootInnerProps {\n puzzle: Puzzle;\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void;\n onFail?: (puzzleContext: ChessPuzzleContextType) => void;\n children: React.ReactNode;\n}\n\nconst PuzzleRootInner: React.FC<PuzzleRootInnerProps> = ({\n puzzle,\n onSolve,\n onFail,\n children,\n}) => {\n const context = useChessPuzzle(puzzle, onSolve, onFail);\n\n return (\n <ChessPuzzleContext.Provider value={context}>\n {children}\n </ChessPuzzleContext.Provider>\n );\n};\n\nexport const Root: React.FC<React.PropsWithChildren<RootProps>> = ({\n puzzle,\n onSolve,\n onFail,\n theme,\n children,\n}) => {\n // Merge partial theme with defaults\n const mergedTheme = React.useMemo(() => mergePuzzleTheme(theme), [theme]);\n\n return (\n <ChessGame.Root\n fen={puzzle.fen}\n orientation={getOrientation(puzzle)}\n theme={mergedTheme}\n >\n <PuzzleThemeProvider theme={mergedTheme}>\n <PuzzleRootInner puzzle={puzzle} onSolve={onSolve} onFail={onFail}>\n {children}\n </PuzzleRootInner>\n </PuzzleThemeProvider>\n </ChessGame.Root>\n );\n};\n","import { type Color, Chess, Move } from \"chess.js\";\nimport React, { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport _ from \"lodash\";\nimport type { ChessPuzzleTheme } from \"../theme/types\";\nimport { defaultPuzzleTheme } from \"../theme/defaults\";\n\nexport type Status = \"not-started\" | \"in-progress\" | \"solved\" | \"failed\";\n\nexport type Hint = \"none\" | \"piece\" | \"move\";\n\nexport type Puzzle = {\n fen: string;\n moves: string[];\n // if the first move of the puzzle has to be made by the cpu, as in chess.com puzzles\n makeFirstMove?: boolean;\n};\n\nexport const getOrientation = (puzzle: Puzzle): Color => {\n const fen = puzzle.fen;\n const game = new Chess(fen);\n if (puzzle.makeFirstMove) {\n game.move(puzzle.moves[0]);\n }\n return game.turn();\n};\n\ninterface ClickableElement extends ReactElement {\n props: {\n onClick?: () => void;\n };\n}\n\nexport const isClickableElement = (\n element: ReactNode,\n): element is ClickableElement => React.isValidElement(element);\n\n/**\n * Generates custom square styles for puzzle states based on theme.\n *\n * @param status - Current puzzle status\n * @param hint - Current hint level\n * @param isPlayerTurn - Whether it's the player's turn\n * @param game - Chess.js game instance\n * @param nextMove - The next expected move (for hints)\n * @param theme - Theme configuration (defaults to defaultPuzzleTheme)\n * @returns Record of square names to CSS properties\n */\nexport const getCustomSquareStyles = (\n status: Status,\n hint: Hint,\n isPlayerTurn: boolean,\n game: Chess,\n nextMove?: Move | null,\n theme: ChessPuzzleTheme = defaultPuzzleTheme,\n) => {\n const customSquareStyles: Record<string, CSSProperties> = {};\n\n const lastMove = _.last(game.history({ verbose: true }));\n\n if (status === \"failed\" && lastMove) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: theme.puzzle.failure,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: theme.puzzle.failure,\n };\n }\n\n if (\n lastMove &&\n (status === \"solved\" || (status !== \"failed\" && !isPlayerTurn))\n ) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: theme.puzzle.success,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: theme.puzzle.success,\n };\n }\n\n if (hint === \"piece\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: theme.puzzle.hint,\n };\n }\n }\n\n if (hint === \"move\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: theme.puzzle.hint,\n };\n customSquareStyles[nextMove.to] = {\n backgroundColor: theme.puzzle.hint,\n };\n }\n }\n\n return customSquareStyles;\n};\n\nexport const stringToMove = (game: Chess, move: string | null | undefined) => {\n const copy = new Chess(game.fen());\n if (move === null || move === undefined) {\n return null;\n }\n try {\n return copy.move(move);\n } catch (e) {\n return null;\n }\n};\n","import { defaultGameTheme } from \"@react-chess-tools/react-chess-game\";\nimport type { ChessPuzzleTheme } from \"./types\";\n\n/**\n * Default theme for ChessPuzzle component.\n * Extends the default game theme with puzzle-specific colors.\n * These values match the original hardcoded colors for backward compatibility.\n */\nexport const defaultPuzzleTheme: ChessPuzzleTheme = {\n ...defaultGameTheme,\n puzzle: {\n success: \"rgba(172, 206, 89, 0.5)\",\n failure: \"rgba(201, 52, 48, 0.5)\",\n hint: \"rgba(27, 172, 166, 0.5)\",\n },\n};\n","import { useEffect, useReducer, useCallback, useMemo } from \"react\";\nimport { initializePuzzle, reducer } from \"./reducer\";\nimport { getOrientation, type Puzzle, type Hint, type Status } from \"../utils\";\nimport { useChessGameContext } from \"@react-chess-tools/react-chess-game\";\n\nexport type ChessPuzzleContextType = {\n status: Status;\n changePuzzle: (puzzle: Puzzle) => void;\n resetPuzzle: () => void;\n puzzle: Puzzle;\n hint: Hint;\n nextMove?: string | null;\n isPlayerTurn: boolean;\n onHint: () => void;\n puzzleState: Status;\n movesPlayed: number;\n totalMoves: number;\n};\n\nexport const useChessPuzzle = (\n puzzle: Puzzle,\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void,\n onFail?: (puzzleContext: ChessPuzzleContextType) => void,\n): ChessPuzzleContextType => {\n const gameContext = useChessGameContext();\n\n const [state, dispatch] = useReducer(reducer, { puzzle }, initializePuzzle);\n\n const {\n game,\n methods: { makeMove, setPosition },\n } = gameContext;\n\n const changePuzzle = useCallback(\n (puzzle: Puzzle) => {\n setPosition(puzzle.fen, getOrientation(puzzle));\n dispatch({ type: \"INITIALIZE\", payload: { puzzle } });\n },\n [setPosition],\n );\n\n useEffect(() => {\n changePuzzle(puzzle);\n }, [JSON.stringify(puzzle), changePuzzle]);\n\n useEffect(() => {\n if (gameContext && game.fen() === puzzle.fen && state.needCpuMove) {\n setTimeout(\n () =>\n dispatch({\n type: \"CPU_MOVE\",\n }),\n 0,\n );\n }\n }, [gameContext, state.needCpuMove]);\n\n useEffect(() => {\n if (state.cpuMove) {\n makeMove(state.cpuMove);\n }\n }, [state.cpuMove]);\n\n if (!gameContext) {\n throw new Error(\"useChessPuzzle must be used within a ChessGameContext\");\n }\n\n const onHint = useCallback(() => {\n dispatch({ type: \"TOGGLE_HINT\" });\n }, []);\n\n const resetPuzzle = useCallback(() => {\n changePuzzle(puzzle);\n }, [changePuzzle, puzzle]);\n\n const puzzleContext: ChessPuzzleContextType = useMemo(\n () => ({\n status: state.status,\n changePuzzle,\n resetPuzzle,\n puzzle,\n hint: state.hint,\n onHint,\n nextMove: state.nextMove,\n isPlayerTurn: state.isPlayerTurn,\n puzzleState: state.status,\n movesPlayed: state.currentMoveIndex,\n totalMoves: puzzle.moves.length,\n }),\n [\n state.status,\n changePuzzle,\n resetPuzzle,\n puzzle,\n state.hint,\n onHint,\n state.nextMove,\n state.isPlayerTurn,\n state.currentMoveIndex,\n ],\n );\n\n useEffect(() => {\n if (game?.history()?.length <= 0 + (puzzle.makeFirstMove ? 1 : 0)) {\n return;\n }\n if (game.history().length % 2 === (puzzle.makeFirstMove ? 0 : 1)) {\n dispatch({\n type: \"PLAYER_MOVE\",\n payload: {\n move: gameContext?.game?.history({ verbose: true })?.pop() ?? null,\n puzzleContext,\n game: game,\n },\n });\n\n dispatch({\n type: \"CPU_MOVE\",\n });\n }\n }, [game?.history()?.length]);\n\n useEffect(() => {\n if (state.status === \"solved\" && !state.onSolveInvoked && onSolve) {\n onSolve(puzzleContext);\n dispatch({ type: \"MARK_SOLVE_INVOKED\" });\n }\n }, [state.status, state.onSolveInvoked]);\n\n useEffect(() => {\n if (state.status === \"failed\" && !state.onFailInvoked && onFail) {\n onFail(puzzleContext);\n dispatch({ type: \"MARK_FAIL_INVOKED\" });\n }\n }, [state.status, state.onFailInvoked]);\n\n return puzzleContext;\n};\n","import { Chess, Move } from \"chess.js\";\nimport { type Puzzle, type Hint, type Status } from \"../utils\";\nimport { ChessPuzzleContextType } from \"./useChessPuzzle\";\n\nexport type State = {\n puzzle: Puzzle;\n currentMoveIndex: number;\n status: Status;\n cpuMove?: string | null;\n nextMove?: string | null;\n hint: Hint;\n needCpuMove: boolean;\n isPlayerTurn: boolean;\n onSolveInvoked: boolean;\n onFailInvoked: boolean;\n};\n\nexport type Action =\n | {\n type: \"INITIALIZE\";\n payload: {\n puzzle: Puzzle;\n };\n }\n | {\n type: \"RESET\";\n }\n | { type: \"TOGGLE_HINT\" }\n | {\n type: \"CPU_MOVE\";\n }\n | {\n type: \"PLAYER_MOVE\";\n payload: {\n move?: Move | null;\n puzzleContext: ChessPuzzleContextType;\n game: Chess;\n };\n }\n | { type: \"MARK_SOLVE_INVOKED\" }\n | { type: \"MARK_FAIL_INVOKED\" };\n\nexport const initializePuzzle = ({ puzzle }: { puzzle: Puzzle }): State => {\n return {\n puzzle,\n currentMoveIndex: 0,\n status: \"not-started\",\n nextMove: puzzle.moves[0],\n hint: \"none\",\n cpuMove: null,\n needCpuMove: !!puzzle.makeFirstMove,\n isPlayerTurn: !puzzle.makeFirstMove,\n onSolveInvoked: false,\n onFailInvoked: false,\n };\n};\n\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case \"INITIALIZE\":\n return {\n ...state,\n ...initializePuzzle(action.payload),\n };\n case \"RESET\":\n return {\n ...state,\n ...initializePuzzle({\n puzzle: state.puzzle,\n }),\n };\n case \"TOGGLE_HINT\":\n if (state.hint === \"none\") {\n return { ...state, hint: \"piece\" };\n }\n return { ...state, hint: \"move\" };\n case \"CPU_MOVE\":\n if (state.isPlayerTurn) {\n return state;\n }\n if ([\"solved\", \"failed\"].includes(state.status)) {\n return state;\n }\n\n return {\n ...state,\n currentMoveIndex: state.currentMoveIndex + 1,\n cpuMove: state.puzzle.moves[state.currentMoveIndex],\n nextMove:\n state.currentMoveIndex < state.puzzle.moves.length - 1\n ? state.puzzle.moves[state.currentMoveIndex + 1]\n : null,\n needCpuMove: false,\n isPlayerTurn: true,\n status: \"in-progress\",\n };\n\n case \"PLAYER_MOVE\": {\n const { move } = action.payload;\n\n const isMoveRight = [move?.san, move?.lan].includes(\n state?.nextMove || \"\",\n );\n const isPuzzleSolved =\n state.currentMoveIndex === state.puzzle.moves.length - 1;\n\n if (!isMoveRight) {\n return {\n ...state,\n status: \"failed\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n onFailInvoked: false,\n };\n }\n\n if (isPuzzleSolved) {\n return {\n ...state,\n status: \"solved\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n onSolveInvoked: false,\n };\n }\n\n return {\n ...state,\n hint: \"none\",\n currentMoveIndex: state.currentMoveIndex + 1,\n nextMove: state.puzzle.moves[state.currentMoveIndex + 1],\n status: \"in-progress\",\n needCpuMove: true,\n isPlayerTurn: false,\n };\n }\n\n case \"MARK_SOLVE_INVOKED\":\n return {\n ...state,\n onSolveInvoked: true,\n };\n\n case \"MARK_FAIL_INVOKED\":\n return {\n ...state,\n onFailInvoked: true,\n };\n\n default:\n return state;\n }\n};\n","import React from \"react\";\nimport { useChessPuzzle } from \"./useChessPuzzle\";\n\nexport const ChessPuzzleContext = React.createContext<ReturnType<\n typeof useChessPuzzle\n> | null>(null);\n\nexport const useChessPuzzleContext = () => {\n const context = React.useContext(ChessPuzzleContext);\n if (!context) {\n throw new Error(\n `useChessPuzzleContext must be used within a ChessPuzzle component. Make sure your component is wrapped with <ChessPuzzle.Root> or ensure the ChessPuzzle component is properly rendered in the component tree.`,\n );\n }\n return context;\n};\n","import React, { createContext, useContext } from \"react\";\nimport type { ChessPuzzleTheme } from \"./types\";\nimport { defaultPuzzleTheme } from \"./defaults\";\n\n/**\n * Context for ChessPuzzle theme\n */\nexport const ChessPuzzleThemeContext =\n createContext<ChessPuzzleTheme>(defaultPuzzleTheme);\n\n/**\n * Hook to access the current ChessPuzzle theme.\n * Returns the default puzzle theme if no ThemeProvider is present.\n */\nexport const useChessPuzzleTheme = (): ChessPuzzleTheme => {\n return useContext(ChessPuzzleThemeContext);\n};\n\nexport interface PuzzleThemeProviderProps {\n theme: ChessPuzzleTheme;\n children: React.ReactNode;\n}\n\n/**\n * Internal provider component used by Puzzle Root when a theme prop is provided.\n */\nexport const PuzzleThemeProvider: React.FC<PuzzleThemeProviderProps> = ({\n theme,\n children,\n}) => {\n return (\n <ChessPuzzleThemeContext.Provider value={theme}>\n {children}\n </ChessPuzzleThemeContext.Provider>\n );\n};\n","import { merge } from \"lodash\";\nimport type { ChessPuzzleTheme, PartialChessPuzzleTheme } from \"./types\";\nimport { defaultPuzzleTheme } from \"./defaults\";\n\n/**\n * Deep merges a partial puzzle theme with the default puzzle theme.\n * Allows users to override only specific theme properties while keeping defaults for the rest.\n *\n * @param partialTheme - Partial theme with only the properties to override\n * @returns Complete puzzle theme with overridden properties merged with defaults\n *\n * @example\n * ```typescript\n * const customTheme = mergePuzzleTheme({\n * puzzle: { hint: \"rgba(0, 255, 255, 0.5)\" }\n * });\n * // Returns full puzzle theme with only hint color changed\n * ```\n */\nexport const mergePuzzleTheme = (\n partialTheme?: PartialChessPuzzleTheme,\n): ChessPuzzleTheme => {\n if (!partialTheme) {\n return { ...defaultPuzzleTheme };\n }\n\n return merge({}, defaultPuzzleTheme, partialTheme);\n};\n","import React from \"react\";\nimport {\n ChessGame,\n deepMergeChessboardOptions,\n useChessGameContext,\n} from \"@react-chess-tools/react-chess-game\";\nimport { getCustomSquareStyles, stringToMove } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\nimport { useChessPuzzleTheme } from \"../../../theme/context\";\n\nexport interface PuzzleBoardProps extends React.ComponentProps<\n typeof ChessGame.Board\n> {}\n\nexport const PuzzleBoard: React.FC<PuzzleBoardProps> = ({\n options = {},\n ...rest\n}) => {\n const puzzleContext = useChessPuzzleContext();\n const gameContext = useChessGameContext();\n const theme = useChessPuzzleTheme();\n\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n if (!gameContext) {\n throw new Error(\"ChessGameContext not found\");\n }\n\n const { game } = gameContext;\n const { status, hint, isPlayerTurn, nextMove } = puzzleContext;\n\n const mergedOptions = deepMergeChessboardOptions(options, {\n squareStyles: getCustomSquareStyles(\n status,\n hint,\n isPlayerTurn,\n game,\n stringToMove(game, nextMove),\n theme,\n ),\n });\n\n return <ChessGame.Board {...rest} options={mergedOptions} />;\n};\n","import React from \"react\";\nimport { isClickableElement, type Puzzle, type Status } from \"../../../utils\";\nimport { useChessPuzzleContext, type ChessPuzzleContextType } from \"../../..\";\n\nexport interface ResetProps {\n asChild?: boolean;\n puzzle?: Puzzle;\n onReset?: (puzzleContext: ChessPuzzleContextType) => void;\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"failed\", \"solved\"];\n\nexport const Reset: React.FC<React.PropsWithChildren<ResetProps>> = ({\n children,\n asChild,\n puzzle,\n onReset,\n showOn = defaultShowOn,\n}) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { changePuzzle, status } = puzzleContext;\n const handleClick = () => {\n changePuzzle(puzzle || puzzleContext.puzzle);\n onReset?.(puzzleContext);\n };\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n if (asChild) {\n const child = React.Children.only(children);\n if (isClickableElement(child)) {\n return React.cloneElement(child, {\n onClick: handleClick,\n });\n } else {\n throw new Error(\"Change child must be a clickable element\");\n }\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n","import React from \"react\";\nimport { Status, isClickableElement } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface HintProps {\n asChild?: boolean;\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"not-started\", \"in-progress\"];\n\nexport const Hint: React.FC<React.PropsWithChildren<HintProps>> = ({\n children,\n asChild,\n showOn = defaultShowOn,\n}) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { onHint, status } = puzzleContext;\n const handleClick = () => {\n onHint();\n };\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n if (asChild) {\n const child = React.Children.only(children);\n if (isClickableElement(child)) {\n return React.cloneElement(child, {\n onClick: handleClick,\n });\n } else {\n throw new Error(\"Change child must be a clickable element\");\n }\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n","import { Root } from \"./parts/Root\";\nimport { PuzzleBoard } from \"./parts/PuzzleBoard\";\nimport { Reset } from \"./parts/Reset\";\nimport { Hint } from \"./parts/Hint\";\n\nexport const ChessPuzzle = {\n Root,\n Board: PuzzleBoard,\n Reset,\n Hint,\n};\n"],"mappings":"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","names":["React","puzzle","_a","React","React","React","React","React","ChessGame","useChessGameContext","useChessGameContext","React","ChessGame","React","React","React","defaultShowOn","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ChessPuzzle/parts/Root.tsx","../src/utils/index.ts","../src/theme/defaults.ts","../src/hooks/useChessPuzzle.ts","../src/hooks/reducer.ts","../src/hooks/useChessPuzzleContext.ts","../src/theme/context.tsx","../src/theme/utils.ts","../src/components/ChessPuzzle/parts/PuzzleBoard.tsx","../src/components/ChessPuzzle/parts/Reset.tsx","../src/components/ChessPuzzle/parts/Hint.tsx","../src/components/ChessPuzzle/index.ts"],"sourcesContent":["import React from \"react\";\nimport { Puzzle, getOrientation } from \"../../../utils\";\nimport {\n ChessPuzzleContextType,\n useChessPuzzle,\n} from \"../../../hooks/useChessPuzzle\";\nimport { ChessGame } from \"@react-chess-tools/react-chess-game\";\nimport { ChessPuzzleContext } from \"../../../hooks/useChessPuzzleContext\";\nimport { PuzzleThemeProvider } from \"../../../theme/context\";\nimport { mergePuzzleTheme } from \"../../../theme/utils\";\nimport type { PartialChessPuzzleTheme } from \"../../../theme/types\";\n\nexport interface RootProps {\n puzzle: Puzzle;\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void;\n onFail?: (puzzleContext: ChessPuzzleContextType) => void;\n /** Optional theme configuration. Supports partial themes - only override the colors you need. */\n theme?: PartialChessPuzzleTheme;\n /** When true, any checkmate move solves the puzzle (not just the canonical solution). Defaults to true. */\n solveOnCheckmate?: boolean;\n}\n\ninterface PuzzleRootInnerProps {\n puzzle: Puzzle;\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void;\n onFail?: (puzzleContext: ChessPuzzleContextType) => void;\n solveOnCheckmate: boolean;\n children: React.ReactNode;\n}\n\nconst PuzzleRootInner: React.FC<PuzzleRootInnerProps> = ({\n puzzle,\n onSolve,\n onFail,\n solveOnCheckmate,\n children,\n}) => {\n const context = useChessPuzzle(puzzle, onSolve, onFail, solveOnCheckmate);\n\n return (\n <ChessPuzzleContext.Provider value={context}>\n {children}\n </ChessPuzzleContext.Provider>\n );\n};\n\nexport const Root: React.FC<React.PropsWithChildren<RootProps>> = ({\n puzzle,\n onSolve,\n onFail,\n theme,\n solveOnCheckmate = true,\n children,\n}) => {\n // Merge partial theme with defaults\n const mergedTheme = React.useMemo(() => mergePuzzleTheme(theme), [theme]);\n\n return (\n <ChessGame.Root\n fen={puzzle.fen}\n orientation={getOrientation(puzzle)}\n theme={mergedTheme}\n >\n <PuzzleThemeProvider theme={mergedTheme}>\n <PuzzleRootInner\n puzzle={puzzle}\n onSolve={onSolve}\n onFail={onFail}\n solveOnCheckmate={solveOnCheckmate}\n >\n {children}\n </PuzzleRootInner>\n </PuzzleThemeProvider>\n </ChessGame.Root>\n );\n};\n\nRoot.displayName = \"ChessPuzzle.Root\";\n","import { type Color, Chess, Move } from \"chess.js\";\nimport { type CSSProperties } from \"react\";\nimport _ from \"lodash\";\nimport type { ChessPuzzleTheme } from \"../theme/types\";\nimport { defaultPuzzleTheme } from \"../theme/defaults\";\n\nexport type Status = \"not-started\" | \"in-progress\" | \"solved\" | \"failed\";\n\nexport type Hint = \"none\" | \"piece\" | \"move\";\n\nexport type Puzzle = {\n fen: string;\n moves: string[];\n // if the first move of the puzzle has to be made by the cpu, as in chess.com puzzles\n makeFirstMove?: boolean;\n};\n\nexport const getOrientation = (puzzle: Puzzle): Color => {\n const fen = puzzle.fen;\n const game = new Chess(fen);\n if (puzzle.makeFirstMove) {\n game.move(puzzle.moves[0]);\n }\n return game.turn();\n};\n\n/**\n * Generates custom square styles for puzzle states based on theme.\n *\n * @param status - Current puzzle status\n * @param hint - Current hint level\n * @param isPlayerTurn - Whether it's the player's turn\n * @param game - Chess.js game instance\n * @param nextMove - The next expected move (for hints)\n * @param theme - Theme configuration (defaults to defaultPuzzleTheme)\n * @returns Record of square names to CSS properties\n */\nexport const getCustomSquareStyles = (\n status: Status,\n hint: Hint,\n isPlayerTurn: boolean,\n game: Chess,\n nextMove?: Move | null,\n theme: ChessPuzzleTheme = defaultPuzzleTheme,\n) => {\n const customSquareStyles: Record<string, CSSProperties> = {};\n\n const lastMove = _.last(game.history({ verbose: true }));\n\n if (status === \"failed\" && lastMove) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: theme.puzzle.failure,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: theme.puzzle.failure,\n };\n }\n\n if (\n lastMove &&\n (status === \"solved\" || (status !== \"failed\" && !isPlayerTurn))\n ) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: theme.puzzle.success,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: theme.puzzle.success,\n };\n }\n\n if (hint === \"piece\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: theme.puzzle.hint,\n };\n }\n }\n\n if (hint === \"move\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: theme.puzzle.hint,\n };\n customSquareStyles[nextMove.to] = {\n backgroundColor: theme.puzzle.hint,\n };\n }\n }\n\n return customSquareStyles;\n};\n\nexport const stringToMove = (game: Chess, move: string | null | undefined) => {\n const copy = new Chess(game.fen());\n if (move === null || move === undefined) {\n return null;\n }\n try {\n return copy.move(move);\n } catch (e) {\n return null;\n }\n};\n","import { defaultGameTheme } from \"@react-chess-tools/react-chess-game\";\nimport type { ChessPuzzleTheme } from \"./types\";\n\n/**\n * Default theme for ChessPuzzle component.\n * Extends the default game theme with puzzle-specific colors.\n * These values match the original hardcoded colors for backward compatibility.\n */\nexport const defaultPuzzleTheme: ChessPuzzleTheme = {\n ...defaultGameTheme,\n puzzle: {\n success: \"rgba(172, 206, 89, 0.5)\",\n failure: \"rgba(201, 52, 48, 0.5)\",\n hint: \"rgba(27, 172, 166, 0.5)\",\n },\n};\n","import { useEffect, useReducer, useCallback, useMemo } from \"react\";\nimport { initializePuzzle, reducer } from \"./reducer\";\nimport { getOrientation, type Puzzle, type Hint, type Status } from \"../utils\";\nimport { useChessGameContext } from \"@react-chess-tools/react-chess-game\";\n\nexport type ChessPuzzleContextType = {\n status: Status;\n changePuzzle: (puzzle: Puzzle) => void;\n resetPuzzle: () => void;\n puzzle: Puzzle;\n hint: Hint;\n nextMove?: string | null;\n isPlayerTurn: boolean;\n onHint: () => void;\n puzzleState: Status;\n movesPlayed: number;\n totalMoves: number;\n};\n\nexport const useChessPuzzle = (\n puzzle: Puzzle,\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void,\n onFail?: (puzzleContext: ChessPuzzleContextType) => void,\n solveOnCheckmate: boolean = true,\n): ChessPuzzleContextType => {\n const gameContext = useChessGameContext();\n\n const [state, dispatch] = useReducer(reducer, { puzzle }, initializePuzzle);\n\n const {\n game,\n methods: { makeMove, setPosition },\n } = gameContext;\n\n const changePuzzle = useCallback(\n (puzzle: Puzzle) => {\n setPosition(puzzle.fen, getOrientation(puzzle));\n dispatch({ type: \"INITIALIZE\", payload: { puzzle } });\n },\n [setPosition],\n );\n\n useEffect(() => {\n changePuzzle(puzzle);\n }, [JSON.stringify(puzzle), changePuzzle]);\n\n useEffect(() => {\n if (gameContext && game.fen() === puzzle.fen && state.needCpuMove) {\n setTimeout(\n () =>\n dispatch({\n type: \"CPU_MOVE\",\n }),\n 0,\n );\n }\n }, [gameContext, state.needCpuMove]);\n\n useEffect(() => {\n if (state.cpuMove) {\n makeMove(state.cpuMove);\n }\n }, [state.cpuMove]);\n\n if (!gameContext) {\n throw new Error(\"useChessPuzzle must be used within a ChessGameContext\");\n }\n\n const onHint = useCallback(() => {\n dispatch({ type: \"TOGGLE_HINT\" });\n }, []);\n\n const resetPuzzle = useCallback(() => {\n changePuzzle(puzzle);\n }, [changePuzzle, puzzle]);\n\n const puzzleContext: ChessPuzzleContextType = useMemo(\n () => ({\n status: state.status,\n changePuzzle,\n resetPuzzle,\n puzzle,\n hint: state.hint,\n onHint,\n nextMove: state.nextMove,\n isPlayerTurn: state.isPlayerTurn,\n puzzleState: state.status,\n movesPlayed: state.currentMoveIndex,\n totalMoves: puzzle.moves.length,\n }),\n [\n state.status,\n changePuzzle,\n resetPuzzle,\n puzzle,\n state.hint,\n onHint,\n state.nextMove,\n state.isPlayerTurn,\n state.currentMoveIndex,\n ],\n );\n\n useEffect(() => {\n if (game?.history()?.length <= 0 + (puzzle.makeFirstMove ? 1 : 0)) {\n return;\n }\n if (game.history().length % 2 === (puzzle.makeFirstMove ? 0 : 1)) {\n dispatch({\n type: \"PLAYER_MOVE\",\n payload: {\n move: gameContext?.game?.history({ verbose: true })?.pop() ?? null,\n puzzleContext,\n game: game,\n solveOnCheckmate,\n },\n });\n\n dispatch({\n type: \"CPU_MOVE\",\n });\n }\n }, [game?.history()?.length]);\n\n useEffect(() => {\n if (state.status === \"solved\" && !state.onSolveInvoked && onSolve) {\n onSolve(puzzleContext);\n dispatch({ type: \"MARK_SOLVE_INVOKED\" });\n }\n }, [state.status, state.onSolveInvoked]);\n\n useEffect(() => {\n if (state.status === \"failed\" && !state.onFailInvoked && onFail) {\n onFail(puzzleContext);\n dispatch({ type: \"MARK_FAIL_INVOKED\" });\n }\n }, [state.status, state.onFailInvoked]);\n\n return puzzleContext;\n};\n","import { Chess, Move } from \"chess.js\";\nimport { type Puzzle, type Hint, type Status } from \"../utils\";\nimport { ChessPuzzleContextType } from \"./useChessPuzzle\";\n\nexport type State = {\n puzzle: Puzzle;\n currentMoveIndex: number;\n status: Status;\n cpuMove?: string | null;\n nextMove?: string | null;\n hint: Hint;\n needCpuMove: boolean;\n isPlayerTurn: boolean;\n onSolveInvoked: boolean;\n onFailInvoked: boolean;\n};\n\nexport type Action =\n | {\n type: \"INITIALIZE\";\n payload: {\n puzzle: Puzzle;\n };\n }\n | {\n type: \"RESET\";\n }\n | { type: \"TOGGLE_HINT\" }\n | {\n type: \"CPU_MOVE\";\n }\n | {\n type: \"PLAYER_MOVE\";\n payload: {\n move?: Move | null;\n puzzleContext: ChessPuzzleContextType;\n game: Chess;\n solveOnCheckmate?: boolean;\n };\n }\n | { type: \"MARK_SOLVE_INVOKED\" }\n | { type: \"MARK_FAIL_INVOKED\" };\n\nexport const initializePuzzle = ({ puzzle }: { puzzle: Puzzle }): State => {\n return {\n puzzle,\n currentMoveIndex: 0,\n status: \"not-started\",\n nextMove: puzzle.moves[0],\n hint: \"none\",\n cpuMove: null,\n needCpuMove: !!puzzle.makeFirstMove,\n isPlayerTurn: !puzzle.makeFirstMove,\n onSolveInvoked: false,\n onFailInvoked: false,\n };\n};\n\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case \"INITIALIZE\":\n return {\n ...state,\n ...initializePuzzle(action.payload),\n };\n case \"RESET\":\n return {\n ...state,\n ...initializePuzzle({\n puzzle: state.puzzle,\n }),\n };\n case \"TOGGLE_HINT\":\n if (state.hint === \"none\") {\n return { ...state, hint: \"piece\" };\n }\n return { ...state, hint: \"move\" };\n case \"CPU_MOVE\":\n if (state.isPlayerTurn) {\n return state;\n }\n if ([\"solved\", \"failed\"].includes(state.status)) {\n return state;\n }\n\n return {\n ...state,\n currentMoveIndex: state.currentMoveIndex + 1,\n cpuMove: state.puzzle.moves[state.currentMoveIndex],\n nextMove:\n state.currentMoveIndex < state.puzzle.moves.length - 1\n ? state.puzzle.moves[state.currentMoveIndex + 1]\n : null,\n needCpuMove: false,\n isPlayerTurn: true,\n status: \"in-progress\",\n };\n\n case \"PLAYER_MOVE\": {\n const { move, game, solveOnCheckmate } = action.payload;\n\n if (move && solveOnCheckmate !== false && game.isCheckmate()) {\n return {\n ...state,\n status: \"solved\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n onSolveInvoked: false,\n };\n }\n\n const isMoveRight = [move?.san, move?.lan].includes(\n state?.nextMove || \"\",\n );\n const isPuzzleSolved =\n state.currentMoveIndex === state.puzzle.moves.length - 1;\n\n if (!isMoveRight) {\n return {\n ...state,\n status: \"failed\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n onFailInvoked: false,\n };\n }\n\n if (isPuzzleSolved) {\n return {\n ...state,\n status: \"solved\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n onSolveInvoked: false,\n };\n }\n\n return {\n ...state,\n hint: \"none\",\n currentMoveIndex: state.currentMoveIndex + 1,\n nextMove: state.puzzle.moves[state.currentMoveIndex + 1],\n status: \"in-progress\",\n needCpuMove: true,\n isPlayerTurn: false,\n };\n }\n\n case \"MARK_SOLVE_INVOKED\":\n return {\n ...state,\n onSolveInvoked: true,\n };\n\n case \"MARK_FAIL_INVOKED\":\n return {\n ...state,\n onFailInvoked: true,\n };\n\n default:\n return state;\n }\n};\n","import React from \"react\";\nimport { useChessPuzzle } from \"./useChessPuzzle\";\n\nexport const ChessPuzzleContext = React.createContext<ReturnType<\n typeof useChessPuzzle\n> | null>(null);\n\nexport const useChessPuzzleContext = () => {\n const context = React.useContext(ChessPuzzleContext);\n if (!context) {\n throw new Error(\n `useChessPuzzleContext must be used within a ChessPuzzle component. Make sure your component is wrapped with <ChessPuzzle.Root> or ensure the ChessPuzzle component is properly rendered in the component tree.`,\n );\n }\n return context;\n};\n","import React, { createContext, useContext } from \"react\";\nimport type { ChessPuzzleTheme } from \"./types\";\nimport { defaultPuzzleTheme } from \"./defaults\";\n\n/**\n * Context for ChessPuzzle theme\n */\nexport const ChessPuzzleThemeContext =\n createContext<ChessPuzzleTheme>(defaultPuzzleTheme);\n\n/**\n * Hook to access the current ChessPuzzle theme.\n * Returns the default puzzle theme if no ThemeProvider is present.\n */\nexport const useChessPuzzleTheme = (): ChessPuzzleTheme => {\n return useContext(ChessPuzzleThemeContext);\n};\n\nexport interface PuzzleThemeProviderProps {\n theme: ChessPuzzleTheme;\n children: React.ReactNode;\n}\n\n/**\n * Internal provider component used by Puzzle Root when a theme prop is provided.\n */\nexport const PuzzleThemeProvider: React.FC<PuzzleThemeProviderProps> = ({\n theme,\n children,\n}) => {\n return (\n <ChessPuzzleThemeContext.Provider value={theme}>\n {children}\n </ChessPuzzleThemeContext.Provider>\n );\n};\n","import { merge } from \"lodash\";\nimport type { ChessPuzzleTheme, PartialChessPuzzleTheme } from \"./types\";\nimport { defaultPuzzleTheme } from \"./defaults\";\n\n/**\n * Deep merges a partial puzzle theme with the default puzzle theme.\n * Allows users to override only specific theme properties while keeping defaults for the rest.\n *\n * @param partialTheme - Partial theme with only the properties to override\n * @returns Complete puzzle theme with overridden properties merged with defaults\n *\n * @example\n * ```typescript\n * const customTheme = mergePuzzleTheme({\n * puzzle: { hint: \"rgba(0, 255, 255, 0.5)\" }\n * });\n * // Returns full puzzle theme with only hint color changed\n * ```\n */\nexport const mergePuzzleTheme = (\n partialTheme?: PartialChessPuzzleTheme,\n): ChessPuzzleTheme => {\n if (!partialTheme) {\n return { ...defaultPuzzleTheme };\n }\n\n return merge({}, defaultPuzzleTheme, partialTheme);\n};\n","import React from \"react\";\nimport {\n ChessGame,\n deepMergeChessboardOptions,\n useChessGameContext,\n} from \"@react-chess-tools/react-chess-game\";\nimport { getCustomSquareStyles, stringToMove } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\nimport { useChessPuzzleTheme } from \"../../../theme/context\";\n\nexport interface PuzzleBoardProps extends React.ComponentProps<\n typeof ChessGame.Board\n> {}\n\nexport const PuzzleBoard = React.forwardRef<HTMLDivElement, PuzzleBoardProps>(\n ({ options, ...rest }, ref) => {\n const puzzleContext = useChessPuzzleContext();\n const gameContext = useChessGameContext();\n const theme = useChessPuzzleTheme();\n\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n if (!gameContext) {\n throw new Error(\"ChessGameContext not found\");\n }\n\n const { game } = gameContext;\n const { status, hint, isPlayerTurn, nextMove } = puzzleContext;\n\n const mergedOptions = deepMergeChessboardOptions(options || {}, {\n squareStyles: getCustomSquareStyles(\n status,\n hint,\n isPlayerTurn,\n game,\n stringToMove(game, nextMove),\n theme,\n ),\n });\n\n return <ChessGame.Board ref={ref} {...rest} options={mergedOptions} />;\n },\n);\n\nPuzzleBoard.displayName = \"ChessPuzzle.PuzzleBoard\";\n","import React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { type Puzzle, type Status } from \"../../../utils\";\nimport { useChessPuzzleContext, type ChessPuzzleContextType } from \"../../..\";\n\nexport interface ResetProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"onReset\"\n> {\n asChild?: boolean;\n puzzle?: Puzzle;\n onReset?: (puzzleContext: ChessPuzzleContextType) => void;\n /**\n * The puzzle statuses in which the reset button should be visible.\n * @default [\"failed\", \"solved\"]\n */\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"failed\", \"solved\"];\n\nexport const Reset = React.forwardRef<\n HTMLElement,\n React.PropsWithChildren<ResetProps>\n>(\n (\n {\n children,\n asChild,\n puzzle,\n onReset,\n showOn = defaultShowOn,\n className,\n ...rest\n },\n ref,\n ) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { changePuzzle, puzzle: contextPuzzle, status } = puzzleContext;\n\n const handleClick = React.useCallback(() => {\n changePuzzle(puzzle || contextPuzzle);\n onReset?.(puzzleContext);\n }, [changePuzzle, puzzle, contextPuzzle, puzzleContext, onReset]);\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n return asChild ? (\n <Slot ref={ref} onClick={handleClick} className={className} {...rest}>\n {children}\n </Slot>\n ) : (\n <button\n ref={ref as React.RefObject<HTMLButtonElement>}\n type=\"button\"\n className={className}\n onClick={handleClick}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n\nReset.displayName = \"ChessPuzzle.Reset\";\n","import React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { Status } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface HintProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"onClick\"\n> {\n asChild?: boolean;\n /**\n * The puzzle statuses in which the hint button should be visible.\n * @default [\"not-started\", \"in-progress\"]\n */\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"not-started\", \"in-progress\"];\n\nexport const Hint = React.forwardRef<\n HTMLElement,\n React.PropsWithChildren<HintProps>\n>(({ children, asChild, showOn = defaultShowOn, className, ...rest }, ref) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { onHint, status } = puzzleContext;\n\n const handleClick = React.useCallback(() => {\n onHint();\n }, [onHint]);\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n return asChild ? (\n <Slot ref={ref} onClick={handleClick} className={className} {...rest}>\n {children}\n </Slot>\n ) : (\n <button\n ref={ref as React.RefObject<HTMLButtonElement>}\n type=\"button\"\n className={className}\n onClick={handleClick}\n {...rest}\n >\n {children}\n </button>\n );\n});\n\nHint.displayName = \"ChessPuzzle.Hint\";\n","import { Root } from \"./parts/Root\";\nimport { PuzzleBoard } from \"./parts/PuzzleBoard\";\nimport { Reset } from \"./parts/Reset\";\nimport { Hint } from \"./parts/Hint\";\n\nexport const ChessPuzzle = {\n Root,\n Board: PuzzleBoard,\n Reset,\n Hint,\n};\n"],"mappings":";AAAA,OAAOA,YAAW;;;ACAlB,SAAqB,aAAmB;AAExC,OAAO,OAAO;;;ACFd,SAAS,wBAAwB;AAQ1B,IAAM,qBAAuC;AAAA,EAClD,GAAG;AAAA,EACH,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AACF;;;ADEO,IAAM,iBAAiB,CAAC,WAA0B;AACvD,QAAM,MAAM,OAAO;AACnB,QAAM,OAAO,IAAI,MAAM,GAAG;AAC1B,MAAI,OAAO,eAAe;AACxB,SAAK,KAAK,OAAO,MAAM,CAAC,CAAC;AAAA,EAC3B;AACA,SAAO,KAAK,KAAK;AACnB;AAaO,IAAM,wBAAwB,CACnC,QACA,MACA,cACA,MACA,UACA,QAA0B,uBACvB;AACH,QAAM,qBAAoD,CAAC;AAE3D,QAAM,WAAW,EAAE,KAAK,KAAK,QAAQ,EAAE,SAAS,KAAK,CAAC,CAAC;AAEvD,MAAI,WAAW,YAAY,UAAU;AACnC,uBAAmB,SAAS,IAAI,IAAI;AAAA,MAClC,iBAAiB,MAAM,OAAO;AAAA,IAChC;AACA,uBAAmB,SAAS,EAAE,IAAI;AAAA,MAChC,iBAAiB,MAAM,OAAO;AAAA,IAChC;AAAA,EACF;AAEA,MACE,aACC,WAAW,YAAa,WAAW,YAAY,CAAC,eACjD;AACA,uBAAmB,SAAS,IAAI,IAAI;AAAA,MAClC,iBAAiB,MAAM,OAAO;AAAA,IAChC;AACA,uBAAmB,SAAS,EAAE,IAAI;AAAA,MAChC,iBAAiB,MAAM,OAAO;AAAA,IAChC;AAAA,EACF;AAEA,MAAI,SAAS,SAAS;AACpB,QAAI,UAAU;AACZ,yBAAmB,SAAS,IAAI,IAAI;AAAA,QAClC,iBAAiB,MAAM,OAAO;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,QAAQ;AACnB,QAAI,UAAU;AACZ,yBAAmB,SAAS,IAAI,IAAI;AAAA,QAClC,iBAAiB,MAAM,OAAO;AAAA,MAChC;AACA,yBAAmB,SAAS,EAAE,IAAI;AAAA,QAChC,iBAAiB,MAAM,OAAO;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,eAAe,CAAC,MAAa,SAAoC;AAC5E,QAAM,OAAO,IAAI,MAAM,KAAK,IAAI,CAAC;AACjC,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,KAAK,IAAI;AAAA,EACvB,SAAS,GAAG;AACV,WAAO;AAAA,EACT;AACF;;;AEtGA,SAAS,WAAW,YAAY,aAAa,eAAe;;;AC2CrD,IAAM,mBAAmB,CAAC,EAAE,OAAO,MAAiC;AACzE,SAAO;AAAA,IACL;AAAA,IACA,kBAAkB;AAAA,IAClB,QAAQ;AAAA,IACR,UAAU,OAAO,MAAM,CAAC;AAAA,IACxB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa,CAAC,CAAC,OAAO;AAAA,IACtB,cAAc,CAAC,OAAO;AAAA,IACtB,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACjB;AACF;AAEO,IAAM,UAAU,CAAC,OAAc,WAA0B;AAC9D,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG,iBAAiB,OAAO,OAAO;AAAA,MACpC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG,iBAAiB;AAAA,UAClB,QAAQ,MAAM;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF,KAAK;AACH,UAAI,MAAM,SAAS,QAAQ;AACzB,eAAO,EAAE,GAAG,OAAO,MAAM,QAAQ;AAAA,MACnC;AACA,aAAO,EAAE,GAAG,OAAO,MAAM,OAAO;AAAA,IAClC,KAAK;AACH,UAAI,MAAM,cAAc;AACtB,eAAO;AAAA,MACT;AACA,UAAI,CAAC,UAAU,QAAQ,EAAE,SAAS,MAAM,MAAM,GAAG;AAC/C,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,kBAAkB,MAAM,mBAAmB;AAAA,QAC3C,SAAS,MAAM,OAAO,MAAM,MAAM,gBAAgB;AAAA,QAClD,UACE,MAAM,mBAAmB,MAAM,OAAO,MAAM,SAAS,IACjD,MAAM,OAAO,MAAM,MAAM,mBAAmB,CAAC,IAC7C;AAAA,QACN,aAAa;AAAA,QACb,cAAc;AAAA,QACd,QAAQ;AAAA,MACV;AAAA,IAEF,KAAK,eAAe;AAClB,YAAM,EAAE,MAAM,MAAM,iBAAiB,IAAI,OAAO;AAEhD,UAAI,QAAQ,qBAAqB,SAAS,KAAK,YAAY,GAAG;AAC5D,eAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,UACN,cAAc;AAAA,UACd,gBAAgB;AAAA,QAClB;AAAA,MACF;AAEA,YAAM,cAAc,CAAC,6BAAM,KAAK,6BAAM,GAAG,EAAE;AAAA,SACzC,+BAAO,aAAY;AAAA,MACrB;AACA,YAAM,iBACJ,MAAM,qBAAqB,MAAM,OAAO,MAAM,SAAS;AAEzD,UAAI,CAAC,aAAa;AAChB,eAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,UACN,cAAc;AAAA,UACd,eAAe;AAAA,QACjB;AAAA,MACF;AAEA,UAAI,gBAAgB;AAClB,eAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,UACN,cAAc;AAAA,UACd,gBAAgB;AAAA,QAClB;AAAA,MACF;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,kBAAkB,MAAM,mBAAmB;AAAA,QAC3C,UAAU,MAAM,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAAA,QACvD,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,cAAc;AAAA,MAChB;AAAA,IACF;AAAA,IAEA,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,gBAAgB;AAAA,MAClB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,eAAe;AAAA,MACjB;AAAA,IAEF;AACE,aAAO;AAAA,EACX;AACF;;;ADnKA,SAAS,2BAA2B;AAgB7B,IAAM,iBAAiB,CAC5B,QACA,SACA,QACA,mBAA4B,SACD;AAxB7B;AAyBE,QAAM,cAAc,oBAAoB;AAExC,QAAM,CAAC,OAAO,QAAQ,IAAI,WAAW,SAAS,EAAE,OAAO,GAAG,gBAAgB;AAE1E,QAAM;AAAA,IACJ;AAAA,IACA,SAAS,EAAE,UAAU,YAAY;AAAA,EACnC,IAAI;AAEJ,QAAM,eAAe;AAAA,IACnB,CAACC,YAAmB;AAClB,kBAAYA,QAAO,KAAK,eAAeA,OAAM,CAAC;AAC9C,eAAS,EAAE,MAAM,cAAc,SAAS,EAAE,QAAAA,QAAO,EAAE,CAAC;AAAA,IACtD;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,YAAU,MAAM;AACd,iBAAa,MAAM;AAAA,EACrB,GAAG,CAAC,KAAK,UAAU,MAAM,GAAG,YAAY,CAAC;AAEzC,YAAU,MAAM;AACd,QAAI,eAAe,KAAK,IAAI,MAAM,OAAO,OAAO,MAAM,aAAa;AACjE;AAAA,QACE,MACE,SAAS;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,aAAa,MAAM,WAAW,CAAC;AAEnC,YAAU,MAAM;AACd,QAAI,MAAM,SAAS;AACjB,eAAS,MAAM,OAAO;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEA,QAAM,SAAS,YAAY,MAAM;AAC/B,aAAS,EAAE,MAAM,cAAc,CAAC;AAAA,EAClC,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,YAAY,MAAM;AACpC,iBAAa,MAAM;AAAA,EACrB,GAAG,CAAC,cAAc,MAAM,CAAC;AAEzB,QAAM,gBAAwC;AAAA,IAC5C,OAAO;AAAA,MACL,QAAQ,MAAM;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,cAAc,MAAM;AAAA,MACpB,aAAa,MAAM;AAAA,MACnB,aAAa,MAAM;AAAA,MACnB,YAAY,OAAO,MAAM;AAAA,IAC3B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AAEA,YAAU,MAAM;AAvGlB,QAAAC,KAAA;AAwGI,UAAIA,MAAA,6BAAM,cAAN,gBAAAA,IAAiB,WAAU,KAAK,OAAO,gBAAgB,IAAI,IAAI;AACjE;AAAA,IACF;AACA,QAAI,KAAK,QAAQ,EAAE,SAAS,OAAO,OAAO,gBAAgB,IAAI,IAAI;AAChE,eAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS;AAAA,UACP,QAAM,sDAAa,SAAb,mBAAmB,QAAQ,EAAE,SAAS,KAAK,OAA3C,mBAA+C,UAAS;AAAA,UAC9D;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC;AAED,eAAS;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,GAAG,EAAC,kCAAM,cAAN,mBAAiB,MAAM,CAAC;AAE5B,YAAU,MAAM;AACd,QAAI,MAAM,WAAW,YAAY,CAAC,MAAM,kBAAkB,SAAS;AACjE,cAAQ,aAAa;AACrB,eAAS,EAAE,MAAM,qBAAqB,CAAC;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,MAAM,QAAQ,MAAM,cAAc,CAAC;AAEvC,YAAU,MAAM;AACd,QAAI,MAAM,WAAW,YAAY,CAAC,MAAM,iBAAiB,QAAQ;AAC/D,aAAO,aAAa;AACpB,eAAS,EAAE,MAAM,oBAAoB,CAAC;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,MAAM,QAAQ,MAAM,aAAa,CAAC;AAEtC,SAAO;AACT;;;AHrIA,SAAS,iBAAiB;;;AKN1B,OAAO,WAAW;AAGX,IAAM,qBAAqB,MAAM,cAE9B,IAAI;AAEP,IAAM,wBAAwB,MAAM;AACzC,QAAM,UAAU,MAAM,WAAW,kBAAkB;AACnD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACfA,OAAOC,UAAS,eAAe,kBAAkB;AAO1C,IAAM,0BACX,cAAgC,kBAAkB;AAM7C,IAAM,sBAAsB,MAAwB;AACzD,SAAO,WAAW,uBAAuB;AAC3C;AAUO,IAAM,sBAA0D,CAAC;AAAA,EACtE;AAAA,EACA;AACF,MAAM;AACJ,SACE,gBAAAC,OAAA,cAAC,wBAAwB,UAAxB,EAAiC,OAAO,SACtC,QACH;AAEJ;;;ACnCA,SAAS,aAAa;AAmBf,IAAM,mBAAmB,CAC9B,iBACqB;AACrB,MAAI,CAAC,cAAc;AACjB,WAAO,EAAE,GAAG,mBAAmB;AAAA,EACjC;AAEA,SAAO,MAAM,CAAC,GAAG,oBAAoB,YAAY;AACnD;;;APGA,IAAM,kBAAkD,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,eAAe,QAAQ,SAAS,QAAQ,gBAAgB;AAExE,SACE,gBAAAC,OAAA,cAAC,mBAAmB,UAAnB,EAA4B,OAAO,WACjC,QACH;AAEJ;AAEO,IAAM,OAAqD,CAAC;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AACF,MAAM;AAEJ,QAAM,cAAcA,OAAM,QAAQ,MAAM,iBAAiB,KAAK,GAAG,CAAC,KAAK,CAAC;AAExE,SACE,gBAAAA,OAAA;AAAA,IAAC,UAAU;AAAA,IAAV;AAAA,MACC,KAAK,OAAO;AAAA,MACZ,aAAa,eAAe,MAAM;AAAA,MAClC,OAAO;AAAA;AAAA,IAEP,gBAAAA,OAAA,cAAC,uBAAoB,OAAO,eAC1B,gBAAAA,OAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,MAEC;AAAA,IACH,CACF;AAAA,EACF;AAEJ;AAEA,KAAK,cAAc;;;AQ7EnB,OAAOC,YAAW;AAClB;AAAA,EACE,aAAAC;AAAA,EACA;AAAA,EACA,uBAAAC;AAAA,OACK;AASA,IAAM,cAAcC,OAAM;AAAA,EAC/B,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ;AAC7B,UAAM,gBAAgB,sBAAsB;AAC5C,UAAM,cAAcC,qBAAoB;AACxC,UAAM,QAAQ,oBAAoB;AAElC,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,UAAM,EAAE,KAAK,IAAI;AACjB,UAAM,EAAE,QAAQ,MAAM,cAAc,SAAS,IAAI;AAEjD,UAAM,gBAAgB,2BAA2B,WAAW,CAAC,GAAG;AAAA,MAC9D,cAAc;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa,MAAM,QAAQ;AAAA,QAC3B;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,gBAAAD,OAAA,cAACE,WAAU,OAAV,EAAgB,KAAW,GAAG,MAAM,SAAS,eAAe;AAAA,EACtE;AACF;AAEA,YAAY,cAAc;;;AC7C1B,OAAOC,YAAW;AAClB,SAAS,YAAY;AAkBrB,IAAM,gBAA0B,CAAC,UAAU,QAAQ;AAE5C,IAAM,QAAQC,OAAM;AAAA,EAIzB,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,gBAAgB,sBAAsB;AAC5C,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,UAAM,EAAE,cAAc,QAAQ,eAAe,OAAO,IAAI;AAExD,UAAM,cAAcA,OAAM,YAAY,MAAM;AAC1C,mBAAa,UAAU,aAAa;AACpC,yCAAU;AAAA,IACZ,GAAG,CAAC,cAAc,QAAQ,eAAe,eAAe,OAAO,CAAC;AAEhE,QAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,aAAO;AAAA,IACT;AAEA,WAAO,UACL,gBAAAA,OAAA,cAAC,QAAK,KAAU,SAAS,aAAa,WAAuB,GAAG,QAC7D,QACH,IAEA,gBAAAA,OAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACR,GAAG;AAAA;AAAA,MAEH;AAAA,IACH;AAAA,EAEJ;AACF;AAEA,MAAM,cAAc;;;ACtEpB,OAAOC,YAAW;AAClB,SAAS,QAAAC,aAAY;AAgBrB,IAAMC,iBAA0B,CAAC,eAAe,aAAa;AAEtD,IAAM,OAAOC,OAAM,WAGxB,CAAC,EAAE,UAAU,SAAS,SAASD,gBAAe,WAAW,GAAG,KAAK,GAAG,QAAQ;AAC5E,QAAM,gBAAgB,sBAAsB;AAC5C,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,QAAM,EAAE,QAAQ,OAAO,IAAI;AAE3B,QAAM,cAAcC,OAAM,YAAY,MAAM;AAC1C,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,UACL,gBAAAA,OAAA,cAACC,OAAA,EAAK,KAAU,SAAS,aAAa,WAAuB,GAAG,QAC7D,QACH,IAEA,gBAAAD,OAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL;AAAA,MACA,SAAS;AAAA,MACR,GAAG;AAAA;AAAA,IAEH;AAAA,EACH;AAEJ,CAAC;AAED,KAAK,cAAc;;;ACjDZ,IAAM,cAAc;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AACF;","names":["React","puzzle","_a","React","React","React","React","ChessGame","useChessGameContext","React","useChessGameContext","ChessGame","React","React","React","Slot","defaultShowOn","React","Slot"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-chess-tools/react-chess-puzzle",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A lightweight, customizable React component library for rendering and interacting with chess puzzles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"author": "Daniele Cammareri <daniele.cammareri@gmail.com>",
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@
|
|
44
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
45
|
+
"@react-chess-tools/react-chess-game": "1.0.1",
|
|
45
46
|
"chess.js": "^1.4.0",
|
|
46
47
|
"lodash": "^4.17.21"
|
|
47
48
|
},
|
|
@@ -137,3 +137,49 @@ export const WithKeyboardControls = (args: RootProps) => {
|
|
|
137
137
|
</ChessPuzzle.Root>
|
|
138
138
|
);
|
|
139
139
|
};
|
|
140
|
+
|
|
141
|
+
// Puzzle with multiple checkmate solutions for testing solveOnCheckmate prop
|
|
142
|
+
const multiMatePuzzle = {
|
|
143
|
+
fen: "7k/R7/1R6/2Q5/4Q3/8/8/7K w - - 0 1",
|
|
144
|
+
moves: ["a7a8"], // Canonical solution
|
|
145
|
+
makeFirstMove: false,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const MultiMatePuzzle = (args: RootProps) => {
|
|
149
|
+
return (
|
|
150
|
+
<div>
|
|
151
|
+
<p style={{ marginBottom: "1rem", fontSize: "0.875rem" }}>
|
|
152
|
+
<strong>solveOnCheckmate=true (default):</strong> Any checkmate move
|
|
153
|
+
solves the puzzle. Try Qc8# (queen mate), Qf8# (queen mate), Rb8# (rook
|
|
154
|
+
mate), or the canonical Ra8#.
|
|
155
|
+
</p>
|
|
156
|
+
<ChessPuzzle.Root {...args} puzzle={multiMatePuzzle}>
|
|
157
|
+
<ChessPuzzle.Board />
|
|
158
|
+
<ChessPuzzle.Reset asChild>
|
|
159
|
+
<button>restart</button>
|
|
160
|
+
</ChessPuzzle.Reset>
|
|
161
|
+
</ChessPuzzle.Root>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const MultiMatePuzzleStrict = (args: RootProps) => {
|
|
167
|
+
return (
|
|
168
|
+
<div>
|
|
169
|
+
<p style={{ marginBottom: "1rem", fontSize: "0.875rem" }}>
|
|
170
|
+
<strong>solveOnCheckmate=false:</strong> Only the canonical solution
|
|
171
|
+
(a7a8#) is accepted. Alternative mates like Qc8# will fail the puzzle.
|
|
172
|
+
</p>
|
|
173
|
+
<ChessPuzzle.Root
|
|
174
|
+
{...args}
|
|
175
|
+
puzzle={multiMatePuzzle}
|
|
176
|
+
solveOnCheckmate={false}
|
|
177
|
+
>
|
|
178
|
+
<ChessPuzzle.Board />
|
|
179
|
+
<ChessPuzzle.Reset asChild>
|
|
180
|
+
<button>restart</button>
|
|
181
|
+
</ChessPuzzle.Reset>
|
|
182
|
+
</ChessPuzzle.Root>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
};
|