react-chess-core 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/chessboard/CorrectMoveCheckBadge.d.ts +3 -0
- package/dist/features/chessboard/HighlightChessboard.d.ts +5 -1
- package/dist/features/chessboard/boardSquareHighlightColors.d.ts +3 -0
- package/dist/features/chessboard/createFeedbackSquareRenderer.d.ts +3 -0
- package/dist/features/chessboard/index.d.ts +1 -0
- package/dist/features/chessboard/useClickToMove.d.ts +28 -0
- package/dist/features/training/correctMoveFeedbackMs.d.ts +2 -0
- package/dist/features/training/index.d.ts +2 -0
- package/dist/features/training/useCorrectMoveFeedback.d.ts +6 -0
- package/dist/index.esm.js +256 -5
- package/dist/index.js +257 -3
- package/dist/stories/Chessboard.stories.d.ts +2 -0
- package/package.json +1 -1
|
@@ -2,6 +2,10 @@ export interface HighlightChessboardProps {
|
|
|
2
2
|
checkSquare: string;
|
|
3
3
|
hintSquare: string | null;
|
|
4
4
|
incorrectMoveSquare: string | null;
|
|
5
|
+
/** Destination square of the last correct training move — shows a green check. */
|
|
6
|
+
correctMoveSquare?: string | null;
|
|
7
|
+
/** Enable click-to-move when `onPieceDrop` is provided. Defaults to true. */
|
|
8
|
+
clickToMove?: boolean;
|
|
5
9
|
[key: string]: any;
|
|
6
10
|
}
|
|
7
|
-
export declare const HighlightChessboard: ({ checkSquare, hintSquare, incorrectMoveSquare, customSquareStyles: extraSquareStyles, ...props }: HighlightChessboardProps) => import("react").JSX.Element;
|
|
11
|
+
export declare const HighlightChessboard: ({ checkSquare, hintSquare, incorrectMoveSquare, correctMoveSquare, clickToMove, customSquareStyles: extraSquareStyles, customBoardStyle, onPieceDrop, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, showPromotionDialog: showPromotionDialogProp, promotionToSquare: promotionToSquareProp, ...props }: HighlightChessboardProps) => import("react").JSX.Element;
|
|
@@ -6,4 +6,7 @@ export declare const boardSquareHighlightColors: {
|
|
|
6
6
|
readonly hint: "rgba(119, 177, 212, 0.75)";
|
|
7
7
|
/** Muted red — softer than the in-check highlight. */
|
|
8
8
|
readonly incorrect: "rgba(140, 38, 38, 0.82)";
|
|
9
|
+
readonly selected: "rgba(255, 255, 0, 0.45)";
|
|
10
|
+
readonly moveTarget: "radial-gradient(circle, rgba(0, 0, 0, 0.18) 22%, transparent 22%)";
|
|
11
|
+
readonly captureTarget: "radial-gradient(circle, rgba(0, 0, 0, 0.18) 72%, transparent 72%)";
|
|
9
12
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Square } from 'chess.js';
|
|
2
|
+
import { type CSSProperties } from 'react';
|
|
3
|
+
type Piece = string;
|
|
4
|
+
export type ClickToMoveSquareStyles = Record<string, CSSProperties>;
|
|
5
|
+
export type UseClickToMoveOptions = {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
position: string | undefined;
|
|
8
|
+
arePiecesDraggable?: boolean;
|
|
9
|
+
autoPromoteToQueen?: boolean;
|
|
10
|
+
isDraggablePiece?: (args: {
|
|
11
|
+
piece: Piece;
|
|
12
|
+
sourceSquare: Square;
|
|
13
|
+
}) => boolean;
|
|
14
|
+
onPromotionCheck?: (sourceSquare: Square, targetSquare: Square, piece: Piece) => boolean;
|
|
15
|
+
onPieceDrop?: (sourceSquare: Square, targetSquare: Square, piece: Piece) => boolean;
|
|
16
|
+
onSquareClick?: (square: Square, piece: Piece | undefined) => void;
|
|
17
|
+
onPromotionPieceSelect?: (piece?: Piece, promoteFromSquare?: Square, promoteToSquare?: Square) => boolean;
|
|
18
|
+
onPieceDragBegin?: (piece: Piece, sourceSquare: Square) => void;
|
|
19
|
+
};
|
|
20
|
+
export declare function useClickToMove({ enabled, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onPieceDrop, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, }: UseClickToMoveOptions): {
|
|
21
|
+
clickSquareStyles: ClickToMoveSquareStyles;
|
|
22
|
+
handleSquareClick: (square: Square, piece: Piece | undefined) => void;
|
|
23
|
+
handlePromotionPieceSelect: (piece?: Piece, promoteFromSquare?: Square, promoteToSquare?: Square) => boolean;
|
|
24
|
+
handlePieceDragBegin: (piece: Piece, sourceSquare: Square) => void;
|
|
25
|
+
showPromotionDialog: boolean;
|
|
26
|
+
promotionToSquare: Square | null;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import { createContext, useContext,
|
|
2
|
+
import { createContext, useContext, useState, useCallback, useEffect, useMemo, useRef, useLayoutEffect, Component } from 'react';
|
|
3
3
|
import { ChessboardDnDProvider as ChessboardDnDProvider$1, Chessboard } from 'react-chessboard';
|
|
4
4
|
import { Chess } from 'chess.js';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
@@ -1741,8 +1741,200 @@ const boardSquareHighlightColors = {
|
|
|
1741
1741
|
hint: 'rgba(119, 177, 212, 0.75)',
|
|
1742
1742
|
/** Muted red — softer than the in-check highlight. */
|
|
1743
1743
|
incorrect: 'rgba(140, 38, 38, 0.82)',
|
|
1744
|
+
selected: 'rgba(255, 255, 0, 0.45)',
|
|
1745
|
+
moveTarget: 'radial-gradient(circle, rgba(0, 0, 0, 0.18) 22%, transparent 22%)',
|
|
1746
|
+
captureTarget: 'radial-gradient(circle, rgba(0, 0, 0, 0.18) 72%, transparent 72%)',
|
|
1744
1747
|
};
|
|
1745
1748
|
|
|
1749
|
+
const badgeStyle = {
|
|
1750
|
+
position: 'absolute',
|
|
1751
|
+
right: '6%',
|
|
1752
|
+
bottom: '6%',
|
|
1753
|
+
width: '26%',
|
|
1754
|
+
height: '26%',
|
|
1755
|
+
minWidth: 14,
|
|
1756
|
+
minHeight: 14,
|
|
1757
|
+
maxWidth: 26,
|
|
1758
|
+
maxHeight: 26,
|
|
1759
|
+
borderRadius: '50%',
|
|
1760
|
+
backgroundColor: '#2e7d32',
|
|
1761
|
+
display: 'flex',
|
|
1762
|
+
alignItems: 'center',
|
|
1763
|
+
justifyContent: 'center',
|
|
1764
|
+
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.35)',
|
|
1765
|
+
pointerEvents: 'none',
|
|
1766
|
+
};
|
|
1767
|
+
/** Green circle with a white check, anchored to the bottom-right of a square
|
|
1768
|
+
* square (over the piece). */
|
|
1769
|
+
const CorrectMoveCheckBadge = () => (jsx("span", { "aria-hidden": true, style: badgeStyle, children: jsx("svg", { viewBox: "0 0 12 12", width: "62%", height: "62%", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M2.5 6.2 5 8.7 9.5 3.8", stroke: "#fff", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
|
|
1770
|
+
|
|
1771
|
+
const overlayStyle$1 = {
|
|
1772
|
+
position: 'absolute',
|
|
1773
|
+
inset: 0,
|
|
1774
|
+
pointerEvents: 'none',
|
|
1775
|
+
zIndex: 20,
|
|
1776
|
+
overflow: 'visible',
|
|
1777
|
+
};
|
|
1778
|
+
function createFeedbackSquareRenderer(correctMoveSquare) {
|
|
1779
|
+
return function FeedbackSquare({ children, style, square, ref }) {
|
|
1780
|
+
return (jsxs("div", { ref: ref, style: Object.assign(Object.assign({}, style), { position: 'relative', overflow: 'visible' }), children: [children, square === correctMoveSquare ? (jsx("div", { style: overlayStyle$1, children: jsx(CorrectMoveCheckBadge, {}) })) : null] }));
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
function defaultPromotionCheck(sourceSquare, targetSquare, piece) {
|
|
1785
|
+
return (((piece === 'wP' && sourceSquare[1] === '7' && targetSquare[1] === '8') ||
|
|
1786
|
+
(piece === 'bP' && sourceSquare[1] === '2' && targetSquare[1] === '1')) &&
|
|
1787
|
+
Math.abs(sourceSquare.charCodeAt(0) - targetSquare.charCodeAt(0)) <= 1);
|
|
1788
|
+
}
|
|
1789
|
+
function pieceAtSquare(fen, square) {
|
|
1790
|
+
const boardPiece = new Chess(fen).get(square);
|
|
1791
|
+
if (!boardPiece) {
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
const color = boardPiece.color === 'w' ? 'w' : 'b';
|
|
1795
|
+
const type = boardPiece.type.toUpperCase();
|
|
1796
|
+
return `${color}${type}`;
|
|
1797
|
+
}
|
|
1798
|
+
function getMoveOptionStyles(fen, fromSquare) {
|
|
1799
|
+
const chess = new Chess(fen);
|
|
1800
|
+
const moves = chess.moves({ square: fromSquare, verbose: true });
|
|
1801
|
+
if (!moves.length) {
|
|
1802
|
+
return {
|
|
1803
|
+
[fromSquare]: { backgroundColor: boardSquareHighlightColors.selected },
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
const styles = {
|
|
1807
|
+
[fromSquare]: { backgroundColor: boardSquareHighlightColors.selected },
|
|
1808
|
+
};
|
|
1809
|
+
for (const move of moves) {
|
|
1810
|
+
styles[move.to] = {
|
|
1811
|
+
background: chess.get(move.to)
|
|
1812
|
+
? boardSquareHighlightColors.captureTarget
|
|
1813
|
+
: boardSquareHighlightColors.moveTarget,
|
|
1814
|
+
borderRadius: '50%',
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
return styles;
|
|
1818
|
+
}
|
|
1819
|
+
function useClickToMove({ enabled, position, arePiecesDraggable = true, autoPromoteToQueen = false, isDraggablePiece, onPromotionCheck = defaultPromotionCheck, onPieceDrop, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, }) {
|
|
1820
|
+
var _a;
|
|
1821
|
+
const [moveFrom, setMoveFrom] = useState(null);
|
|
1822
|
+
const [pendingPromotion, setPendingPromotion] = useState(null);
|
|
1823
|
+
const fen = typeof position === 'string' ? position : undefined;
|
|
1824
|
+
const clearSelection = useCallback(() => {
|
|
1825
|
+
setMoveFrom(null);
|
|
1826
|
+
setPendingPromotion(null);
|
|
1827
|
+
}, []);
|
|
1828
|
+
useEffect(() => {
|
|
1829
|
+
clearSelection();
|
|
1830
|
+
}, [fen, clearSelection]);
|
|
1831
|
+
const clickSquareStyles = useMemo(() => {
|
|
1832
|
+
if (!enabled || !moveFrom || !fen) {
|
|
1833
|
+
return {};
|
|
1834
|
+
}
|
|
1835
|
+
return getMoveOptionStyles(fen, moveFrom);
|
|
1836
|
+
}, [enabled, fen, moveFrom]);
|
|
1837
|
+
const canSelectPiece = useCallback((square, piece) => {
|
|
1838
|
+
if (!piece || !arePiecesDraggable || !onPieceDrop) {
|
|
1839
|
+
return false;
|
|
1840
|
+
}
|
|
1841
|
+
if (isDraggablePiece) {
|
|
1842
|
+
return isDraggablePiece({ piece, sourceSquare: square });
|
|
1843
|
+
}
|
|
1844
|
+
return true;
|
|
1845
|
+
}, [arePiecesDraggable, isDraggablePiece, onPieceDrop]);
|
|
1846
|
+
const tryCompleteMove = useCallback((from, to, piece) => {
|
|
1847
|
+
if (!onPieceDrop) {
|
|
1848
|
+
return false;
|
|
1849
|
+
}
|
|
1850
|
+
if (onPromotionCheck(from, to, piece)) {
|
|
1851
|
+
if (autoPromoteToQueen) {
|
|
1852
|
+
const promotedPiece = piece[0] === 'w' ? 'wQ' : 'bQ';
|
|
1853
|
+
const accepted = onPieceDrop(from, to, promotedPiece);
|
|
1854
|
+
clearSelection();
|
|
1855
|
+
return accepted;
|
|
1856
|
+
}
|
|
1857
|
+
setPendingPromotion({ from, to, piece });
|
|
1858
|
+
setMoveFrom(null);
|
|
1859
|
+
return true;
|
|
1860
|
+
}
|
|
1861
|
+
const accepted = onPieceDrop(from, to, piece);
|
|
1862
|
+
clearSelection();
|
|
1863
|
+
return accepted;
|
|
1864
|
+
}, [autoPromoteToQueen, clearSelection, onPieceDrop, onPromotionCheck]);
|
|
1865
|
+
const handleSquareClick = useCallback((square, piece) => {
|
|
1866
|
+
onSquareClick === null || onSquareClick === void 0 ? void 0 : onSquareClick(square, piece);
|
|
1867
|
+
if (!enabled || !onPieceDrop || !arePiecesDraggable || !fen) {
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
if (!moveFrom) {
|
|
1871
|
+
if (canSelectPiece(square, piece)) {
|
|
1872
|
+
setMoveFrom(square);
|
|
1873
|
+
}
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
if (square === moveFrom) {
|
|
1877
|
+
clearSelection();
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
const sourcePiece = pieceAtSquare(fen, moveFrom);
|
|
1881
|
+
if (!sourcePiece) {
|
|
1882
|
+
clearSelection();
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
const accepted = tryCompleteMove(moveFrom, square, sourcePiece);
|
|
1886
|
+
if (accepted) {
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
if (canSelectPiece(square, piece)) {
|
|
1890
|
+
setMoveFrom(square);
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
clearSelection();
|
|
1894
|
+
}, [
|
|
1895
|
+
arePiecesDraggable,
|
|
1896
|
+
canSelectPiece,
|
|
1897
|
+
clearSelection,
|
|
1898
|
+
enabled,
|
|
1899
|
+
fen,
|
|
1900
|
+
moveFrom,
|
|
1901
|
+
onPieceDrop,
|
|
1902
|
+
onSquareClick,
|
|
1903
|
+
tryCompleteMove,
|
|
1904
|
+
]);
|
|
1905
|
+
const handlePromotionPieceSelect = useCallback((piece, promoteFromSquare, promoteToSquare) => {
|
|
1906
|
+
if (pendingPromotion && piece) {
|
|
1907
|
+
const { from, to } = pendingPromotion;
|
|
1908
|
+
onPieceDrop === null || onPieceDrop === void 0 ? void 0 : onPieceDrop(from, to, piece);
|
|
1909
|
+
onPromotionPieceSelect === null || onPromotionPieceSelect === void 0 ? void 0 : onPromotionPieceSelect(piece, from, to);
|
|
1910
|
+
clearSelection();
|
|
1911
|
+
return false;
|
|
1912
|
+
}
|
|
1913
|
+
if (onPromotionPieceSelect) {
|
|
1914
|
+
return onPromotionPieceSelect(piece, promoteFromSquare, promoteToSquare);
|
|
1915
|
+
}
|
|
1916
|
+
return true;
|
|
1917
|
+
}, [clearSelection, onPieceDrop, onPromotionPieceSelect, pendingPromotion]);
|
|
1918
|
+
const handlePieceDragBegin = useCallback((piece, sourceSquare) => {
|
|
1919
|
+
clearSelection();
|
|
1920
|
+
onPieceDragBegin === null || onPieceDragBegin === void 0 ? void 0 : onPieceDragBegin(piece, sourceSquare);
|
|
1921
|
+
}, [clearSelection, onPieceDragBegin]);
|
|
1922
|
+
return {
|
|
1923
|
+
clickSquareStyles,
|
|
1924
|
+
handleSquareClick,
|
|
1925
|
+
handlePromotionPieceSelect,
|
|
1926
|
+
handlePieceDragBegin,
|
|
1927
|
+
showPromotionDialog: pendingPromotion !== null,
|
|
1928
|
+
promotionToSquare: (_a = pendingPromotion === null || pendingPromotion === void 0 ? void 0 : pendingPromotion.to) !== null && _a !== void 0 ? _a : null,
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
/** Prevent mobile long-press text selection and iOS callout menus on the board. */
|
|
1933
|
+
const nonSelectableBoardStyle = {
|
|
1934
|
+
userSelect: 'none',
|
|
1935
|
+
WebkitUserSelect: 'none',
|
|
1936
|
+
WebkitTouchCallout: 'none',
|
|
1937
|
+
};
|
|
1746
1938
|
const getCheckHighlighting = (checkSquare) => {
|
|
1747
1939
|
const styles = {};
|
|
1748
1940
|
styles[checkSquare] = { backgroundColor: boardSquareHighlightColors.check };
|
|
@@ -1761,12 +1953,40 @@ const getFeedbackHighlighting = (hintSquare, incorrectMoveSquare) => {
|
|
|
1761
1953
|
return styles;
|
|
1762
1954
|
};
|
|
1763
1955
|
const HighlightChessboard = (_a) => {
|
|
1764
|
-
var { checkSquare, hintSquare, incorrectMoveSquare, customSquareStyles: extraSquareStyles } = _a, props = __rest(_a, ["checkSquare", "hintSquare", "incorrectMoveSquare", "customSquareStyles"]);
|
|
1956
|
+
var { checkSquare, hintSquare, incorrectMoveSquare, correctMoveSquare = null, clickToMove, customSquareStyles: extraSquareStyles, customBoardStyle, onPieceDrop, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, showPromotionDialog: showPromotionDialogProp, promotionToSquare: promotionToSquareProp } = _a, props = __rest(_a, ["checkSquare", "hintSquare", "incorrectMoveSquare", "correctMoveSquare", "clickToMove", "customSquareStyles", "customBoardStyle", "onPieceDrop", "position", "arePiecesDraggable", "autoPromoteToQueen", "isDraggablePiece", "onPromotionCheck", "onSquareClick", "onPromotionPieceSelect", "onPieceDragBegin", "showPromotionDialog", "promotionToSquare"]);
|
|
1765
1957
|
const { customDarkSquareStyle, customLightSquareStyle } = useChessboardTheme();
|
|
1958
|
+
const clickToMoveEnabled = clickToMove !== false && typeof onPieceDrop === 'function';
|
|
1959
|
+
const { clickSquareStyles, handleSquareClick, handlePromotionPieceSelect, handlePieceDragBegin, showPromotionDialog: clickPromotionDialog, promotionToSquare: clickPromotionToSquare, } = useClickToMove({
|
|
1960
|
+
enabled: clickToMoveEnabled,
|
|
1961
|
+
position,
|
|
1962
|
+
arePiecesDraggable,
|
|
1963
|
+
autoPromoteToQueen,
|
|
1964
|
+
isDraggablePiece,
|
|
1965
|
+
onPromotionCheck,
|
|
1966
|
+
onPieceDrop,
|
|
1967
|
+
onSquareClick,
|
|
1968
|
+
onPromotionPieceSelect,
|
|
1969
|
+
onPieceDragBegin,
|
|
1970
|
+
});
|
|
1766
1971
|
const checkStyles = getCheckHighlighting(checkSquare);
|
|
1767
1972
|
const feedbackStyles = getFeedbackHighlighting(hintSquare, incorrectMoveSquare);
|
|
1768
|
-
const customSquareStyles = Object.assign(Object.assign(Object.assign({}, checkStyles), feedbackStyles), extraSquareStyles);
|
|
1769
|
-
|
|
1973
|
+
const customSquareStyles = Object.assign(Object.assign(Object.assign(Object.assign({}, clickSquareStyles), checkStyles), feedbackStyles), extraSquareStyles);
|
|
1974
|
+
const customSquare = useMemo(() => correctMoveSquare
|
|
1975
|
+
? createFeedbackSquareRenderer(correctMoveSquare)
|
|
1976
|
+
: undefined, [correctMoveSquare]);
|
|
1977
|
+
const promotionControlProps = clickPromotionDialog
|
|
1978
|
+
? {
|
|
1979
|
+
showPromotionDialog: true,
|
|
1980
|
+
promotionToSquare: clickPromotionToSquare,
|
|
1981
|
+
}
|
|
1982
|
+
: showPromotionDialogProp !== undefined ||
|
|
1983
|
+
promotionToSquareProp !== undefined
|
|
1984
|
+
? {
|
|
1985
|
+
showPromotionDialog: showPromotionDialogProp,
|
|
1986
|
+
promotionToSquare: promotionToSquareProp,
|
|
1987
|
+
}
|
|
1988
|
+
: {};
|
|
1989
|
+
return (jsx(Chessboard, Object.assign({ customDarkSquareStyle: customDarkSquareStyle, customLightSquareStyle: customLightSquareStyle, customSquareStyles: customSquareStyles, customSquare: customSquare, customBoardStyle: Object.assign(Object.assign({}, nonSelectableBoardStyle), customBoardStyle), position: position, arePiecesDraggable: arePiecesDraggable, autoPromoteToQueen: autoPromoteToQueen, isDraggablePiece: isDraggablePiece, onPromotionCheck: onPromotionCheck, onPieceDrop: onPieceDrop, onSquareClick: clickToMoveEnabled ? handleSquareClick : onSquareClick, onPromotionPieceSelect: clickToMoveEnabled ? handlePromotionPieceSelect : onPromotionPieceSelect, onPieceDragBegin: clickToMoveEnabled ? handlePieceDragBegin : onPieceDragBegin }, promotionControlProps, props)));
|
|
1770
1990
|
};
|
|
1771
1991
|
|
|
1772
1992
|
const emptyEngineEvaluation = () => ({
|
|
@@ -3502,6 +3722,37 @@ function useBoardRevision() {
|
|
|
3502
3722
|
return { revision, bumpRevision };
|
|
3503
3723
|
}
|
|
3504
3724
|
|
|
3725
|
+
/** Pause (ms) to show the correct-move check before advancing the line. */
|
|
3726
|
+
const CORRECT_MOVE_FEEDBACK_MS = 450;
|
|
3727
|
+
|
|
3728
|
+
function useCorrectMoveFeedback(delayMs = CORRECT_MOVE_FEEDBACK_MS) {
|
|
3729
|
+
const [correctMoveSquare, setCorrectMoveSquare] = useState(null);
|
|
3730
|
+
const timeoutRef = useRef(null);
|
|
3731
|
+
const clearCorrectMoveFeedback = useCallback(() => {
|
|
3732
|
+
if (timeoutRef.current !== null) {
|
|
3733
|
+
window.clearTimeout(timeoutRef.current);
|
|
3734
|
+
timeoutRef.current = null;
|
|
3735
|
+
}
|
|
3736
|
+
setCorrectMoveSquare(null);
|
|
3737
|
+
}, []);
|
|
3738
|
+
const showCorrectMove = useCallback((targetSquare, onComplete) => {
|
|
3739
|
+
clearCorrectMoveFeedback();
|
|
3740
|
+
setCorrectMoveSquare(targetSquare);
|
|
3741
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
3742
|
+
timeoutRef.current = null;
|
|
3743
|
+
setCorrectMoveSquare(null);
|
|
3744
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
|
|
3745
|
+
}, delayMs);
|
|
3746
|
+
}, [clearCorrectMoveFeedback, delayMs]);
|
|
3747
|
+
useEffect(() => clearCorrectMoveFeedback, [clearCorrectMoveFeedback]);
|
|
3748
|
+
return {
|
|
3749
|
+
correctMoveSquare,
|
|
3750
|
+
showCorrectMove,
|
|
3751
|
+
clearCorrectMoveFeedback,
|
|
3752
|
+
isShowingCorrectMove: correctMoveSquare !== null,
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3505
3756
|
/** Minimum eval loss (pawns) from the wrong move before showing a refutation. */
|
|
3506
3757
|
const REFUTATION_EVAL_GAP_PAWNS = 0.5;
|
|
3507
3758
|
const REFUTATION_EVAL_GAP_CP = REFUTATION_EVAL_GAP_PAWNS * 100;
|
|
@@ -3809,4 +4060,4 @@ function useMissBoard({ feedback, expectedUci, positionFen, answerArrowColor, au
|
|
|
3809
4060
|
};
|
|
3810
4061
|
}
|
|
3811
4062
|
|
|
3812
|
-
export { AnalysisBoard, AnalysisBoardCore, AnalysisBoardCoreView, AnalysisBoardLayout, AnalysisChessboardView, AnalysisEngineProvider, AnalysisErrorBoundary, AnalysisPosition, BOARD_THEMES, BOARD_THEME_IDS, ChessboardDnDProvider, ChessboardThemeContext, DEFAULT_ANALYSIS_LAYOUT, DEFAULT_BOARD_THEME, DEFAULT_STOCKFISH_SCRIPT_URL, DefaultAnalysisContainer, DefaultAnalysisSidebar, DefaultPlyNavigation, EngineEvaluationPanel, HighlightChessboard, MISS_MOVE_ANIMATION_MS, MISS_REFUTATION_MAX_WAIT_MS, MISS_REFUTATION_PAUSE_MS, MISS_WRONG_PAUSE_MS, PlyNavigation, REFUTATION_EVAL_GAP_CP, REFUTATION_EVAL_GAP_PAWNS, StockfishBrowserEngine, ThemeProvider, analysisBoardHighlightColors, analysisSidebarColors, applyUciMove, boardSquareHighlightColors, boardThemeFromLegacyUiTheme, createExpectedMoveDropHandler, createSidebarRowBandCounters, defaultRenderPlyNavigation, emptyEngineEvaluation, evaluateExpectedMoveDrop, fenAfterUci, formatEvaluation, formatPvPreview, getAnalysisModalStyles, getBoardThemeStyles, getCheckSquareFromChess, getLastMoveSquareStyles, getMissDisplay, getSidebarRowBackground, getStylesForTheme, isAnalyzableFen, isBoardThemeId, isEditableKeyboardTarget, lineEvalCpForGap, matchesExpectedUci, navButtonStyle, navRowStyle, normalizeEvalForWhite, normalizePvMoves, normalizeSubscriberOptions, parseUciInfoLine, parseUciMove, plyLabelStyle, palette as plyNavigationPalette, refutationEngineOptions, refutationEvalGapCp, refutationFromEvaluation, resolveStockfishScriptUrl, resolveStockfishWasmUrl, resolveStockfishWorkerUrl, scrubberInputStyle, splitWorkerLines, uciFromDrop, uciPvToSan, useAnalysisBoardModel, useAnalysisEngine, useAnalysisEngineContext, useBoardRevision, useChessboardTheme, useMissBoard, useMissRefutation, useMissSequence, usePositionKeyboardNav, useTheme };
|
|
4063
|
+
export { AnalysisBoard, AnalysisBoardCore, AnalysisBoardCoreView, AnalysisBoardLayout, AnalysisChessboardView, AnalysisEngineProvider, AnalysisErrorBoundary, AnalysisPosition, BOARD_THEMES, BOARD_THEME_IDS, CORRECT_MOVE_FEEDBACK_MS, ChessboardDnDProvider, ChessboardThemeContext, CorrectMoveCheckBadge, DEFAULT_ANALYSIS_LAYOUT, DEFAULT_BOARD_THEME, DEFAULT_STOCKFISH_SCRIPT_URL, DefaultAnalysisContainer, DefaultAnalysisSidebar, DefaultPlyNavigation, EngineEvaluationPanel, HighlightChessboard, MISS_MOVE_ANIMATION_MS, MISS_REFUTATION_MAX_WAIT_MS, MISS_REFUTATION_PAUSE_MS, MISS_WRONG_PAUSE_MS, PlyNavigation, REFUTATION_EVAL_GAP_CP, REFUTATION_EVAL_GAP_PAWNS, StockfishBrowserEngine, ThemeProvider, analysisBoardHighlightColors, analysisSidebarColors, applyUciMove, boardSquareHighlightColors, boardThemeFromLegacyUiTheme, createExpectedMoveDropHandler, createSidebarRowBandCounters, defaultRenderPlyNavigation, emptyEngineEvaluation, evaluateExpectedMoveDrop, fenAfterUci, formatEvaluation, formatPvPreview, getAnalysisModalStyles, getBoardThemeStyles, getCheckSquareFromChess, getLastMoveSquareStyles, getMissDisplay, getSidebarRowBackground, getStylesForTheme, isAnalyzableFen, isBoardThemeId, isEditableKeyboardTarget, lineEvalCpForGap, matchesExpectedUci, navButtonStyle, navRowStyle, normalizeEvalForWhite, normalizePvMoves, normalizeSubscriberOptions, parseUciInfoLine, parseUciMove, plyLabelStyle, palette as plyNavigationPalette, refutationEngineOptions, refutationEvalGapCp, refutationFromEvaluation, resolveStockfishScriptUrl, resolveStockfishWasmUrl, resolveStockfishWorkerUrl, scrubberInputStyle, splitWorkerLines, uciFromDrop, uciPvToSan, useAnalysisBoardModel, useAnalysisEngine, useAnalysisEngineContext, useBoardRevision, useChessboardTheme, useCorrectMoveFeedback, useMissBoard, useMissRefutation, useMissSequence, usePositionKeyboardNav, useTheme };
|
package/dist/index.js
CHANGED
|
@@ -1743,8 +1743,200 @@ const boardSquareHighlightColors = {
|
|
|
1743
1743
|
hint: 'rgba(119, 177, 212, 0.75)',
|
|
1744
1744
|
/** Muted red — softer than the in-check highlight. */
|
|
1745
1745
|
incorrect: 'rgba(140, 38, 38, 0.82)',
|
|
1746
|
+
selected: 'rgba(255, 255, 0, 0.45)',
|
|
1747
|
+
moveTarget: 'radial-gradient(circle, rgba(0, 0, 0, 0.18) 22%, transparent 22%)',
|
|
1748
|
+
captureTarget: 'radial-gradient(circle, rgba(0, 0, 0, 0.18) 72%, transparent 72%)',
|
|
1746
1749
|
};
|
|
1747
1750
|
|
|
1751
|
+
const badgeStyle = {
|
|
1752
|
+
position: 'absolute',
|
|
1753
|
+
right: '6%',
|
|
1754
|
+
bottom: '6%',
|
|
1755
|
+
width: '26%',
|
|
1756
|
+
height: '26%',
|
|
1757
|
+
minWidth: 14,
|
|
1758
|
+
minHeight: 14,
|
|
1759
|
+
maxWidth: 26,
|
|
1760
|
+
maxHeight: 26,
|
|
1761
|
+
borderRadius: '50%',
|
|
1762
|
+
backgroundColor: '#2e7d32',
|
|
1763
|
+
display: 'flex',
|
|
1764
|
+
alignItems: 'center',
|
|
1765
|
+
justifyContent: 'center',
|
|
1766
|
+
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.35)',
|
|
1767
|
+
pointerEvents: 'none',
|
|
1768
|
+
};
|
|
1769
|
+
/** Green circle with a white check, anchored to the bottom-right of a square
|
|
1770
|
+
* square (over the piece). */
|
|
1771
|
+
const CorrectMoveCheckBadge = () => (jsxRuntime.jsx("span", { "aria-hidden": true, style: badgeStyle, children: jsxRuntime.jsx("svg", { viewBox: "0 0 12 12", width: "62%", height: "62%", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M2.5 6.2 5 8.7 9.5 3.8", stroke: "#fff", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
|
|
1772
|
+
|
|
1773
|
+
const overlayStyle$1 = {
|
|
1774
|
+
position: 'absolute',
|
|
1775
|
+
inset: 0,
|
|
1776
|
+
pointerEvents: 'none',
|
|
1777
|
+
zIndex: 20,
|
|
1778
|
+
overflow: 'visible',
|
|
1779
|
+
};
|
|
1780
|
+
function createFeedbackSquareRenderer(correctMoveSquare) {
|
|
1781
|
+
return function FeedbackSquare({ children, style, square, ref }) {
|
|
1782
|
+
return (jsxRuntime.jsxs("div", { ref: ref, style: Object.assign(Object.assign({}, style), { position: 'relative', overflow: 'visible' }), children: [children, square === correctMoveSquare ? (jsxRuntime.jsx("div", { style: overlayStyle$1, children: jsxRuntime.jsx(CorrectMoveCheckBadge, {}) })) : null] }));
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
function defaultPromotionCheck(sourceSquare, targetSquare, piece) {
|
|
1787
|
+
return (((piece === 'wP' && sourceSquare[1] === '7' && targetSquare[1] === '8') ||
|
|
1788
|
+
(piece === 'bP' && sourceSquare[1] === '2' && targetSquare[1] === '1')) &&
|
|
1789
|
+
Math.abs(sourceSquare.charCodeAt(0) - targetSquare.charCodeAt(0)) <= 1);
|
|
1790
|
+
}
|
|
1791
|
+
function pieceAtSquare(fen, square) {
|
|
1792
|
+
const boardPiece = new chess_js.Chess(fen).get(square);
|
|
1793
|
+
if (!boardPiece) {
|
|
1794
|
+
return null;
|
|
1795
|
+
}
|
|
1796
|
+
const color = boardPiece.color === 'w' ? 'w' : 'b';
|
|
1797
|
+
const type = boardPiece.type.toUpperCase();
|
|
1798
|
+
return `${color}${type}`;
|
|
1799
|
+
}
|
|
1800
|
+
function getMoveOptionStyles(fen, fromSquare) {
|
|
1801
|
+
const chess = new chess_js.Chess(fen);
|
|
1802
|
+
const moves = chess.moves({ square: fromSquare, verbose: true });
|
|
1803
|
+
if (!moves.length) {
|
|
1804
|
+
return {
|
|
1805
|
+
[fromSquare]: { backgroundColor: boardSquareHighlightColors.selected },
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
const styles = {
|
|
1809
|
+
[fromSquare]: { backgroundColor: boardSquareHighlightColors.selected },
|
|
1810
|
+
};
|
|
1811
|
+
for (const move of moves) {
|
|
1812
|
+
styles[move.to] = {
|
|
1813
|
+
background: chess.get(move.to)
|
|
1814
|
+
? boardSquareHighlightColors.captureTarget
|
|
1815
|
+
: boardSquareHighlightColors.moveTarget,
|
|
1816
|
+
borderRadius: '50%',
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
return styles;
|
|
1820
|
+
}
|
|
1821
|
+
function useClickToMove({ enabled, position, arePiecesDraggable = true, autoPromoteToQueen = false, isDraggablePiece, onPromotionCheck = defaultPromotionCheck, onPieceDrop, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, }) {
|
|
1822
|
+
var _a;
|
|
1823
|
+
const [moveFrom, setMoveFrom] = react.useState(null);
|
|
1824
|
+
const [pendingPromotion, setPendingPromotion] = react.useState(null);
|
|
1825
|
+
const fen = typeof position === 'string' ? position : undefined;
|
|
1826
|
+
const clearSelection = react.useCallback(() => {
|
|
1827
|
+
setMoveFrom(null);
|
|
1828
|
+
setPendingPromotion(null);
|
|
1829
|
+
}, []);
|
|
1830
|
+
react.useEffect(() => {
|
|
1831
|
+
clearSelection();
|
|
1832
|
+
}, [fen, clearSelection]);
|
|
1833
|
+
const clickSquareStyles = react.useMemo(() => {
|
|
1834
|
+
if (!enabled || !moveFrom || !fen) {
|
|
1835
|
+
return {};
|
|
1836
|
+
}
|
|
1837
|
+
return getMoveOptionStyles(fen, moveFrom);
|
|
1838
|
+
}, [enabled, fen, moveFrom]);
|
|
1839
|
+
const canSelectPiece = react.useCallback((square, piece) => {
|
|
1840
|
+
if (!piece || !arePiecesDraggable || !onPieceDrop) {
|
|
1841
|
+
return false;
|
|
1842
|
+
}
|
|
1843
|
+
if (isDraggablePiece) {
|
|
1844
|
+
return isDraggablePiece({ piece, sourceSquare: square });
|
|
1845
|
+
}
|
|
1846
|
+
return true;
|
|
1847
|
+
}, [arePiecesDraggable, isDraggablePiece, onPieceDrop]);
|
|
1848
|
+
const tryCompleteMove = react.useCallback((from, to, piece) => {
|
|
1849
|
+
if (!onPieceDrop) {
|
|
1850
|
+
return false;
|
|
1851
|
+
}
|
|
1852
|
+
if (onPromotionCheck(from, to, piece)) {
|
|
1853
|
+
if (autoPromoteToQueen) {
|
|
1854
|
+
const promotedPiece = piece[0] === 'w' ? 'wQ' : 'bQ';
|
|
1855
|
+
const accepted = onPieceDrop(from, to, promotedPiece);
|
|
1856
|
+
clearSelection();
|
|
1857
|
+
return accepted;
|
|
1858
|
+
}
|
|
1859
|
+
setPendingPromotion({ from, to, piece });
|
|
1860
|
+
setMoveFrom(null);
|
|
1861
|
+
return true;
|
|
1862
|
+
}
|
|
1863
|
+
const accepted = onPieceDrop(from, to, piece);
|
|
1864
|
+
clearSelection();
|
|
1865
|
+
return accepted;
|
|
1866
|
+
}, [autoPromoteToQueen, clearSelection, onPieceDrop, onPromotionCheck]);
|
|
1867
|
+
const handleSquareClick = react.useCallback((square, piece) => {
|
|
1868
|
+
onSquareClick === null || onSquareClick === void 0 ? void 0 : onSquareClick(square, piece);
|
|
1869
|
+
if (!enabled || !onPieceDrop || !arePiecesDraggable || !fen) {
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
if (!moveFrom) {
|
|
1873
|
+
if (canSelectPiece(square, piece)) {
|
|
1874
|
+
setMoveFrom(square);
|
|
1875
|
+
}
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
if (square === moveFrom) {
|
|
1879
|
+
clearSelection();
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
const sourcePiece = pieceAtSquare(fen, moveFrom);
|
|
1883
|
+
if (!sourcePiece) {
|
|
1884
|
+
clearSelection();
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
const accepted = tryCompleteMove(moveFrom, square, sourcePiece);
|
|
1888
|
+
if (accepted) {
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (canSelectPiece(square, piece)) {
|
|
1892
|
+
setMoveFrom(square);
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
clearSelection();
|
|
1896
|
+
}, [
|
|
1897
|
+
arePiecesDraggable,
|
|
1898
|
+
canSelectPiece,
|
|
1899
|
+
clearSelection,
|
|
1900
|
+
enabled,
|
|
1901
|
+
fen,
|
|
1902
|
+
moveFrom,
|
|
1903
|
+
onPieceDrop,
|
|
1904
|
+
onSquareClick,
|
|
1905
|
+
tryCompleteMove,
|
|
1906
|
+
]);
|
|
1907
|
+
const handlePromotionPieceSelect = react.useCallback((piece, promoteFromSquare, promoteToSquare) => {
|
|
1908
|
+
if (pendingPromotion && piece) {
|
|
1909
|
+
const { from, to } = pendingPromotion;
|
|
1910
|
+
onPieceDrop === null || onPieceDrop === void 0 ? void 0 : onPieceDrop(from, to, piece);
|
|
1911
|
+
onPromotionPieceSelect === null || onPromotionPieceSelect === void 0 ? void 0 : onPromotionPieceSelect(piece, from, to);
|
|
1912
|
+
clearSelection();
|
|
1913
|
+
return false;
|
|
1914
|
+
}
|
|
1915
|
+
if (onPromotionPieceSelect) {
|
|
1916
|
+
return onPromotionPieceSelect(piece, promoteFromSquare, promoteToSquare);
|
|
1917
|
+
}
|
|
1918
|
+
return true;
|
|
1919
|
+
}, [clearSelection, onPieceDrop, onPromotionPieceSelect, pendingPromotion]);
|
|
1920
|
+
const handlePieceDragBegin = react.useCallback((piece, sourceSquare) => {
|
|
1921
|
+
clearSelection();
|
|
1922
|
+
onPieceDragBegin === null || onPieceDragBegin === void 0 ? void 0 : onPieceDragBegin(piece, sourceSquare);
|
|
1923
|
+
}, [clearSelection, onPieceDragBegin]);
|
|
1924
|
+
return {
|
|
1925
|
+
clickSquareStyles,
|
|
1926
|
+
handleSquareClick,
|
|
1927
|
+
handlePromotionPieceSelect,
|
|
1928
|
+
handlePieceDragBegin,
|
|
1929
|
+
showPromotionDialog: pendingPromotion !== null,
|
|
1930
|
+
promotionToSquare: (_a = pendingPromotion === null || pendingPromotion === void 0 ? void 0 : pendingPromotion.to) !== null && _a !== void 0 ? _a : null,
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
/** Prevent mobile long-press text selection and iOS callout menus on the board. */
|
|
1935
|
+
const nonSelectableBoardStyle = {
|
|
1936
|
+
userSelect: 'none',
|
|
1937
|
+
WebkitUserSelect: 'none',
|
|
1938
|
+
WebkitTouchCallout: 'none',
|
|
1939
|
+
};
|
|
1748
1940
|
const getCheckHighlighting = (checkSquare) => {
|
|
1749
1941
|
const styles = {};
|
|
1750
1942
|
styles[checkSquare] = { backgroundColor: boardSquareHighlightColors.check };
|
|
@@ -1763,12 +1955,40 @@ const getFeedbackHighlighting = (hintSquare, incorrectMoveSquare) => {
|
|
|
1763
1955
|
return styles;
|
|
1764
1956
|
};
|
|
1765
1957
|
const HighlightChessboard = (_a) => {
|
|
1766
|
-
var { checkSquare, hintSquare, incorrectMoveSquare, customSquareStyles: extraSquareStyles } = _a, props = __rest(_a, ["checkSquare", "hintSquare", "incorrectMoveSquare", "customSquareStyles"]);
|
|
1958
|
+
var { checkSquare, hintSquare, incorrectMoveSquare, correctMoveSquare = null, clickToMove, customSquareStyles: extraSquareStyles, customBoardStyle, onPieceDrop, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, showPromotionDialog: showPromotionDialogProp, promotionToSquare: promotionToSquareProp } = _a, props = __rest(_a, ["checkSquare", "hintSquare", "incorrectMoveSquare", "correctMoveSquare", "clickToMove", "customSquareStyles", "customBoardStyle", "onPieceDrop", "position", "arePiecesDraggable", "autoPromoteToQueen", "isDraggablePiece", "onPromotionCheck", "onSquareClick", "onPromotionPieceSelect", "onPieceDragBegin", "showPromotionDialog", "promotionToSquare"]);
|
|
1767
1959
|
const { customDarkSquareStyle, customLightSquareStyle } = useChessboardTheme();
|
|
1960
|
+
const clickToMoveEnabled = clickToMove !== false && typeof onPieceDrop === 'function';
|
|
1961
|
+
const { clickSquareStyles, handleSquareClick, handlePromotionPieceSelect, handlePieceDragBegin, showPromotionDialog: clickPromotionDialog, promotionToSquare: clickPromotionToSquare, } = useClickToMove({
|
|
1962
|
+
enabled: clickToMoveEnabled,
|
|
1963
|
+
position,
|
|
1964
|
+
arePiecesDraggable,
|
|
1965
|
+
autoPromoteToQueen,
|
|
1966
|
+
isDraggablePiece,
|
|
1967
|
+
onPromotionCheck,
|
|
1968
|
+
onPieceDrop,
|
|
1969
|
+
onSquareClick,
|
|
1970
|
+
onPromotionPieceSelect,
|
|
1971
|
+
onPieceDragBegin,
|
|
1972
|
+
});
|
|
1768
1973
|
const checkStyles = getCheckHighlighting(checkSquare);
|
|
1769
1974
|
const feedbackStyles = getFeedbackHighlighting(hintSquare, incorrectMoveSquare);
|
|
1770
|
-
const customSquareStyles = Object.assign(Object.assign(Object.assign({}, checkStyles), feedbackStyles), extraSquareStyles);
|
|
1771
|
-
|
|
1975
|
+
const customSquareStyles = Object.assign(Object.assign(Object.assign(Object.assign({}, clickSquareStyles), checkStyles), feedbackStyles), extraSquareStyles);
|
|
1976
|
+
const customSquare = react.useMemo(() => correctMoveSquare
|
|
1977
|
+
? createFeedbackSquareRenderer(correctMoveSquare)
|
|
1978
|
+
: undefined, [correctMoveSquare]);
|
|
1979
|
+
const promotionControlProps = clickPromotionDialog
|
|
1980
|
+
? {
|
|
1981
|
+
showPromotionDialog: true,
|
|
1982
|
+
promotionToSquare: clickPromotionToSquare,
|
|
1983
|
+
}
|
|
1984
|
+
: showPromotionDialogProp !== undefined ||
|
|
1985
|
+
promotionToSquareProp !== undefined
|
|
1986
|
+
? {
|
|
1987
|
+
showPromotionDialog: showPromotionDialogProp,
|
|
1988
|
+
promotionToSquare: promotionToSquareProp,
|
|
1989
|
+
}
|
|
1990
|
+
: {};
|
|
1991
|
+
return (jsxRuntime.jsx(reactChessboard.Chessboard, Object.assign({ customDarkSquareStyle: customDarkSquareStyle, customLightSquareStyle: customLightSquareStyle, customSquareStyles: customSquareStyles, customSquare: customSquare, customBoardStyle: Object.assign(Object.assign({}, nonSelectableBoardStyle), customBoardStyle), position: position, arePiecesDraggable: arePiecesDraggable, autoPromoteToQueen: autoPromoteToQueen, isDraggablePiece: isDraggablePiece, onPromotionCheck: onPromotionCheck, onPieceDrop: onPieceDrop, onSquareClick: clickToMoveEnabled ? handleSquareClick : onSquareClick, onPromotionPieceSelect: clickToMoveEnabled ? handlePromotionPieceSelect : onPromotionPieceSelect, onPieceDragBegin: clickToMoveEnabled ? handlePieceDragBegin : onPieceDragBegin }, promotionControlProps, props)));
|
|
1772
1992
|
};
|
|
1773
1993
|
|
|
1774
1994
|
const emptyEngineEvaluation = () => ({
|
|
@@ -3504,6 +3724,37 @@ function useBoardRevision() {
|
|
|
3504
3724
|
return { revision, bumpRevision };
|
|
3505
3725
|
}
|
|
3506
3726
|
|
|
3727
|
+
/** Pause (ms) to show the correct-move check before advancing the line. */
|
|
3728
|
+
const CORRECT_MOVE_FEEDBACK_MS = 450;
|
|
3729
|
+
|
|
3730
|
+
function useCorrectMoveFeedback(delayMs = CORRECT_MOVE_FEEDBACK_MS) {
|
|
3731
|
+
const [correctMoveSquare, setCorrectMoveSquare] = react.useState(null);
|
|
3732
|
+
const timeoutRef = react.useRef(null);
|
|
3733
|
+
const clearCorrectMoveFeedback = react.useCallback(() => {
|
|
3734
|
+
if (timeoutRef.current !== null) {
|
|
3735
|
+
window.clearTimeout(timeoutRef.current);
|
|
3736
|
+
timeoutRef.current = null;
|
|
3737
|
+
}
|
|
3738
|
+
setCorrectMoveSquare(null);
|
|
3739
|
+
}, []);
|
|
3740
|
+
const showCorrectMove = react.useCallback((targetSquare, onComplete) => {
|
|
3741
|
+
clearCorrectMoveFeedback();
|
|
3742
|
+
setCorrectMoveSquare(targetSquare);
|
|
3743
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
3744
|
+
timeoutRef.current = null;
|
|
3745
|
+
setCorrectMoveSquare(null);
|
|
3746
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
|
|
3747
|
+
}, delayMs);
|
|
3748
|
+
}, [clearCorrectMoveFeedback, delayMs]);
|
|
3749
|
+
react.useEffect(() => clearCorrectMoveFeedback, [clearCorrectMoveFeedback]);
|
|
3750
|
+
return {
|
|
3751
|
+
correctMoveSquare,
|
|
3752
|
+
showCorrectMove,
|
|
3753
|
+
clearCorrectMoveFeedback,
|
|
3754
|
+
isShowingCorrectMove: correctMoveSquare !== null,
|
|
3755
|
+
};
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3507
3758
|
/** Minimum eval loss (pawns) from the wrong move before showing a refutation. */
|
|
3508
3759
|
const REFUTATION_EVAL_GAP_PAWNS = 0.5;
|
|
3509
3760
|
const REFUTATION_EVAL_GAP_CP = REFUTATION_EVAL_GAP_PAWNS * 100;
|
|
@@ -3821,8 +4072,10 @@ exports.AnalysisErrorBoundary = AnalysisErrorBoundary;
|
|
|
3821
4072
|
exports.AnalysisPosition = AnalysisPosition;
|
|
3822
4073
|
exports.BOARD_THEMES = BOARD_THEMES;
|
|
3823
4074
|
exports.BOARD_THEME_IDS = BOARD_THEME_IDS;
|
|
4075
|
+
exports.CORRECT_MOVE_FEEDBACK_MS = CORRECT_MOVE_FEEDBACK_MS;
|
|
3824
4076
|
exports.ChessboardDnDProvider = ChessboardDnDProvider;
|
|
3825
4077
|
exports.ChessboardThemeContext = ChessboardThemeContext;
|
|
4078
|
+
exports.CorrectMoveCheckBadge = CorrectMoveCheckBadge;
|
|
3826
4079
|
exports.DEFAULT_ANALYSIS_LAYOUT = DEFAULT_ANALYSIS_LAYOUT;
|
|
3827
4080
|
exports.DEFAULT_BOARD_THEME = DEFAULT_BOARD_THEME;
|
|
3828
4081
|
exports.DEFAULT_STOCKFISH_SCRIPT_URL = DEFAULT_STOCKFISH_SCRIPT_URL;
|
|
@@ -3889,6 +4142,7 @@ exports.useAnalysisEngine = useAnalysisEngine;
|
|
|
3889
4142
|
exports.useAnalysisEngineContext = useAnalysisEngineContext;
|
|
3890
4143
|
exports.useBoardRevision = useBoardRevision;
|
|
3891
4144
|
exports.useChessboardTheme = useChessboardTheme;
|
|
4145
|
+
exports.useCorrectMoveFeedback = useCorrectMoveFeedback;
|
|
3892
4146
|
exports.useMissBoard = useMissBoard;
|
|
3893
4147
|
exports.useMissRefutation = useMissRefutation;
|
|
3894
4148
|
exports.useMissSequence = useMissSequence;
|