gigaboard 1.4.1 → 1.5.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.
Files changed (33) hide show
  1. package/dist/{board-model-BMtkLQvL.d.cts → board-model-BIVxSCWC.d.cts} +73 -2
  2. package/dist/{board-model-BMtkLQvL.d.ts → board-model-BIVxSCWC.d.ts} +73 -2
  3. package/dist/{chunk-EJUGH25P.js → chunk-53XWDREK.js} +4 -3
  4. package/dist/chunk-53XWDREK.js.map +1 -0
  5. package/dist/{chunk-SO6AOEFF.js → chunk-6F3FZAG7.js} +116 -18
  6. package/dist/chunk-6F3FZAG7.js.map +1 -0
  7. package/dist/{chunk-UBD62NOI.cjs → chunk-FUP2LDMO.cjs} +121 -16
  8. package/dist/chunk-FUP2LDMO.cjs.map +1 -0
  9. package/dist/{chunk-D45G3J5E.cjs → chunk-N4WTPXFC.cjs} +4 -2
  10. package/dist/chunk-N4WTPXFC.cjs.map +1 -0
  11. package/dist/core.cjs +72 -44
  12. package/dist/core.d.cts +17 -12
  13. package/dist/core.d.ts +17 -12
  14. package/dist/core.js +1 -1
  15. package/dist/{gigachess-C08uAeFw.d.ts → gigachess-DN-ncmAU.d.ts} +1 -1
  16. package/dist/{gigachess-Dr5gCN68.d.cts → gigachess-XpNGPM2m.d.cts} +1 -1
  17. package/dist/index.cjs +879 -259
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +55 -8
  20. package/dist/index.d.ts +55 -8
  21. package/dist/index.js +786 -198
  22. package/dist/index.js.map +1 -1
  23. package/dist/server.cjs +11 -11
  24. package/dist/server.d.cts +2 -2
  25. package/dist/server.d.ts +2 -2
  26. package/dist/server.js +1 -1
  27. package/dist/{types-CcOgT5ov.d.ts → types-4W8ULiE2.d.ts} +1 -1
  28. package/dist/{types-DfqRXtbD.d.cts → types-Dox7oLof.d.cts} +1 -1
  29. package/package.json +2 -2
  30. package/dist/chunk-D45G3J5E.cjs.map +0 -1
  31. package/dist/chunk-EJUGH25P.js.map +0 -1
  32. package/dist/chunk-SO6AOEFF.js.map +0 -1
  33. package/dist/chunk-UBD62NOI.cjs.map +0 -1
@@ -42,6 +42,42 @@ declare function unpackMove(move: PackedMove | number): {
42
42
  to: SquareIndex;
43
43
  promo: number;
44
44
  };
45
+ /**
46
+ * The null-move (pass) sentinel — a `u16` word of `0xffff` in the 16-bit
47
+ * Move2 wire format.
48
+ *
49
+ * @remarks
50
+ * CBH `moves2` streams store a pass turn as an ordinary move word, so the
51
+ * word itself has to be distinguishable from a real move. `0xffff` is
52
+ * unambiguous: a packed word only reaches it via `from = to = 63` with
53
+ * promotion code 15, and Move2 promotion codes are `0..4`. No legal word
54
+ * collides.
55
+ *
56
+ * Spellings: UCI is `"0000"`; PGN/SAN is `"--"` and also accepts `"Z0"`.
57
+ * See {@link NULL_MOVE_UCI}, {@link NULL_MOVE_SAN}, {@link isNullMoveUci}
58
+ * and {@link isNullMoveSan}.
59
+ *
60
+ * Gigaboard **tolerates** a pass but never originates one: no gesture,
61
+ * selection, or drag can produce this word. See {@link isNullMove}.
62
+ */
63
+ declare const NULL_MOVE_WORD: PackedMove;
64
+ /** UCI spelling of a pass. */
65
+ declare const NULL_MOVE_UCI = "0000";
66
+ /** PGN/SAN spelling of a pass. */
67
+ declare const NULL_MOVE_SAN = "--";
68
+ /** Accepted input spellings of a pass: the SAN form plus the legacy `Z0`. */
69
+ declare const NULL_MOVE_SANS: readonly string[];
70
+ /**
71
+ * `true` when `word` is the null-move (pass) sentinel.
72
+ *
73
+ * Callers gate on this **before** unpacking — `unpackMove` reads the
74
+ * sentinel as `from = to = 63, promo = 15`, which is meaningless.
75
+ */
76
+ declare function isNullMove(word: PackedMove | number): boolean;
77
+ /** `true` when `text` is the UCI spelling of a pass (`"0000"`). */
78
+ declare function isNullMoveUci(text: string): boolean;
79
+ /** `true` when `text` is an accepted PGN/SAN spelling of a pass (`"--"` or `"Z0"`). */
80
+ declare function isNullMoveSan(text: string): boolean;
45
81
  /**
46
82
  * 64-bit Zobrist key represented as two 32-bit unsigned integers (zero-BigInt).
47
83
  */
@@ -331,8 +367,29 @@ interface EngineAdapter {
331
367
  * any square).
332
368
  */
333
369
  makeMove(from: SquareIndex, to: SquareIndex, promotion?: PieceType): PackedMove | null;
334
- /** Undo the most recent move. Returns the undone move, or `null` if none. */
370
+ /**
371
+ * Undo the most recent move. Returns the undone move, or `null` if none.
372
+ *
373
+ * A pass undoes to {@link NULL_MOVE_WORD}, mirroring what
374
+ * {@link EngineAdapter.makeNullMove} produced.
375
+ */
335
376
  undo(): PackedMove | null;
