react-chess-core 0.1.3 → 0.1.4

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.
@@ -49,6 +49,7 @@ export declare class AnalysisPosition {
49
49
  from: string;
50
50
  to: string;
51
51
  } | null;
52
+ getLastMoveUci(): string | null;
52
53
  fen(): string;
53
54
  getCheckSquare(): string;
54
55
  }
@@ -25,6 +25,7 @@ export type AnalysisBoardModel = {
25
25
  from: string;
26
26
  to: string;
27
27
  } | null;
28
+ lastMoveUci: string | null;
28
29
  checkSquare: string | null;
29
30
  onSelectPly: (ply: number) => void;
30
31
  onSelectHistoryRow: (row: AnalysisHistoryRow) => void;
@@ -4,8 +4,12 @@ export interface HighlightChessboardProps {
4
4
  incorrectMoveSquare: string | null;
5
5
  /** Destination square of the last correct training move — shows a green check. */
6
6
  correctMoveSquare?: string | null;
7
+ /** UCI of the move that led to the current position (shows a last-move arrow). */
8
+ lastMoveUci?: string | null;
9
+ /** Override the default last-move arrow color. */
10
+ lastMoveArrowColor?: string;
7
11
  /** Enable click-to-move when `onPieceDrop` is provided. Defaults to true. */
8
12
  clickToMove?: boolean;
9
13
  [key: string]: any;
10
14
  }
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;
15
+ export declare const HighlightChessboard: ({ checkSquare, hintSquare, incorrectMoveSquare, correctMoveSquare, lastMoveUci, lastMoveArrowColor, clickToMove, customSquareStyles: extraSquareStyles, customArrows, customBoardStyle, onPieceDrop, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, showPromotionDialog: showPromotionDialogProp, promotionToSquare: promotionToSquareProp, ...props }: HighlightChessboardProps) => import("react").JSX.Element;
@@ -4,3 +4,4 @@ export * from './ChessboardDnDProvider';
4
4
  export * from './HighlightChessboard';
5
5
  export * from './CorrectMoveCheckBadge';
6
6
  export * from './boardSquareHighlightColors';
