@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/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.FC<PuzzleBoardProps>;
76
- Reset: React.FC<React.PropsWithChildren<ResetProps>>;
77
- Hint: React.FC<React.PropsWithChildren<HintProps>>;
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 React4 from "react";
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 React2 from "react";
293
- var ChessPuzzleContext = React2.createContext(null);
301
+ import React from "react";
302
+ var ChessPuzzleContext = React.createContext(null);
294
303
  var useChessPuzzleContext = () => {
295
- const context = React2.useContext(ChessPuzzleContext);
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 React3, { createContext, useContext } from "react";
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__ */ React3.createElement(ChessPuzzleThemeContext.Provider, { value: theme }, children);
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__ */ React4.createElement(ChessPuzzleContext.Provider, { value: context }, children);
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 = React4.useMemo(() => mergePuzzleTheme(theme), [theme]);
344
- return /* @__PURE__ */ React4.createElement(
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__ */ React4.createElement(PuzzleThemeProvider, { theme: mergedTheme }, /* @__PURE__ */ React4.createElement(PuzzleRootInner, { puzzle, onSolve, onFail }, children))
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 React5 from "react";
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
- ...rest
365
- }) => {
366
- const puzzleContext = useChessPuzzleContext();
367
- const gameContext = useChessGameContext2();
368
- const theme = useChessPuzzleTheme();
369
- if (!puzzleContext) {
370
- throw new Error("PuzzleContext not found");
371
- }
372
- if (!gameContext) {
373
- throw new Error("ChessGameContext not found");
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
- const { game } = gameContext;
376
- const { status, hint, isPlayerTurn, nextMove } = puzzleContext;
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 React6 from "react";
412
+ import React5 from "react";
413
+ import { Slot } from "@radix-ui/react-slot";
392
414
  var defaultShowOn = ["failed", "solved"];
393
- var Reset = ({
394
- children,
395
- asChild,
396
- puzzle,
397
- onReset,
398
- showOn = defaultShowOn
399
- }) => {
400
- const puzzleContext = useChessPuzzleContext();
401
- if (!puzzleContext) {
402
- throw new Error("PuzzleContext not found");
403
- }
404
- const { changePuzzle, status } = puzzleContext;
405
- const handleClick = () => {
406
- changePuzzle(puzzle || puzzleContext.puzzle);
407
- onReset == null ? void 0 : onReset(puzzleContext);
408
- };
409
- if (!showOn.includes(status)) {
410
- return null;
411
- }
412
- if (asChild) {
413
- const child = React6.Children.only(children);
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
- return /* @__PURE__ */ React6.createElement("button", { type: "button", onClick: handleClick }, children);
423
- };
449
+ );
450
+ Reset.displayName = "ChessPuzzle.Reset";
424
451
 
425
452
  // src/components/ChessPuzzle/parts/Hint.tsx
426
- import React7 from "react";
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
- if (asChild) {
445
- const child = React7.Children.only(children);
446
- if (isClickableElement(child)) {
447
- return React7.cloneElement(child, {
448
- onClick: handleClick
449
- });
450
- } else {
451
- throw new Error("Change child must be a clickable element");
452
- }
453
- }
454
- return /* @__PURE__ */ React7.createElement("button", { type: "button", onClick: handleClick }, children);
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":";AAAA,OAAOA,YAAW;;;ACAlB,SAAqB,aAAmB;AACxC,OAAO,WAAuD;AAC9D,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;AAQO,IAAM,qBAAqB,CAChC,YACgC,MAAM,eAAe,OAAO;AAavD,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;;;AEhHA,SAAS,WAAW,YAAY,aAAa,eAAe;;;AC0CrD,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,KAAK,IAAI,OAAO;AAExB,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;;;ADvJA,SAAS,2BAA2B;AAgB7B,IAAM,iBAAiB,CAC5B,QACA,SACA,WAC2B;AAvB7B;AAwBE,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;AAtGlB,QAAAC,KAAA;AAuGI,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,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;;;AHnIA,SAAS,iBAAiB;;;AKN1B,OAAOC,YAAW;AAGX,IAAM,qBAAqBA,OAAM,cAE9B,IAAI;AAEP,IAAM,wBAAwB,MAAM;AACzC,QAAM,UAAUA,OAAM,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;;;APAA,IAAM,kBAAkD,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,eAAe,QAAQ,SAAS,MAAM;AAEtD,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;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,cAAC,mBAAgB,QAAgB,SAAkB,UAChD,QACH,CACF;AAAA,EACF;AAEJ;;;AQjEA,OAAOC,YAAW;AAClB;AAAA,EACE,aAAAC;AAAA,EACA;AAAA,EACA,uBAAAC;AAAA,OACK;AASA,IAAM,cAA0C,CAAC;AAAA,EACtD,UAAU,CAAC;AAAA,EACX,GAAG;AACL,MAAM;AACJ,QAAM,gBAAgB,sBAAsB;AAC5C,QAAM,cAAcC,qBAAoB;AACxC,QAAM,QAAQ,oBAAoB;AAElC,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,EAAE,QAAQ,MAAM,cAAc,SAAS,IAAI;AAEjD,QAAM,gBAAgB,2BAA2B,SAAS;AAAA,IACxD,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,MAAM,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,gBAAAC,OAAA,cAACC,WAAU,OAAV,EAAiB,GAAG,MAAM,SAAS,eAAe;AAC5D;;;AC5CA,OAAOC,YAAW;AAWlB,IAAM,gBAA0B,CAAC,UAAU,QAAQ;AAE5C,IAAM,QAAuD,CAAC;AAAA,EACnE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACX,MAAM;AACJ,QAAM,gBAAgB,sBAAsB;AAC5C,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,QAAM,EAAE,cAAc,OAAO,IAAI;AACjC,QAAM,cAAc,MAAM;AACxB,iBAAa,UAAU,cAAc,MAAM;AAC3C,uCAAU;AAAA,EACZ;AAEA,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AACX,UAAM,QAAQC,OAAM,SAAS,KAAK,QAAQ;AAC1C,QAAI,mBAAmB,KAAK,GAAG;AAC7B,aAAOA,OAAM,aAAa,OAAO;AAAA,QAC/B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,OAAO;AACL,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,EACF;AAEA,SACE,gBAAAA,OAAA,cAAC,YAAO,MAAK,UAAS,SAAS,eAC5B,QACH;AAEJ;;;AClDA,OAAOC,YAAW;AASlB,IAAMC,iBAA0B,CAAC,eAAe,aAAa;AAEtD,IAAM,OAAqD,CAAC;AAAA,EACjE;AAAA,EACA;AAAA,EACA,SAASA;AACX,MAAM;AACJ,QAAM,gBAAgB,sBAAsB;AAC5C,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,QAAM,EAAE,QAAQ,OAAO,IAAI;AAC3B,QAAM,cAAc,MAAM;AACxB,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AACX,UAAM,QAAQC,OAAM,SAAS,KAAK,QAAQ;AAC1C,QAAI,mBAAmB,KAAK,GAAG;AAC7B,aAAOA,OAAM,aAAa,OAAO;AAAA,QAC/B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,OAAO;AACL,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,EACF;AAEA,SACE,gBAAAA,OAAA,cAAC,YAAO,MAAK,UAAS,SAAS,eAC5B,QACH;AAEJ;;;ACxCO,IAAM,cAAc;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AACF;","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":"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","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.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
- "@react-chess-tools/react-chess-game": "1.0.0",
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
+ };