377
+ /**
378
+ * Play a pass (null move): the position's pieces are untouched, the
379
+ * side to move flips, the en-passant square clears, and both clocks
380
+ * advance — a pass completes a **full** move, whoever passed, so the
381
+ * fullmove number always increments.
382
+ *
383
+ * Returns {@link NULL_MOVE_WORD} on success, or `null` when the pass is
384
+ * refused. A pass is legal **iff** the side to move is not in check: a
385
+ * pass answers no check, so a checked side cannot pass.
386
+ *
387
+ * @remarks
388
+ * Optional: an adapter that cannot represent a pass simply omits it and
389
+ * every pass attempt is refused. This is the tolerance path only —
390
+ * gigaboard never originates a pass from a user gesture.
391
+ */
392
+ makeNullMove?(): PackedMove | null;
336
393
  /**
337
394
  * Legal moves from a single origin square (`from` provided) or all legal
338
395
  * moves (`from` omitted). The returned array is fresh on every call; the
@@ -614,6 +671,20 @@ interface BoardModel {
614
671
  selectSquare(index: SquareIndex | null): void;
615
672
  /** Attempt a move. Returns the packed move on success, `null` on illegal. */
616
673
  tryMove(from: SquareIndex, to: SquareIndex, promotion?: PieceType): PackedMove | null;
674
+ /**
675
+ * Play a pass (null move) as a **tolerance** path: returns
676
+ * {@link NULL_MOVE_WORD} on success, or `null` when the adapter has no
677
+ * pass capability or the pass is illegal (the side to move is in check —
678
+ * a pass answers no check).
679
+ *
680
+ * @remarks
681
+ * A pass flips the side to move and advances both clocks, but it moves no
682
+ * piece, so it schedules **no** animation and leaves the board bytes
683
+ * untouched. No user gesture, selection, or drag routes here — this is
684
+ * only for feeding an externally supplied pass (e.g. a CBH `moves2`
685
+ * stream) into the board.
686
+ */
687
+ pass(): PackedMove | null;
617
688
  /** Undo the most recent move. */
618
689
  undo(): PackedMove | null;
619
690
  /** Redo the most recently undone move. */