7
+ export * from './lastMoveArrow';
@@ -0,0 +1,8 @@
1
+ export type ChessboardArrow = [string, string, string];
2
+ /** Subtle green arrow visible on light and dark boards (Lichess-style). */
3
+ export declare const DEFAULT_LAST_MOVE_ARROW_COLOR = "rgba(155, 199, 0, 0.85)";
4
+ export declare const uciToArrow: (uci: string, color?: string) => ChessboardArrow;
5
+ export declare const lastMoveArrowFromUci: (uci: string | null | undefined, color?: string) => ChessboardArrow[];
6
+ /** UCI of the move that produced the position at {@link plyIndex}. */
7
+ export declare const lastMoveUciAtPly: (movesUci: readonly string[], plyIndex: number) => string | null;
8
+ export declare const mergeCustomArrowsWithLastMove: (customArrows: ChessboardArrow[] | undefined, lastMoveUci: string | null | undefined, lastMoveArrowColor?: string) => ChessboardArrow[];
@@ -7,6 +7,7 @@ export type MissSequenceState = {
7
7
  export type MissDisplay = {
8
8
  fen: string | null;
9
9
  arrows: [string, string, string][];
10
+ lastMoveUci: string | null;
10
11
  animating: boolean;
11
12
  };
12
13
  export declare const MISS_WRONG_PAUSE_MS = 450;
@@ -19,6 +19,7 @@ export declare function useMissBoard({ feedback, expectedUci, positionFen, answe
19
19
  customArrows: [string, string, string][];
20
20
  boardPosition: string;
21
21
  boardAnimating: boolean;
22
+ lastMoveUci: string | null;
22
23
  wrapDropHandler: (onDrop: (source: string, target: string, piece: string) => boolean, { enabled, dropFen, expectedMoveUci, }: {
23
24
  enabled: boolean;
24
25
  dropFen?: string;
package/dist/index.esm.js CHANGED
@@ -1781,6 +1781,31 @@ function createFeedbackSquareRenderer(correctMoveSquare) {
1781
1781
  };
1782
1782
  }
1783
1783
 
1784
+ /** Subtle green arrow visible on light and dark boards (Lichess-style). */
1785
+ const DEFAULT_LAST_MOVE_ARROW_COLOR = 'rgba(155, 199, 0, 0.85)';
1786
+ const uciToArrow = (uci, color = DEFAULT_LAST_MOVE_ARROW_COLOR) => [uci.slice(0, 2), uci.slice(2, 4), color];
1787
+ const lastMoveArrowFromUci = (uci, color = DEFAULT_LAST_MOVE_ARROW_COLOR) => {
1788
+ if (!uci || uci.length < 4) {
1789
+ return [];
1790
+ }
1791
+ return [uciToArrow(uci, color)];
1792
+ };
1793
+ /** UCI of the move that produced the position at {@link plyIndex}. */
1794
+ const lastMoveUciAtPly = (movesUci, plyIndex) => {
1795
+ var _a;
1796
+ if (plyIndex <= 0) {
1797
+ return null;
1798
+ }
1799
+ return (_a = movesUci[plyIndex - 1]) !== null && _a !== void 0 ? _a : null;
1800
+ };
1801
+ const mergeCustomArrowsWithLastMove = (customArrows, lastMoveUci, lastMoveArrowColor) => {
1802
+ const lastMove = lastMoveArrowFromUci(lastMoveUci, lastMoveArrowColor);
1803
+ if (!lastMove.length) {
1804
+ return customArrows !== null && customArrows !== void 0 ? customArrows : [];
1805
+ }
1806
+ return [...lastMove, ...(customArrows !== null && customArrows !== void 0 ? customArrows : [])];
1807
+ };
1808
+
1784
1809
  function defaultPromotionCheck(sourceSquare, targetSquare, piece) {
1785
1810
  return (((piece === 'wP' && sourceSquare[1] === '7' && targetSquare[1] === '8') ||
1786
1811
  (piece === 'bP' && sourceSquare[1] === '2' && targetSquare[1] === '1')) &&
@@ -1953,7 +1978,7 @@ const getFeedbackHighlighting = (hintSquare, incorrectMoveSquare) => {
1953
1978
  return styles;
1954
1979
  };
1955
1980
  const HighlightChessboard = (_a) => {
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"]);
1981
+ var { checkSquare, hintSquare, incorrectMoveSquare, correctMoveSquare = null, lastMoveUci = null, lastMoveArrowColor, clickToMove, customSquareStyles: extraSquareStyles, customArrows, customBoardStyle, onPieceDrop, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, showPromotionDialog: showPromotionDialogProp, promotionToSquare: promotionToSquareProp } = _a, props = __rest(_a, ["checkSquare", "hintSquare", "incorrectMoveSquare", "correctMoveSquare", "lastMoveUci", "lastMoveArrowColor", "clickToMove", "customSquareStyles", "customArrows", "customBoardStyle", "onPieceDrop", "position", "arePiecesDraggable", "autoPromoteToQueen", "isDraggablePiece", "onPromotionCheck", "onSquareClick", "onPromotionPieceSelect", "onPieceDragBegin", "showPromotionDialog", "promotionToSquare"]);
1957
1982
  const { customDarkSquareStyle, customLightSquareStyle } = useChessboardTheme();
1958
1983
  const clickToMoveEnabled = clickToMove !== false && typeof onPieceDrop === 'function';
1959
1984
  const { clickSquareStyles, handleSquareClick, handlePromotionPieceSelect, handlePieceDragBegin, showPromotionDialog: clickPromotionDialog, promotionToSquare: clickPromotionToSquare, } = useClickToMove({
@@ -1974,6 +1999,7 @@ const HighlightChessboard = (_a) => {
1974
1999
  const customSquare = useMemo(() => correctMoveSquare
1975
2000
  ? createFeedbackSquareRenderer(correctMoveSquare)
1976
2001
  : undefined, [correctMoveSquare]);
2002
+ const mergedCustomArrows = useMemo(() => mergeCustomArrowsWithLastMove(customArrows, lastMoveUci, lastMoveArrowColor), [customArrows, lastMoveArrowColor, lastMoveUci]);
1977
2003
  const promotionControlProps = clickPromotionDialog
1978
2004
  ? {
1979
2005
  showPromotionDialog: true,
@@ -1986,7 +2012,7 @@ const HighlightChessboard = (_a) => {
1986
2012
  promotionToSquare: promotionToSquareProp,
1987
2013
  }
1988
2014
  : {};
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)));
2015
+ 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, customArrows: mergedCustomArrows }, promotionControlProps, props)));
1990
2016
  };
1991
2017
 
1992
2018
  const emptyEngineEvaluation = () => ({
@@ -2914,9 +2940,7 @@ const PlyNavigation = ({ plyIndex, totalPly, canPrev, canNext, onGoFirst, onGoPr
2914
2940
  /** Draggable analysis board (no surrounding layout chrome). */
2915
2941
  const AnalysisChessboardView = ({ model }) => {
2916
2942
  var _a;
2917
- return (jsx(ChessboardDnDProvider, { children: jsx(HighlightChessboard, { checkSquare: (_a = model.checkSquare) !== null && _a !== void 0 ? _a : '', hintSquare: null, incorrectMoveSquare: null, position: model.fen, boardOrientation: model.boardOrientation, boardWidth: model.boardWidth, arePiecesDraggable: true, onPieceDrop: model.onPieceDrop, promotionDialogVariant: "modal", customSquareStyles: model.lastMove
2918
- ? getLastMoveSquareStyles(model.lastMove.from, model.lastMove.to, model.theme)
2919
- : {} }) }));
2943
+ return (jsx(ChessboardDnDProvider, { children: jsx(HighlightChessboard, { checkSquare: (_a = model.checkSquare) !== null && _a !== void 0 ? _a : '', hintSquare: null, incorrectMoveSquare: null, position: model.fen, boardOrientation: model.boardOrientation, boardWidth: model.boardWidth, arePiecesDraggable: true, onPieceDrop: model.onPieceDrop, promotionDialogVariant: "modal", lastMoveUci: model.lastMoveUci }) }));
2920
2944
  };
2921
2945
 
2922
2946
  class AnalysisPosition {
@@ -3172,22 +3196,26 @@ class AnalysisPosition {
3172
3196
  return true;
3173
3197
  }
3174
3198
  getLastMoveSquares() {
3175
- const navPly = this.getNavPly();
3176
- if (navPly === 0) {
3199
+ const uci = this.getLastMoveUci();
3200
+ if (!uci) {
3177
3201
  return null;
3178
3202
  }
3179
- let uci;
3180
- if (this.variation && this.variationCursor > 0) {
3181
- uci = this.variation.moves[this.variationCursor - 1];
3182
- }
3183
- else {
3184
- uci = this.solutionMoves[this.mainPly - 1];
3185
- }
3186
3203
  return {
3187
3204
  from: uci.slice(0, 2),
3188
3205
  to: uci.slice(2, 4),
3189
3206
  };
3190
3207
  }
3208
+ getLastMoveUci() {
3209
+ var _a;
3210
+ const navPly = this.getNavPly();
3211
+ if (navPly === 0) {
3212
+ return null;
3213
+ }
3214
+ if (this.variation && this.variationCursor > 0) {
3215
+ return this.variation.moves[this.variationCursor - 1];
3216
+ }
3217
+ return (_a = this.solutionMoves[this.mainPly - 1]) !== null && _a !== void 0 ? _a : null;
3218
+ }
3191
3219
  fen() {
3192
3220
  return this.chess.fen();
3193
3221
  }
@@ -3232,6 +3260,7 @@ const useAnalysisBoardModel = ({ analysisContext, onClose, theme, boardWidth, en
3232
3260
  engineEvaluation,
3233
3261
  engineEnabled,
3234
3262
  lastMove: analysisPosition.getLastMoveSquares(),
3263
+ lastMoveUci: analysisPosition.getLastMoveUci(),
3235
3264
  checkSquare: analysisPosition.getCheckSquare(),
3236
3265
  onSelectPly: (ply) => {
3237
3266
  analysisPosition.goToNavPly(ply);
@@ -3846,6 +3875,7 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3846
3875
  return {
3847
3876
  fen: null,
3848
3877
  arrows: [],
3878
+ lastMoveUci: null,
3849
3879
  animating: false,
3850
3880
  };
3851
3881
  }
@@ -3856,6 +3886,7 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3856
3886
  return {
3857
3887
  fen: fenAfterWrong !== null && fenAfterWrong !== void 0 ? fenAfterWrong : setupFen,
3858
3888
  arrows: [],
3889
+ lastMoveUci: attemptedUci,
3859
3890
  animating: false,
3860
3891
  };
3861
3892
  case 'refutation': {
@@ -3865,6 +3896,7 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3865
3896
  return {
3866
3897
  fen: (_a = fenAfterRefutation !== null && fenAfterRefutation !== void 0 ? fenAfterRefutation : fenAfterWrong) !== null && _a !== void 0 ? _a : setupFen,
3867
3898
  arrows: [],
3899
+ lastMoveUci: refutationUci,
3868
3900
  animating: Boolean(fenAfterRefutation),
3869
3901
  };
3870
3902
  }
@@ -3872,18 +3904,21 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3872
3904
  return {
3873
3905
  fen: setupFen,
3874
3906
  arrows: [],
3907
+ lastMoveUci: null,
3875
3908
  animating: false,
3876
3909
  };
3877
3910
  case 'answer':
3878
3911
  return {
3879
3912
  fen: setupFen,
3880
3913
  arrows: expectedMoveArrow(expectedUci, answerArrowColor),
3914
+ lastMoveUci: null,
3881
3915
  animating: false,
3882
3916
  };
3883
3917
  default:
3884
3918
  return {
3885
3919
  fen: setupFen,
3886
3920
  arrows: [],
3921
+ lastMoveUci: null,
3887
3922
  animating: false,
3888
3923
  };
3889
3924
  }
@@ -4056,8 +4091,9 @@ function useMissBoard({ feedback, expectedUci, positionFen, answerArrowColor, au
4056
4091
  customArrows,
4057
4092
  boardPosition,
4058
4093
  boardAnimating: missSequence.display.animating,
4094
+ lastMoveUci: missSequence.display.lastMoveUci,
4059
4095
  wrapDropHandler,
4060
4096
  };
4061
4097
  }
4062
4098
 
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 };
4099
+ 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_LAST_MOVE_ARROW_COLOR, 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, lastMoveArrowFromUci, lastMoveUciAtPly, lineEvalCpForGap, matchesExpectedUci, mergeCustomArrowsWithLastMove, navButtonStyle, navRowStyle, normalizeEvalForWhite, normalizePvMoves, normalizeSubscriberOptions, parseUciInfoLine, parseUciMove, plyLabelStyle, palette as plyNavigationPalette, refutationEngineOptions, refutationEvalGapCp, refutationFromEvaluation, resolveStockfishScriptUrl, resolveStockfishWasmUrl, resolveStockfishWorkerUrl, scrubberInputStyle, splitWorkerLines, uciFromDrop, uciPvToSan, uciToArrow, useAnalysisBoardModel, useAnalysisEngine, useAnalysisEngineContext, useBoardRevision, useChessboardTheme, useCorrectMoveFeedback, useMissBoard, useMissRefutation, useMissSequence, usePositionKeyboardNav, useTheme };
package/dist/index.js CHANGED
@@ -1783,6 +1783,31 @@ function createFeedbackSquareRenderer(correctMoveSquare) {
1783
1783
  };
1784
1784
  }
1785
1785
 
1786
+ /** Subtle green arrow visible on light and dark boards (Lichess-style). */
1787
+ const DEFAULT_LAST_MOVE_ARROW_COLOR = 'rgba(155, 199, 0, 0.85)';
1788
+ const uciToArrow = (uci, color = DEFAULT_LAST_MOVE_ARROW_COLOR) => [uci.slice(0, 2), uci.slice(2, 4), color];
1789
+ const lastMoveArrowFromUci = (uci, color = DEFAULT_LAST_MOVE_ARROW_COLOR) => {
1790
+ if (!uci || uci.length < 4) {
1791
+ return [];
1792
+ }
1793
+ return [uciToArrow(uci, color)];
1794
+ };
1795
+ /** UCI of the move that produced the position at {@link plyIndex}. */
1796
+ const lastMoveUciAtPly = (movesUci, plyIndex) => {
1797
+ var _a;
1798
+ if (plyIndex <= 0) {
1799
+ return null;
1800
+ }
1801
+ return (_a = movesUci[plyIndex - 1]) !== null && _a !== void 0 ? _a : null;
1802
+ };
1803
+ const mergeCustomArrowsWithLastMove = (customArrows, lastMoveUci, lastMoveArrowColor) => {
1804
+ const lastMove = lastMoveArrowFromUci(lastMoveUci, lastMoveArrowColor);
1805
+ if (!lastMove.length) {
1806
+ return customArrows !== null && customArrows !== void 0 ? customArrows : [];
1807
+ }
1808
+ return [...lastMove, ...(customArrows !== null && customArrows !== void 0 ? customArrows : [])];
1809
+ };
1810
+
1786
1811
  function defaultPromotionCheck(sourceSquare, targetSquare, piece) {
1787
1812
  return (((piece === 'wP' && sourceSquare[1] === '7' && targetSquare[1] === '8') ||
1788
1813
  (piece === 'bP' && sourceSquare[1] === '2' && targetSquare[1] === '1')) &&
@@ -1955,7 +1980,7 @@ const getFeedbackHighlighting = (hintSquare, incorrectMoveSquare) => {
1955
1980
  return styles;
1956
1981
  };
1957
1982
  const HighlightChessboard = (_a) => {
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"]);
1983
+ var { checkSquare, hintSquare, incorrectMoveSquare, correctMoveSquare = null, lastMoveUci = null, lastMoveArrowColor, clickToMove, customSquareStyles: extraSquareStyles, customArrows, customBoardStyle, onPieceDrop, position, arePiecesDraggable, autoPromoteToQueen, isDraggablePiece, onPromotionCheck, onSquareClick, onPromotionPieceSelect, onPieceDragBegin, showPromotionDialog: showPromotionDialogProp, promotionToSquare: promotionToSquareProp } = _a, props = __rest(_a, ["checkSquare", "hintSquare", "incorrectMoveSquare", "correctMoveSquare", "lastMoveUci", "lastMoveArrowColor", "clickToMove", "customSquareStyles", "customArrows", "customBoardStyle", "onPieceDrop", "position", "arePiecesDraggable", "autoPromoteToQueen", "isDraggablePiece", "onPromotionCheck", "onSquareClick", "onPromotionPieceSelect", "onPieceDragBegin", "showPromotionDialog", "promotionToSquare"]);
1959
1984
  const { customDarkSquareStyle, customLightSquareStyle } = useChessboardTheme();
1960
1985
  const clickToMoveEnabled = clickToMove !== false && typeof onPieceDrop === 'function';
1961
1986
  const { clickSquareStyles, handleSquareClick, handlePromotionPieceSelect, handlePieceDragBegin, showPromotionDialog: clickPromotionDialog, promotionToSquare: clickPromotionToSquare, } = useClickToMove({
@@ -1976,6 +2001,7 @@ const HighlightChessboard = (_a) => {
1976
2001
  const customSquare = react.useMemo(() => correctMoveSquare
1977
2002
  ? createFeedbackSquareRenderer(correctMoveSquare)
1978
2003
  : undefined, [correctMoveSquare]);
2004
+ const mergedCustomArrows = react.useMemo(() => mergeCustomArrowsWithLastMove(customArrows, lastMoveUci, lastMoveArrowColor), [customArrows, lastMoveArrowColor, lastMoveUci]);
1979
2005
  const promotionControlProps = clickPromotionDialog
1980
2006
  ? {
1981
2007
  showPromotionDialog: true,
@@ -1988,7 +2014,7 @@ const HighlightChessboard = (_a) => {
1988
2014
  promotionToSquare: promotionToSquareProp,
1989
2015
  }
1990
2016
  : {};
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)));
2017
+ 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, customArrows: mergedCustomArrows }, promotionControlProps, props)));
1992
2018
  };
1993
2019
 
1994
2020
  const emptyEngineEvaluation = () => ({
@@ -2916,9 +2942,7 @@ const PlyNavigation = ({ plyIndex, totalPly, canPrev, canNext, onGoFirst, onGoPr
2916
2942
  /** Draggable analysis board (no surrounding layout chrome). */
2917
2943
  const AnalysisChessboardView = ({ model }) => {
2918
2944
  var _a;
2919
- return (jsxRuntime.jsx(ChessboardDnDProvider, { children: jsxRuntime.jsx(HighlightChessboard, { checkSquare: (_a = model.checkSquare) !== null && _a !== void 0 ? _a : '', hintSquare: null, incorrectMoveSquare: null, position: model.fen, boardOrientation: model.boardOrientation, boardWidth: model.boardWidth, arePiecesDraggable: true, onPieceDrop: model.onPieceDrop, promotionDialogVariant: "modal", customSquareStyles: model.lastMove
2920
- ? getLastMoveSquareStyles(model.lastMove.from, model.lastMove.to, model.theme)
2921
- : {} }) }));
2945
+ return (jsxRuntime.jsx(ChessboardDnDProvider, { children: jsxRuntime.jsx(HighlightChessboard, { checkSquare: (_a = model.checkSquare) !== null && _a !== void 0 ? _a : '', hintSquare: null, incorrectMoveSquare: null, position: model.fen, boardOrientation: model.boardOrientation, boardWidth: model.boardWidth, arePiecesDraggable: true, onPieceDrop: model.onPieceDrop, promotionDialogVariant: "modal", lastMoveUci: model.lastMoveUci }) }));
2922
2946
  };
2923
2947
 
2924
2948
  class AnalysisPosition {
@@ -3174,22 +3198,26 @@ class AnalysisPosition {
3174
3198
  return true;
3175
3199
  }
3176
3200
  getLastMoveSquares() {
3177
- const navPly = this.getNavPly();
3178
- if (navPly === 0) {
3201
+ const uci = this.getLastMoveUci();
3202
+ if (!uci) {
3179
3203
  return null;
3180
3204
  }
3181
- let uci;
3182
- if (this.variation && this.variationCursor > 0) {
3183
- uci = this.variation.moves[this.variationCursor - 1];
3184
- }
3185
- else {
3186
- uci = this.solutionMoves[this.mainPly - 1];
3187
- }
3188
3205
  return {
3189
3206
  from: uci.slice(0, 2),
3190
3207
  to: uci.slice(2, 4),
3191
3208
  };
3192
3209
  }
3210
+ getLastMoveUci() {
3211
+ var _a;
3212
+ const navPly = this.getNavPly();
3213
+ if (navPly === 0) {
3214
+ return null;
3215
+ }
3216
+ if (this.variation && this.variationCursor > 0) {
3217
+ return this.variation.moves[this.variationCursor - 1];
3218
+ }
3219
+ return (_a = this.solutionMoves[this.mainPly - 1]) !== null && _a !== void 0 ? _a : null;
3220
+ }
3193
3221
  fen() {
3194
3222
  return this.chess.fen();
3195
3223
  }
@@ -3234,6 +3262,7 @@ const useAnalysisBoardModel = ({ analysisContext, onClose, theme, boardWidth, en
3234
3262
  engineEvaluation,
3235
3263
  engineEnabled,
3236
3264
  lastMove: analysisPosition.getLastMoveSquares(),
3265
+ lastMoveUci: analysisPosition.getLastMoveUci(),
3237
3266
  checkSquare: analysisPosition.getCheckSquare(),
3238
3267
  onSelectPly: (ply) => {
3239
3268
  analysisPosition.goToNavPly(ply);
@@ -3848,6 +3877,7 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3848
3877
  return {
3849
3878
  fen: null,
3850
3879
  arrows: [],
3880
+ lastMoveUci: null,
3851
3881
  animating: false,
3852
3882
  };
3853
3883
  }
@@ -3858,6 +3888,7 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3858
3888
  return {
3859
3889
  fen: fenAfterWrong !== null && fenAfterWrong !== void 0 ? fenAfterWrong : setupFen,
3860
3890
  arrows: [],
3891
+ lastMoveUci: attemptedUci,
3861
3892
  animating: false,
3862
3893
  };
3863
3894
  case 'refutation': {
@@ -3867,6 +3898,7 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3867
3898
  return {
3868
3899
  fen: (_a = fenAfterRefutation !== null && fenAfterRefutation !== void 0 ? fenAfterRefutation : fenAfterWrong) !== null && _a !== void 0 ? _a : setupFen,
3869
3900
  arrows: [],
3901
+ lastMoveUci: refutationUci,
3870
3902
  animating: Boolean(fenAfterRefutation),
3871
3903
  };
3872
3904
  }
@@ -3874,18 +3906,21 @@ function getMissDisplay(sequence, expectedUci, refutationUci, answerArrowColor)
3874
3906
  return {
3875
3907
  fen: setupFen,
3876
3908
  arrows: [],
3909
+ lastMoveUci: null,
3877
3910
  animating: false,
3878
3911
  };
3879
3912
  case 'answer':
3880
3913
  return {
3881
3914
  fen: setupFen,
3882
3915
  arrows: expectedMoveArrow(expectedUci, answerArrowColor),
3916
+ lastMoveUci: null,
3883
3917
  animating: false,
3884
3918
  };
3885
3919
  default:
3886
3920
  return {
3887
3921
  fen: setupFen,
3888
3922
  arrows: [],
3923
+ lastMoveUci: null,
3889
3924
  animating: false,
3890
3925
  };
3891
3926
  }
@@ -4058,6 +4093,7 @@ function useMissBoard({ feedback, expectedUci, positionFen, answerArrowColor, au
4058
4093
  customArrows,
4059
4094
  boardPosition,
4060
4095
  boardAnimating: missSequence.display.animating,
4096
+ lastMoveUci: missSequence.display.lastMoveUci,
4061
4097
  wrapDropHandler,
4062
4098
  };
4063
4099
  }
@@ -4078,6 +4114,7 @@ exports.ChessboardThemeContext = ChessboardThemeContext;
4078
4114
  exports.CorrectMoveCheckBadge = CorrectMoveCheckBadge;
4079
4115
  exports.DEFAULT_ANALYSIS_LAYOUT = DEFAULT_ANALYSIS_LAYOUT;
4080
4116
  exports.DEFAULT_BOARD_THEME = DEFAULT_BOARD_THEME;
4117
+ exports.DEFAULT_LAST_MOVE_ARROW_COLOR = DEFAULT_LAST_MOVE_ARROW_COLOR;
4081
4118
  exports.DEFAULT_STOCKFISH_SCRIPT_URL = DEFAULT_STOCKFISH_SCRIPT_URL;
4082
4119
  exports.DefaultAnalysisContainer = DefaultAnalysisContainer;
4083
4120
  exports.DefaultAnalysisSidebar = DefaultAnalysisSidebar;
@@ -4116,8 +4153,11 @@ exports.getStylesForTheme = getStylesForTheme;
4116
4153
  exports.isAnalyzableFen = isAnalyzableFen;
4117
4154
  exports.isBoardThemeId = isBoardThemeId;
4118
4155
  exports.isEditableKeyboardTarget = isEditableKeyboardTarget;
4156
+ exports.lastMoveArrowFromUci = lastMoveArrowFromUci;
4157
+ exports.lastMoveUciAtPly = lastMoveUciAtPly;
4119
4158
  exports.lineEvalCpForGap = lineEvalCpForGap;
4120
4159
  exports.matchesExpectedUci = matchesExpectedUci;
4160
+ exports.mergeCustomArrowsWithLastMove = mergeCustomArrowsWithLastMove;
4121
4161
  exports.navButtonStyle = navButtonStyle;
4122
4162
  exports.navRowStyle = navRowStyle;
4123
4163
  exports.normalizeEvalForWhite = normalizeEvalForWhite;
@@ -4137,6 +4177,7 @@ exports.scrubberInputStyle = scrubberInputStyle;
4137
4177
  exports.splitWorkerLines = splitWorkerLines;
4138
4178
  exports.uciFromDrop = uciFromDrop;
4139
4179
  exports.uciPvToSan = uciPvToSan;
4180
+ exports.uciToArrow = uciToArrow;
4140
4181
  exports.useAnalysisBoardModel = useAnalysisBoardModel;
4141
4182
  exports.useAnalysisEngine = useAnalysisEngine;
4142
4183
  exports.useAnalysisEngineContext = useAnalysisEngineContext;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-chess-core",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Shared React chessboard theme, highlights, browser Stockfish, and analysis board",
5
5
  "license": "MIT",
6
6
  "author": "Robert Blackwell",