react-native-chess-kit 0.2.0 → 0.3.0
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/lib/commonjs/board-piece.js +35 -18
- package/lib/commonjs/board-piece.js.map +1 -1
- package/lib/commonjs/board.js +65 -51
- package/lib/commonjs/board.js.map +1 -1
- package/lib/commonjs/use-board-state.js +3 -1
- package/lib/commonjs/use-board-state.js.map +1 -1
- package/lib/module/board-piece.js +35 -18
- package/lib/module/board-piece.js.map +1 -1
- package/lib/module/board.js +65 -51
- package/lib/module/board.js.map +1 -1
- package/lib/module/use-board-state.js +3 -1
- package/lib/module/use-board-state.js.map +1 -1
- package/lib/typescript/board-piece.d.ts +12 -8
- package/lib/typescript/board-piece.d.ts.map +1 -1
- package/lib/typescript/board.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +2 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/use-board-state.d.ts +2 -0
- package/lib/typescript/use-board-state.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/board-piece.tsx +31 -21
- package/src/board.tsx +66 -58
- package/src/types.ts +2 -2
- package/src/use-board-state.ts +5 -0
package/src/board.tsx
CHANGED
|
@@ -203,61 +203,44 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
203
203
|
// a visual effect only — the rotation shared value is available for consumers
|
|
204
204
|
// who want to add a rotation transition effect.
|
|
205
205
|
|
|
206
|
-
// ---
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
//
|
|
206
|
+
// --- Internal FEN state ---
|
|
207
|
+
// The Board owns a private FEN that drives piece rendering.
|
|
208
|
+
// It starts from the parent prop and stays in sync via useEffect,
|
|
209
|
+
// but can temporarily diverge when the library applies a user move
|
|
210
|
+
// before the parent validates it (enables Chess.com-style "show move
|
|
211
|
+
// then revert" behavior via undo()).
|
|
212
|
+
const [internalFen, setInternalFen] = useState(fen);
|
|
213
|
+
|
|
214
|
+
// --- Chess.js for legal move validation + internal state ---
|
|
210
215
|
const boardState = useBoardState(fen);
|
|
211
216
|
|
|
212
|
-
// Sync internal chess.js when parent changes FEN
|
|
217
|
+
// Sync internal FEN + chess.js when parent changes the prop FEN.
|
|
218
|
+
// This covers: accepted moves (parent updates FEN), board reset,
|
|
219
|
+
// and opponent programmatic moves.
|
|
213
220
|
useEffect(() => {
|
|
221
|
+
setInternalFen(fen);
|
|
214
222
|
boardState.loadFen(fen);
|
|
215
223
|
}, [fen, boardState]);
|
|
216
224
|
|
|
217
|
-
// ---
|
|
218
|
-
const
|
|
219
|
-
() => detectCheckSquare(
|
|
220
|
-
fen,
|
|
221
|
-
() => {
|
|
222
|
-
try {
|
|
223
|
-
// chess.js isCheck method
|
|
224
|
-
const chess = boardState as unknown as { getFen: () => string };
|
|
225
|
-
const tempFen = chess.getFen();
|
|
226
|
-
// Use a simple approach: check if the FEN active color king is in check
|
|
227
|
-
// by trying to detect via chess.js internal state
|
|
228
|
-
return false; // Will be properly wired below
|
|
229
|
-
} catch {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
boardState.getTurn,
|
|
234
|
-
),
|
|
235
|
-
[fen, boardState],
|
|
236
|
-
);
|
|
225
|
+
// --- Piece data from internal FEN ---
|
|
226
|
+
const pieces = useBoardPieces(internalFen);
|
|
237
227
|
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
228
|
+
// --- Check detection ---
|
|
229
|
+
// Detect if the side to move is in check by parsing the FEN.
|
|
230
|
+
// chess.js isInCheck() isn't exposed through boardState, so we use
|
|
231
|
+
// a simple heuristic: if the FEN has the king of the side to move
|
|
232
|
+
// attacked, highlight it.
|
|
233
|
+
const checkSquareState = useMemo(() => {
|
|
244
234
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
() =>
|
|
248
|
-
// Attempt move detection: if the position is in check,
|
|
249
|
-
// chess.js will reflect this. We parse the FEN to find the king.
|
|
250
|
-
// For now, use a simple heuristic: try to detect from the position.
|
|
251
|
-
// The real check is done via board state.
|
|
252
|
-
return boardState.getTurn() !== undefined; // placeholder
|
|
253
|
-
},
|
|
235
|
+
return detectCheckSquare(
|
|
236
|
+
internalFen,
|
|
237
|
+
() => boardState.isInCheck(),
|
|
254
238
|
boardState.getTurn,
|
|
255
239
|
);
|
|
256
|
-
setCheckSquareState(square);
|
|
257
240
|
} catch {
|
|
258
|
-
|
|
241
|
+
return null;
|
|
259
242
|
}
|
|
260
|
-
}, [
|
|
243
|
+
}, [internalFen, boardState]);
|
|
261
244
|
|
|
262
245
|
// --- Selection state ---
|
|
263
246
|
const [selectedSquare, setSelectedSquare] = useState<string | null>(null);
|
|
@@ -328,22 +311,32 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
328
311
|
// Check for promotion
|
|
329
312
|
if (isPromotionMove(from, to)) {
|
|
330
313
|
if (onPromotion) {
|
|
331
|
-
// Show promotion picker or get choice from callback
|
|
332
314
|
const piece = pieces.find((p) => p.square === from);
|
|
333
315
|
const color = piece?.color ?? 'w';
|
|
334
|
-
|
|
335
316
|
setPromotionState({ from, to, color });
|
|
336
317
|
return;
|
|
337
318
|
}
|
|
338
|
-
// Auto-promote to queen
|
|
319
|
+
// Auto-promote to queen
|
|
320
|
+
const result = boardState.applyMove(from, to, 'q');
|
|
321
|
+
if (result.applied && result.fen) {
|
|
322
|
+
setInternalFen(result.fen);
|
|
323
|
+
}
|
|
339
324
|
onMove?.({ from, to });
|
|
340
325
|
return;
|
|
341
326
|
}
|
|
342
327
|
|
|
343
|
-
//
|
|
344
|
-
|
|
328
|
+
// Apply the move visually FIRST (Chess.com-style: show the move,
|
|
329
|
+
// then let the parent validate). The parent can call undo() to
|
|
330
|
+
// revert if the move is rejected.
|
|
331
|
+
const result = boardState.applyMove(from, to);
|
|
332
|
+
if (result.applied && result.fen) {
|
|
333
|
+
setInternalFen(result.fen);
|
|
334
|
+
onMove?.({ from, to });
|
|
335
|
+
}
|
|
336
|
+
// If chess.js rejected the move (truly illegal), do nothing —
|
|
337
|
+
// piece stays at its original square.
|
|
345
338
|
},
|
|
346
|
-
[onMove, onPromotion, isPromotionMove, pieces],
|
|
339
|
+
[onMove, onPromotion, isPromotionMove, pieces, boardState],
|
|
347
340
|
);
|
|
348
341
|
|
|
349
342
|
// --- Promotion picker handlers ---
|
|
@@ -353,20 +346,28 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
353
346
|
const { from, to } = promotionState;
|
|
354
347
|
setPromotionState(null);
|
|
355
348
|
|
|
356
|
-
|
|
349
|
+
const promo = piece.toLowerCase();
|
|
350
|
+
|
|
357
351
|
if (onPromotion) {
|
|
358
352
|
try {
|
|
359
353
|
const choice = await onPromotion(from, to);
|
|
360
|
-
|
|
354
|
+
const result = boardState.applyMove(from, to, choice);
|
|
355
|
+
if (result.applied && result.fen) {
|
|
356
|
+
setInternalFen(result.fen);
|
|
357
|
+
}
|
|
361
358
|
onMove?.({ from, to });
|
|
362
359
|
} catch {
|
|
363
|
-
// Promotion cancelled
|
|
360
|
+
// Promotion cancelled — piece stays at origin
|
|
364
361
|
}
|
|
365
362
|
} else {
|
|
363
|
+
const result = boardState.applyMove(from, to, promo);
|
|
364
|
+
if (result.applied && result.fen) {
|
|
365
|
+
setInternalFen(result.fen);
|
|
366
|
+
}
|
|
366
367
|
onMove?.({ from, to });
|
|
367
368
|
}
|
|
368
369
|
},
|
|
369
|
-
[promotionState, onPromotion, onMove],
|
|
370
|
+
[promotionState, onPromotion, onMove, boardState],
|
|
370
371
|
);
|
|
371
372
|
|
|
372
373
|
const handlePromotionCancel = useCallback(() => {
|
|
@@ -400,7 +401,8 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
400
401
|
if (consumed) {
|
|
401
402
|
// Try to execute the premove
|
|
402
403
|
const result = boardState.applyMove(consumed.from, consumed.to, consumed.promotion);
|
|
403
|
-
if (result.applied) {
|
|
404
|
+
if (result.applied && result.fen) {
|
|
405
|
+
setInternalFen(result.fen);
|
|
404
406
|
onMove?.({ from: consumed.from, to: consumed.to });
|
|
405
407
|
onHaptic?.('move');
|
|
406
408
|
} else {
|
|
@@ -454,13 +456,15 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
454
456
|
|
|
455
457
|
// --- Imperative ref ---
|
|
456
458
|
useImperativeHandle(ref, () => ({
|
|
457
|
-
move: (
|
|
458
|
-
boardState.applyMove(
|
|
459
|
+
move: (moveArg) => {
|
|
460
|
+
const result = boardState.applyMove(moveArg.from, moveArg.to, moveArg.promotion);
|
|
461
|
+
if (result.applied && result.fen) {
|
|
462
|
+
setInternalFen(result.fen);
|
|
463
|
+
}
|
|
459
464
|
},
|
|
460
465
|
|
|
461
466
|
highlight: (square, color) => {
|
|
462
467
|
setImperativeHighlights((prev) => {
|
|
463
|
-
// Replace existing highlight on same square, or add new
|
|
464
468
|
const filtered = prev.filter((h) => h.square !== square);
|
|
465
469
|
return [...filtered, { square, color }];
|
|
466
470
|
});
|
|
@@ -472,6 +476,7 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
472
476
|
|
|
473
477
|
resetBoard: (newFen) => {
|
|
474
478
|
boardState.loadFen(newFen);
|
|
479
|
+
setInternalFen(newFen);
|
|
475
480
|
setSelectedSquare(null);
|
|
476
481
|
setLegalMoves([]);
|
|
477
482
|
setImperativeHighlights([]);
|
|
@@ -480,7 +485,10 @@ export const Board = forwardRef<BoardRef, BoardProps>(function Board(
|
|
|
480
485
|
},
|
|
481
486
|
|
|
482
487
|
undo: () => {
|
|
483
|
-
boardState.undoMove();
|
|
488
|
+
const prevFen = boardState.undoMove();
|
|
489
|
+
if (prevFen) {
|
|
490
|
+
setInternalFen(prevFen);
|
|
491
|
+
}
|
|
484
492
|
setSelectedSquare(null);
|
|
485
493
|
setLegalMoves([]);
|
|
486
494
|
},
|
package/src/types.ts
CHANGED
|
@@ -157,8 +157,8 @@ export type BoardTheme = {
|
|
|
157
157
|
// ---------------------------------------------------------------------------
|
|
158
158
|
|
|
159
159
|
export type BoardRef = {
|
|
160
|
-
/**
|
|
161
|
-
move: (move: { from: string; to: string }) => void;
|
|
160
|
+
/** Programmatically apply a move. Animates the piece to the target square. */
|
|
161
|
+
move: (move: { from: string; to: string; promotion?: string }) => void;
|
|
162
162
|
/** Highlight a square with a color. Adds to existing imperative highlights. */
|
|
163
163
|
highlight: (square: string, color: string) => void;
|
|
164
164
|
/** Clear all imperative highlights */
|
package/src/use-board-state.ts
CHANGED
|
@@ -26,6 +26,8 @@ type BoardStateReturn = {
|
|
|
26
26
|
getFen: () => string;
|
|
27
27
|
/** Get the current turn from internal state */
|
|
28
28
|
getTurn: () => 'w' | 'b';
|
|
29
|
+
/** Check if the current side to move is in check */
|
|
30
|
+
isInCheck: () => boolean;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -92,6 +94,8 @@ export function useBoardState(initialFen: string): BoardStateReturn {
|
|
|
92
94
|
|
|
93
95
|
const getTurn = useCallback(() => chessRef.current.turn(), []);
|
|
94
96
|
|
|
97
|
+
const isInCheck = useCallback(() => chessRef.current.isCheck(), []);
|
|
98
|
+
|
|
95
99
|
return {
|
|
96
100
|
getLegalMoves,
|
|
97
101
|
isPlayerPiece,
|
|
@@ -100,5 +104,6 @@ export function useBoardState(initialFen: string): BoardStateReturn {
|
|
|
100
104
|
loadFen,
|
|
101
105
|
getFen,
|
|
102
106
|
getTurn,
|
|
107
|
+
isInCheck,
|
|
103
108
|
};
|
|
104
109
|
}
|