@@ -700,4 +771,4 @@ interface BoardModel {
700
771
  */
701
772
  declare function createBoardModel(engine: EngineAdapter, options?: BoardModelOptions): BoardModel;
702
773
 
703
- export { createBoardStore as $, type AnimDescriptor as A, BOARD_CELL_BB as B, type CancelEvent as C, Color as D, type EngineAdapter as E, type DragController as F, type DragControllerOptions as G, type DragEvent as H, type DragMoveEvent as I, type DragStartEvent as J, type DragState as K, type DropEvent as L, type LegalMoveEntry as M, type LegalMoveIndex as N, type LegalMoveIndexOptions as O, type PackedMove as P, MOVE2_PROMO_BISHOP as Q, MOVE2_PROMO_KNIGHT as R, type SquareIndex as S, MOVE2_PROMO_NONE as T, MOVE2_PROMO_QUEEN as U, MOVE2_PROMO_ROOK as V, type PositionHash as W, type SyncPositionOptions as X, type Unsubscribe as Y, type ZobristKey as Z, createBoardModel as _, PieceType as a, createDragController as a0, createLegalMoveIndex as a1, encodeBoardCell as a2, isEmptyCell as a3, isSquareIndex as a4, packMove as a5, toSquareIndex as a6, unpackMove as a7, writeBoardCell as a8, type Arrow as b, colorOf as c, type Premove as d, type ArrowCustomSvg as e, type ArrowHeadStyle as f, type ArrowLabel as g, BOARD_CELL_BK as h, BOARD_CELL_BN as i, BOARD_CELL_BP as j, BOARD_CELL_BQ as k, BOARD_CELL_BR as l, BOARD_CELL_EMPTY as m, BOARD_CELL_WB as n, BOARD_CELL_WK as o, pieceTypeOf as p, BOARD_CELL_WN as q, BOARD_CELL_WP as r, BOARD_CELL_WQ as s, BOARD_CELL_WR as t, type BoardCell as u, type BoardModel as v, type BoardModelOptions as w, type BoardSnapshot as x, type BoardStore as y, type ClickEvent as z };
774
+ export { type SyncPositionOptions as $, type AnimDescriptor as A, BOARD_CELL_BB as B, type CancelEvent as C, Color as D, type EngineAdapter as E, type DragController as F, type DragControllerOptions as G, type DragEvent as H, type DragMoveEvent as I, type DragStartEvent as J, type DragState as K, type DropEvent as L, type LegalMoveEntry as M, type LegalMoveIndex as N, type LegalMoveIndexOptions as O, type PackedMove as P, MOVE2_PROMO_BISHOP as Q, MOVE2_PROMO_KNIGHT as R, type SquareIndex as S, MOVE2_PROMO_NONE as T, MOVE2_PROMO_QUEEN as U, MOVE2_PROMO_ROOK as V, NULL_MOVE_SAN as W, NULL_MOVE_SANS as X, NULL_MOVE_UCI as Y, NULL_MOVE_WORD as Z, type PositionHash as _, PieceType as a, type Unsubscribe as a0, type ZobristKey as a1, createBoardModel as a2, createBoardStore as a3, createDragController as a4, createLegalMoveIndex as a5, encodeBoardCell as a6, isEmptyCell as a7, isNullMove as a8, isNullMoveSan as a9, isNullMoveUci as aa, isSquareIndex as ab, packMove as ac, toSquareIndex as ad, unpackMove as ae, writeBoardCell as af, type Arrow as b, colorOf as c, type Premove as d, type ArrowCustomSvg as e, type ArrowHeadStyle as f, type ArrowLabel as g, BOARD_CELL_BK as h, BOARD_CELL_BN as i, BOARD_CELL_BP as j, BOARD_CELL_BQ as k, BOARD_CELL_BR as l, BOARD_CELL_EMPTY as m, BOARD_CELL_WB as n, BOARD_CELL_WK as o, pieceTypeOf as p, BOARD_CELL_WN as q, BOARD_CELL_WP as r, BOARD_CELL_WQ as s, BOARD_CELL_WR as t, type BoardCell as u, type BoardModel as v, type BoardModelOptions as w, type BoardSnapshot as x, type BoardStore as y, type ClickEvent as z };
@@ -42,6 +42,42 @@ declare function unpackMove(move: PackedMove | number): {
42
42
  to: SquareIndex;
43
43
  promo: number;
44
44
  };
45
+ /**
46
+ * The null-move (pass) sentinel — a `u16` word of `0xffff` in the 16-bit
47
+ * Move2 wire format.
48
+ *
49
+ * @remarks
50
+ * CBH `moves2` streams store a pass turn as an ordinary move word, so the
51
+ * word itself has to be distinguishable from a real move. `0xffff` is
52
+ * unambiguous: a packed word only reaches it via `from = to = 63` with
53
+ * promotion code 15, and Move2 promotion codes are `0..4`. No legal word
54
+ * collides.
55
+ *
56
+ * Spellings: UCI is `"0000"`; PGN/SAN is `"--"` and also accepts `"Z0"`.
57
+ * See {@link NULL_MOVE_UCI}, {@link NULL_MOVE_SAN}, {@link isNullMoveUci}
58
+ * and {@link isNullMoveSan}.
59
+ *
60
+ * Gigaboard **tolerates** a pass but never originates one: no gesture,
61
+ * selection, or drag can produce this word. See {@link isNullMove}.
62
+ */
63
+ declare const NULL_MOVE_WORD: PackedMove;
64
+ /** UCI spelling of a pass. */
65
+ declare const NULL_MOVE_UCI = "0000";
66
+ /** PGN/SAN spelling of a pass. */
67
+ declare const NULL_MOVE_SAN = "--";
68
+ /** Accepted input spellings of a pass: the SAN form plus the legacy `Z0`. */
69
+ declare const NULL_MOVE_SANS: readonly string[];
70
+ /**
71
+ * `true` when `word` is the null-move (pass) sentinel.
72
+ *
73
+ * Callers gate on this **before** unpacking — `unpackMove` reads the
74
+ * sentinel as `from = to = 63, promo = 15`, which is meaningless.
75
+ */
76
+ declare function isNullMove(word: PackedMove | number): boolean;
77
+ /** `true` when `text` is the UCI spelling of a pass (`"0000"`). */
78
+ declare function isNullMoveUci(text: string): boolean;
79
+ /** `true` when `text` is an accepted PGN/SAN spelling of a pass (`"--"` or `"Z0"`). */
80
+ declare function isNullMoveSan(text: string): boolean;
45
81
  /**
46
82
  * 64-bit Zobrist key represented as two 32-bit unsigned integers (zero-BigInt).
47
83
  */
@@ -331,8 +367,29 @@ interface EngineAdapter {
331
367
  * any square).
332
368
  */
333
369
  makeMove(from: SquareIndex, to: SquareIndex, promotion?: PieceType): PackedMove | null;
334
- /** Undo the most recent move. Returns the undone move, or `null` if none. */
370
+ /**
371
+ * Undo the most recent move. Returns the undone move, or `null` if none.
372
+ *
373
+ * A pass undoes to {@link NULL_MOVE_WORD}, mirroring what
374
+ * {@link EngineAdapter.makeNullMove} produced.
375
+ */
335
376
  undo(): PackedMove | null;
377
+ /**
378
+ * Play a pass (null move): the position's pieces are untouched, the
379
+ * side to move flips, the en-passant square clears, and both clocks
380
+ * advance — a pass completes a **full** move, whoever passed, so the
381
+ * fullmove number always increments.
382
+ *
383
+ * Returns {@link NULL_MOVE_WORD} on success, or `null` when the pass is
384
+ * refused. A pass is legal **iff** the side to move is not in check: a
385
+ * pass answers no check, so a checked side cannot pass.
386
+ *
387
+ * @remarks
388
+ * Optional: an adapter that cannot represent a pass simply omits it and
389
+ * every pass attempt is refused. This is the tolerance path only —
390
+ * gigaboard never originates a pass from a user gesture.
391
+ */
392
+ makeNullMove?(): PackedMove | null;
336
393
  /**
337
394
  * Legal moves from a single origin square (`from` provided) or all legal
338
395
  * moves (`from` omitted). The returned array is fresh on every call; the
@@ -614,6 +671,20 @@ interface BoardModel {
614
671
  selectSquare(index: SquareIndex | null): void;
615
672
  /** Attempt a move. Returns the packed move on success, `null` on illegal. */
616
673
  tryMove(from: SquareIndex, to: SquareIndex, promotion?: PieceType): PackedMove | null;
674
+ /**
675
+ * Play a pass (null move) as a **tolerance** path: returns
676
+ * {@link NULL_MOVE_WORD} on success, or `null` when the adapter has no
677
+ * pass capability or the pass is illegal (the side to move is in check —
678
+ * a pass answers no check).
679
+ *
680
+ * @remarks
681
+ * A pass flips the side to move and advances both clocks, but it moves no
682
+ * piece, so it schedules **no** animation and leaves the board bytes
683
+ * untouched. No user gesture, selection, or drag routes here — this is
684
+ * only for feeding an externally supplied pass (e.g. a CBH `moves2`
685
+ * stream) into the board.
686
+ */
687
+ pass(): PackedMove | null;
617
688
  /** Undo the most recent move. */
618
689
  undo(): PackedMove | null;
619
690
  /** Redo the most recently undone move. */
@@ -700,4 +771,4 @@ interface BoardModel {
700
771
  */
701
772
  declare function createBoardModel(engine: EngineAdapter, options?: BoardModelOptions): BoardModel;
702
773
 
703
- export { createBoardStore as $, type AnimDescriptor as A, BOARD_CELL_BB as B, type CancelEvent as C, Color as D, type EngineAdapter as E, type DragController as F, type DragControllerOptions as G, type DragEvent as H, type DragMoveEvent as I, type DragStartEvent as J, type DragState as K, type DropEvent as L, type LegalMoveEntry as M, type LegalMoveIndex as N, type LegalMoveIndexOptions as O, type PackedMove as P, MOVE2_PROMO_BISHOP as Q, MOVE2_PROMO_KNIGHT as R, type SquareIndex as S, MOVE2_PROMO_NONE as T, MOVE2_PROMO_QUEEN as U, MOVE2_PROMO_ROOK as V, type PositionHash as W, type SyncPositionOptions as X, type Unsubscribe as Y, type ZobristKey as Z, createBoardModel as _, PieceType as a, createDragController as a0, createLegalMoveIndex as a1, encodeBoardCell as a2, isEmptyCell as a3, isSquareIndex as a4, packMove as a5, toSquareIndex as a6, unpackMove as a7, writeBoardCell as a8, type Arrow as b, colorOf as c, type Premove as d, type ArrowCustomSvg as e, type ArrowHeadStyle as f, type ArrowLabel as g, BOARD_CELL_BK as h, BOARD_CELL_BN as i, BOARD_CELL_BP as j, BOARD_CELL_BQ as k, BOARD_CELL_BR as l, BOARD_CELL_EMPTY as m, BOARD_CELL_WB as n, BOARD_CELL_WK as o, pieceTypeOf as p, BOARD_CELL_WN as q, BOARD_CELL_WP as r, BOARD_CELL_WQ as s, BOARD_CELL_WR as t, type BoardCell as u, type BoardModel as v, type BoardModelOptions as w, type BoardSnapshot as x, type BoardStore as y, type ClickEvent as z };
774
+ export { type SyncPositionOptions as $, type AnimDescriptor as A, BOARD_CELL_BB as B, type CancelEvent as C, Color as D, type EngineAdapter as E, type DragController as F, type DragControllerOptions as G, type DragEvent as H, type DragMoveEvent as I, type DragStartEvent as J, type DragState as K, type DropEvent as L, type LegalMoveEntry as M, type LegalMoveIndex as N, type LegalMoveIndexOptions as O, type PackedMove as P, MOVE2_PROMO_BISHOP as Q, MOVE2_PROMO_KNIGHT as R, type SquareIndex as S, MOVE2_PROMO_NONE as T, MOVE2_PROMO_QUEEN as U, MOVE2_PROMO_ROOK as V, NULL_MOVE_SAN as W, NULL_MOVE_SANS as X, NULL_MOVE_UCI as Y, NULL_MOVE_WORD as Z, type PositionHash as _, PieceType as a, type Unsubscribe as a0, type ZobristKey as a1, createBoardModel as a2, createBoardStore as a3, createDragController as a4, createLegalMoveIndex as a5, encodeBoardCell as a6, isEmptyCell as a7, isNullMove as a8, isNullMoveSan as a9, isNullMoveUci as aa, isSquareIndex as ab, packMove as ac, toSquareIndex as ad, unpackMove as ae, writeBoardCell as af, type Arrow as b, colorOf as c, type Premove as d, type ArrowCustomSvg as e, type ArrowHeadStyle as f, type ArrowLabel as g, BOARD_CELL_BK as h, BOARD_CELL_BN as i, BOARD_CELL_BP as j, BOARD_CELL_BQ as k, BOARD_CELL_BR as l, BOARD_CELL_EMPTY as m, BOARD_CELL_WB as n, BOARD_CELL_WK as o, pieceTypeOf as p, BOARD_CELL_WN as q, BOARD_CELL_WP as r, BOARD_CELL_WQ as s, BOARD_CELL_WR as t, type BoardCell as u, type BoardModel as v, type BoardModelOptions as w, type BoardSnapshot as x, type BoardStore as y, type ClickEvent as z };
@@ -102,10 +102,11 @@ function parseFenPlacement(fen) {
102
102
  }
103
103
  return board;
104
104
  }
105
+ var STARTING_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
105
106
 
106
107
  // src/pieces/default-pieces.tsx
107
108
  var defaultPieces = neo;
108
109
 
109
- export { CSS_VARS, DEFAULT_ILLEGAL_FLASH_TINT, DEFAULT_PREMOVE_TINT, defaultArrowColors, defaultPieces, defaultTheme, parseFenPlacement };
110
- //# sourceMappingURL=chunk-EJUGH25P.js.map
111
- //# sourceMappingURL=chunk-EJUGH25P.js.map
110
+ export { CSS_VARS, DEFAULT_ILLEGAL_FLASH_TINT, DEFAULT_PREMOVE_TINT, STARTING_FEN, defaultArrowColors, defaultPieces, defaultTheme, parseFenPlacement };
111
+ //# sourceMappingURL=chunk-53XWDREK.js.map
112
+ //# sourceMappingURL=chunk-53XWDREK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/default-theme.ts","../src/fen.ts","../src/pieces/default-pieces.tsx"],"names":[],"mappings":"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","file":"chunk-53XWDREK.js","sourcesContent":["import { green } from \"./themes/green.js\";\nimport type { Theme } from \"./types.js\";\n\n/** Keys every Gigaboard board understands. */\nexport const CSS_VARS = {\n SQ_LIGHT: \"--gb-sq-light\",\n SQ_DARK: \"--gb-sq-dark\",\n SQ_LIGHT_IMAGE: \"--gb-sq-light-image\",\n SQ_DARK_IMAGE: \"--gb-sq-dark-image\",\n SQ_IMAGE_SIZE: \"--gb-sq-image-size\",\n SQ_BLEND_MODE: \"--gb-sq-blend-mode\",\n PIECE_FILTER_WHITE: \"--gb-piece-filter-white\",\n PIECE_FILTER_BLACK: \"--gb-piece-filter-black\",\n LAST_MOVE: \"--gb-last-move\",\n SELECTED: \"--gb-selected\",\n LEGAL_TARGET: \"--gb-legal-target\",\n LEGAL_TARGET_CAPTURE: \"--gb-legal-target-capture\",\n CHECK: \"--gb-check\",\n COORDINATE_LIGHT: \"--gb-coord-light\",\n COORDINATE_DARK: \"--gb-coord-dark\",\n /**\n * Opacity of the piece left behind at the drag origin while the ghost\n * is in flight. Default `0.35` (lichess parity). Themes may override\n * to `0` for a chess.com-style \"piece vanishes\" effect.\n */\n DRAG_GHOST_OPACITY: \"--gb-drag-ghost-opacity\",\n} as const;\n\nexport const defaultTheme: Theme = Object.freeze({\n ...green,\n [CSS_VARS.DRAG_GHOST_OPACITY]: \"0.35\",\n});\n\n/**\n * Default arrow-channel colours. Close to lichess's palette so arrows feel\n * familiar to players migrating over.\n */\nexport const defaultArrowColors = Object.freeze({\n default: \"rgba(21, 120, 27, 0.8)\",\n shift: \"rgba(153, 40, 40, 0.8)\",\n alt: \"rgba(230, 160, 0, 0.8)\",\n ctrl: \"rgba(0, 60, 130, 0.8)\",\n});\n\n/** Distinct blue tint used to preview pending premoves. */\nexport const DEFAULT_PREMOVE_TINT = \"rgba(30, 98, 255, 0.4)\";\n\n/** Red tint used to announce rejected moves. */\nexport const DEFAULT_ILLEGAL_FLASH_TINT = \"rgba(220, 40, 40, 0.55)\";\n","/**\n * FEN placement parsing — the one slice of FEN that both the server\n * `<StaticChessboard/>` and the client fallback-render path need.\n *\n * The parser is intentionally narrow: it only consumes the first\n * whitespace-delimited field of a FEN (piece placement) and returns a\n * 64-byte `Uint8Array` using our {@link BoardCell} encoding. Side-to-move,\n * castling, en-passant and the move clocks are the engine's business and\n * don't affect how the board is drawn.\n *\n * Kept in its own module so that:\n *\n * 1. `server.tsx` can import it without pulling a `\"use client\"` module.\n * 2. The client `<Chessboard/>`'s fallback path (`fallbackFen`) can reuse\n * the exact same parser — one source of truth for FEN shape.\n *\n * Throws on malformed input so misconfiguration fails at render time\n * rather than silently producing a blank board.\n */\n\nimport type { BoardCell } from \"./core/index.js\";\n\n/** FEN piece letter → our 1..12 `BoardCell` code, or 0 for unknown. */\nfunction pieceCellFromFenChar(ch: string): BoardCell {\n switch (ch) {\n case \"P\":\n return 1 as BoardCell;\n case \"N\":\n return 2 as BoardCell;\n case \"B\":\n return 3 as BoardCell;\n case \"R\":\n return 4 as BoardCell;\n case \"Q\":\n return 5 as BoardCell;\n case \"K\":\n return 6 as BoardCell;\n case \"p\":\n return 7 as BoardCell;\n case \"n\":\n return 8 as BoardCell;\n case \"b\":\n return 9 as BoardCell;\n case \"r\":\n return 10 as BoardCell;\n case \"q\":\n return 11 as BoardCell;\n case \"k\":\n return 12 as BoardCell;\n default:\n return 0 as BoardCell;\n }\n}\n\n/**\n * Parse the piece-placement field of a FEN into a 64-byte `Uint8Array`\n * in LERF order (index 0 = a1, index 63 = h8).\n *\n * Only the first whitespace-delimited token is read; anything after it\n * is ignored. That means both a full FEN (`\"rnbq... w KQkq - 0 1\"`) and\n * a bare placement (`\"rnbq...\"`) are accepted.\n */\nexport function parseFenPlacement(fen: string): Uint8Array {\n const placement = fen.trim().split(/\\s+/)[0];\n if (placement === undefined) {\n throw new Error(\"FEN is empty\");\n }\n const ranks = placement.split(\"/\");\n if (ranks.length !== 8) {\n throw new Error(`FEN must have 8 ranks, got ${ranks.length}`);\n }\n const board = new Uint8Array(64);\n for (let r = 0; r < 8; r++) {\n // FEN lists ranks 8→1; our LERF layout has rank 7 at top, 0 at bottom.\n const rank = 7 - r;\n const row = ranks[r];\n if (row === undefined) throw new Error(`FEN rank ${rank + 1} missing`);\n let file = 0;\n for (const ch of row) {\n if (file > 7) throw new Error(`FEN rank ${rank + 1} has too many files`);\n const skip = Number.parseInt(ch, 10);\n if (!Number.isNaN(skip)) {\n file += skip;\n continue;\n }\n const cell = pieceCellFromFenChar(ch);\n if (cell === 0) throw new Error(`FEN contains unknown piece: ${ch}`);\n board[rank * 8 + file] = cell;\n file++;\n }\n if (file !== 8) {\n throw new Error(`FEN rank ${rank + 1} ended at file ${file}, expected 8`);\n }\n }\n return board;\n}\n\n/** Standard starting position FEN, re-exported for convenience. */\nexport const STARTING_FEN = \"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\";\n","import type { PieceRenderer } from \"../types.js\";\nimport { neo } from \"./sets/neo.js\";\n\nexport const defaultPieces: PieceRenderer = neo;\n"]}
@@ -1,4 +1,4 @@
1
- import { ensureMagicTablesLoaded, ensureZobristLoaded, makeSan } from 'gigachess';
1
+ import { ensureMagicTablesLoaded, ensureZobristLoaded, isEmpty, kingAttackers, opposite, makeSan } from 'gigachess';
2
2
  import { Chess } from 'gigachess/chessjs';
3
3
 
4
4
  // src/core/adapters/gigachess.ts
@@ -20,6 +20,19 @@ function unpackMove(move) {
20
20
  promo: w >>> 12 & 15
21
21
  };
22
22
  }
23
+ var NULL_MOVE_WORD = 65535;
24
+ var NULL_MOVE_UCI = "0000";
25
+ var NULL_MOVE_SAN = "--";
26
+ var NULL_MOVE_SANS = Object.freeze([NULL_MOVE_SAN, "Z0"]);
27
+ function isNullMove(word) {
28
+ return (word & 65535) === 65535;
29
+ }
30
+ function isNullMoveUci(text) {
31
+ return text.trim().toLowerCase() === NULL_MOVE_UCI;
32
+ }
33
+ function isNullMoveSan(text) {
34
+ return NULL_MOVE_SANS.includes(text.trim());
35
+ }
23
36
  var Color = {
24
37
  White: 0,
25
38
  Black: 1
@@ -121,6 +134,10 @@ function promoChar(piece) {
121
134
  }
122
135
  function createGigachessAdapter(fen) {
123
136
  const chess = fen === void 0 ? new Chess() : new Chess(fen);
137
+ const plyStack = [];
138
+ const clearPlyStack = () => {
139
+ plyStack.length = 0;
140
+ };
124
141
  const makeMove = (from, to, promotion) => {
125
142
  try {
126
143
  const fromAlg = algebraicOf(from);
@@ -132,6 +149,7 @@ function createGigachessAdapter(fen) {
132
149
  ...promo ? { promotion: promo } : {}
133
150
  });
134
151
  if (res === null) return null;
152
+ plyStack.push(null);
135
153
  if (res.flags.includes("k")) {
136
154
  return res.color === "w" ? packMove(4, 7, 0) : packMove(60, 63, 0);
137
155
  }
@@ -145,6 +163,19 @@ function createGigachessAdapter(fen) {
145
163
  }
146
164
  };
147
165
  const undo = () => {
166
+ const top = plyStack.pop();
167
+ if (top === void 0) return null;
168
+ if (top !== null) {
169
+ const board = chess.boardInstance;
170
+ board.turn = top.turn;
171
+ board.epSquare = top.epSquare;
172
+ board.halfmoves = top.halfmoves;
173
+ board.fullmoves = top.fullmoves;
174
+ board.checkers = { lo: top.checkersLo, hi: top.checkersHi };
175
+ board._zobristLo = top.zobristLo;
176
+ board._zobristHi = top.zobristHi;
177
+ return NULL_MOVE_WORD;
178
+ }
148
179
  const res = chess.undo();
149
180
  if (res === null) return null;
150
181
  if (res.flags.includes("k")) {
@@ -192,6 +223,30 @@ function createGigachessAdapter(fen) {
192
223
  }
193
224
  }
194
225
  };
226
+ const makeNullMove = () => {
227
+ const board = chess.boardInstance;
228
+ const us = board.turn;
229
+ if (!isEmpty(kingAttackers(board, us))) return null;
230
+ plyStack.push({
231
+ turn: us,
232
+ epSquare: board.epSquare,
233
+ halfmoves: board.halfmoves,
234
+ fullmoves: board.fullmoves,
235
+ checkersLo: board.checkers.lo,
236
+ checkersHi: board.checkers.hi,
237
+ zobristLo: board._zobristLo,
238
+ zobristHi: board._zobristHi
239
+ });
240
+ const them = opposite(us);
241
+ board.epSquare = null;
242
+ board.halfmoves = board.halfmoves + 1;
243
+ board.fullmoves = board.fullmoves + 1;
244
+ board.turn = them;
245
+ board._zobristLo = 0;
246
+ board._zobristHi = 0;
247
+ board.checkers = kingAttackers(board, them);
248
+ return NULL_MOVE_WORD;
249
+ };
195
250
  const san = (move) => {
196
251
  const { from, to, promo } = unpackMove(move);
197
252
  const role = promoRole(promo);
@@ -207,6 +262,7 @@ function createGigachessAdapter(fen) {
207
262
  return {
208
263
  makeMove,
209
264
  undo,
265
+ makeNullMove,
210
266
  legalMoves,
211
267
  readBoard,
212
268
  fen: () => chess.fen(),
@@ -215,9 +271,19 @@ function createGigachessAdapter(fen) {
215
271
  inCheck: () => chess.inCheck(),
216
272
  san,
217
273
  isGameOver: () => chess.isGameOver(),
218
- load: (f) => chess.load(f),
219
- reset: () => chess.reset(),
274
+ // Loading or resetting replaces the position and drops the engine's
275
+ // history, so the mirrored ply stack has to go with it — otherwise a
276
+ // later `undo()` would unmake a ply that no longer exists.
277
+ load: (f) => {
278
+ chess.load(f);
279
+ clearPlyStack();
280
+ },
281
+ reset: () => {
282
+ chess.reset();
283
+ clearPlyStack();
284
+ },
220
285
  dispose: () => {
286
+ clearPlyStack();
221
287
  }
222
288
  };
223
289
  }
@@ -242,6 +308,7 @@ function planAnimations(prev, next, move) {
242
308
  }
243
309
  }
244
310
  if (move !== null) {
311
+ if (isNullMove(move)) return [];
245
312
  return planFromMove(prev, next, move);
246
313
  }
247
314
  return planFromDiff(prev, next);
@@ -335,6 +402,9 @@ function planFromDiff(prev, next) {
335
402
  return descriptors;
336
403
  }
337
404
  function decodePackedMove(m) {
405
+ if (isNullMove(m)) {
406
+ return { from: 0, to: 0, kind: "pass", promotion: null };
407
+ }
338
408
  const from = m & MOVE_FROM_MASK;
339
409
  const to = m >> MOVE_TO_SHIFT & MOVE_TO_MASK;
340
410
  const promoCode = m >> MOVE_PROMO_SHIFT & MOVE_PROMO_MASK;
@@ -371,6 +441,19 @@ function freezeArrow(a) {
371
441
  return Object.freeze(out);
372
442
  }
373
443
  var EMPTY_ARROWS = Object.freeze([]);
444
+ function labelsEqual(x, y) {
445
+ if (x === y) return true;
446
+ if (x === void 0 || y === void 0) return false;
447
+ return x.text === y.text && x.fill === y.fill && x.background === y.background && x.fontSize === y.fontSize && x.anchor === y.anchor && x.borderColor === y.borderColor && x.headStyle === y.headStyle && x.textRotation === y.textRotation && x.style3d === y.style3d;
448
+ }
449
+ function customSvgsEqual(x, y) {
450
+ if (x === y) return true;
451
+ if (x === void 0 || y === void 0) return false;
452
+ return x.html === y.html && x.center === y.center;
453
+ }
454
+ function arrowVisualsEqual(stored, incoming) {
455
+ return stored.managed === incoming.managed && labelsEqual(stored.label, incoming.label) && customSvgsEqual(stored.customSvg, incoming.customSvg);
456
+ }
374
457
  function createArrowModel() {
375
458
  const byKey = /* @__PURE__ */ new Map();
376
459
  let changed = false;
@@ -434,7 +517,8 @@ function createArrowModel() {
434
517
  const key = arrowKey(marked);
435
518
  if (nextKeys.has(key)) continue;
436
519
  nextKeys.add(key);
437
- if (!byKey.has(key)) {
520
+ const stored = byKey.get(key);
521
+ if (stored === void 0 || !arrowVisualsEqual(stored, marked)) {
438
522
  byKey.set(key, freezeArrow(marked));
439
523
  mutated = true;
440
524
  }
@@ -457,7 +541,8 @@ function createArrowModel() {
457
541
  const key = arrowKey(a);
458
542
  if (nextKeys.has(key)) continue;
459
543
  nextKeys.add(key);
460
- if (!byKey.has(key)) {
544
+ const stored = byKey.get(key);
545
+ if (stored === void 0 || !arrowVisualsEqual(stored, a)) {
461
546
  byKey.set(key, freezeArrow(a));
462
547
  mutated = true;
463
548
  }
@@ -838,6 +923,7 @@ function createBoardModel(engine, options = {}) {
838
923
  m = engine.makeMove(from, to, promotion);
839
924
  }
840
925
  if (m === null) return null;
926
+ if (isNullMove(m)) return null;
841
927
  redoStack.length = 0;
842
928
  history.push(m);
843
929
  lastMove = m;
@@ -851,6 +937,17 @@ function createBoardModel(engine, options = {}) {
851
937
  }
852
938
  return m;
853
939
  };
940
+ const pass = () => {
941
+ if (engine.makeNullMove === void 0) return null;
942
+ const m = engine.makeNullMove();
943
+ if (m === null) return null;
944
+ redoStack.length = 0;
945
+ history.push(m);
946
+ lastMove = m;
947
+ selected = null;
948
+ commitWith(m);
949
+ return m;
950
+ };
854
951
  const undo = () => {
855
952
  const m = engine.undo();
856
953
  if (m === null) return null;
@@ -864,11 +961,7 @@ function createBoardModel(engine, options = {}) {
864
961
  const redo = () => {
865
962
  const m = redoStack.pop();
866
963
  if (m === void 0) return null;
867
- const from = m & 63;
868
- const to = moveTo(m);
869
- const promoBits = m >> 12 & 15;
870
- const promotion = promoBits > 0 ? promoBits : void 0;
871
- const applied = engine.makeMove(from, to, promotion);
964
+ const applied = replayPackedMove(m);
872
965
  if (applied === null) {
873
966
  return null;
874
967
  }
@@ -878,6 +971,14 @@ function createBoardModel(engine, options = {}) {
878
971
  commitWith(applied);
879
972
  return applied;
880
973
  };
974
+ const replayPackedMove = (m) => {
975
+ if (isNullMove(m)) return engine.makeNullMove?.() ?? null;
976
+ const from = m & 63;
977
+ const to = moveTo(m);
978
+ const promoBits = m >> 12 & 15;
979
+ const promotion = promoBits > 0 ? promoBits : void 0;
980
+ return engine.makeMove(from, to, promotion);
981
+ };
881
982
  const goto = (ply) => {
882
983
  if (!Number.isInteger(ply) || ply < 0) return;
883
984
  const total = history.length + redoStack.length;
@@ -891,11 +992,7 @@ function createBoardModel(engine, options = {}) {
891
992
  while (history.length < ply) {
892
993
  const m = redoStack.pop();
893
994
  if (m === void 0) break;
894
- const from = m & 63;
895
- const to = moveTo(m);
896
- const promoBits = m >> 12 & 15;
897
- const promotion = promoBits > 0 ? promoBits : void 0;
898
- const applied = engine.makeMove(from, to, promotion);
995
+ const applied = replayPackedMove(m);
899
996
  if (applied === null) break;
900
997
  history.push(applied);
901
998
  }
@@ -1069,6 +1166,7 @@ function createBoardModel(engine, options = {}) {
1069
1166
  subscribeSquare: (i, l) => store.subscribeSquare(i, l),
1070
1167
  selectSquare,
1071
1168
  tryMove,
1169
+ pass,
1072
1170
  undo,
1073
1171
  redo,
1074
1172
  goto,
@@ -1105,6 +1203,6 @@ function writeBoardCell(out, index, cell) {
1105
1203
  // src/core/index.ts
1106
1204
  var PACKAGE_VERSION = "1.3.3";
1107
1205
 
1108
- export { BOARD_CELL_BB, BOARD_CELL_BK, BOARD_CELL_BN, BOARD_CELL_BP, BOARD_CELL_BQ, BOARD_CELL_BR, BOARD_CELL_EMPTY, BOARD_CELL_WB, BOARD_CELL_WK, BOARD_CELL_WN, BOARD_CELL_WP, BOARD_CELL_WQ, BOARD_CELL_WR, Color, MOVE2_PROMO_BISHOP, MOVE2_PROMO_KNIGHT, MOVE2_PROMO_NONE, MOVE2_PROMO_QUEEN, MOVE2_PROMO_ROOK, PACKAGE_VERSION, PieceType, animationInspect, colorOf, createArrowModel, createBoardModel, createBoardStore, createDragController, createGigachessAdapter, createLegalMoveIndex, createPremoveBuffer, decodePackedMove, encodeBoardCell, isEmptyCell, isSquareIndex, makeArrow, makePremove, packMove, pieceTypeOf, planAnimations, preloadGigachessAdapter, toSquareIndex, unpackMove, writeBoardCell };
1109
- //# sourceMappingURL=chunk-SO6AOEFF.js.map
1110
- //# sourceMappingURL=chunk-SO6AOEFF.js.map
1206
+ export { BOARD_CELL_BB, BOARD_CELL_BK, BOARD_CELL_BN, BOARD_CELL_BP, BOARD_CELL_BQ, BOARD_CELL_BR, BOARD_CELL_EMPTY, BOARD_CELL_WB, BOARD_CELL_WK, BOARD_CELL_WN, BOARD_CELL_WP, BOARD_CELL_WQ, BOARD_CELL_WR, Color, MOVE2_PROMO_BISHOP, MOVE2_PROMO_KNIGHT, MOVE2_PROMO_NONE, MOVE2_PROMO_QUEEN, MOVE2_PROMO_ROOK, NULL_MOVE_SAN, NULL_MOVE_SANS, NULL_MOVE_UCI, NULL_MOVE_WORD, PACKAGE_VERSION, PieceType, animationInspect, colorOf, createArrowModel, createBoardModel, createBoardStore, createDragController, createGigachessAdapter, createLegalMoveIndex, createPremoveBuffer, decodePackedMove, encodeBoardCell, isEmptyCell, isNullMove, isNullMoveSan, isNullMoveUci, isSquareIndex, makeArrow, makePremove, packMove, pieceTypeOf, planAnimations, preloadGigachessAdapter, toSquareIndex, unpackMove, writeBoardCell };
1207
+ //# sourceMappingURL=chunk-6F3FZAG7.js.map
1208
+ //# sourceMappingURL=chunk-6F3FZAG7.js.map