@connectorvol/chess-widgets 8.0.1 → 9.0.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 (47) hide show
  1. package/dist/constants/default-board-appearance-settings.d.ts +9 -0
  2. package/dist/constants/default-board-appearance-settings.js +30 -0
  3. package/dist/constants/editable-board-settings.d.ts +3 -21
  4. package/dist/constants/editable-board-settings.js +24 -19
  5. package/dist/game-analyzer/GameAnalyzer.svelte +74 -70
  6. package/dist/game-analyzer/gameAnalyzer.svelte.js +8 -6
  7. package/dist/game-analyzer/types.d.ts +11 -3
  8. package/dist/index.d.ts +9 -1
  9. package/dist/index.js +6 -0
  10. package/dist/position-editor/EditPanel.svelte +9 -6
  11. package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
  12. package/dist/puzzle/puzzleData.d.ts +4 -0
  13. package/dist/puzzle/puzzleData.js +10 -0
  14. package/dist/puzzle/puzzlePreviewConstants.d.ts +4 -0
  15. package/dist/puzzle/puzzlePreviewConstants.js +4 -0
  16. package/dist/puzzle/puzzlePreviewPathNags.d.ts +6 -0
  17. package/dist/puzzle/puzzlePreviewPathNags.js +35 -0
  18. package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +2 -4
  19. package/dist/puzzle/puzzleSolverForkAnnotations.js +13 -22
  20. package/dist/puzzle/puzzleStepPreviewSolver.d.ts +13 -1
  21. package/dist/puzzle/puzzleStepPreviewSolver.js +69 -9
  22. package/dist/puzzle/syncPuzzleBranchNags.d.ts +14 -0
  23. package/dist/puzzle/syncPuzzleBranchNags.js +125 -0
  24. package/dist/puzzle-creation/OpeningTagHoverPreview.svelte +81 -0
  25. package/dist/puzzle-creation/OpeningTagHoverPreview.svelte.d.ts +11 -0
  26. package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte +104 -32
  27. package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte.d.ts +16 -2
  28. package/dist/puzzle-creation/PuzzleCreationWizard.svelte +192 -202
  29. package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +47 -3
  30. package/dist/puzzle-creation/PuzzlePgnBoardTreeEditor.svelte +485 -74
  31. package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte +36 -0
  32. package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte.d.ts +12 -0
  33. package/dist/puzzle-creation/StepMoves.svelte +38 -18
  34. package/dist/puzzle-creation/StepMoves.svelte.d.ts +2 -1
  35. package/dist/puzzle-creation/StepPosition.svelte +15 -9
  36. package/dist/puzzle-creation/StepPreview.svelte +24 -11
  37. package/dist/puzzle-creation/StepPreview.svelte.d.ts +3 -1
  38. package/dist/puzzle-creation/StepTags.svelte +270 -0
  39. package/dist/puzzle-creation/StepTags.svelte.d.ts +19 -0
  40. package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.d.ts +9 -0
  41. package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.js +19 -0
  42. package/dist/puzzle-creation/createPuzzleLineEditingBoard.d.ts +10 -3
  43. package/dist/puzzle-creation/createPuzzleLineEditingBoard.js +14 -9
  44. package/dist/puzzle-creation/puzzleWizardState.d.ts +35 -0
  45. package/dist/puzzle-creation/puzzleWizardState.js +37 -0
  46. package/dist/puzzle-creation/types.d.ts +37 -7
  47. package/package.json +20 -17
@@ -0,0 +1,9 @@
1
+ import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
2
+ /**
3
+ * Представляет визуальные настройки шахматной доски по умолчанию для виджетов chess-widgets
4
+ * (без `boardSize`, `orientation`, `draggable` — их подставляет вызывающий код).
5
+ */
6
+ export declare const DEFAULT_BOARD_APPEARANCE_SETTINGS: {
7
+ play: TChessBoardPlaySettings;
8
+ design: TChessBoardDesignSettings;
9
+ };
@@ -0,0 +1,30 @@
1
+ import { DEFAULT_DESIGN_SETTINGS, Draggable, PieceInputMode } from "@connectorvol/chessboard";
2
+ import { Color } from "@connectorvol/shared";
3
+ /**
4
+ * Представляет визуальные настройки шахматной доски по умолчанию для виджетов chess-widgets
5
+ * (без `boardSize`, `orientation`, `draggable` — их подставляет вызывающий код).
6
+ */
7
+ export const DEFAULT_BOARD_APPEARANCE_SETTINGS = {
8
+ play: {
9
+ isResizable: true,
10
+ orientation: Color.WHITE,
11
+ boardSize: 38,
12
+ draggable: Draggable.NONE,
13
+ pieceInputMode: PieceInputMode.SOURCE_DRAG,
14
+ allowPreMove: true,
15
+ clearMarkersOnLeftClick: true,
16
+ showSquareBadges: true,
17
+ animationTime: 300,
18
+ editSettings: null,
19
+ needBorderRadius: false,
20
+ },
21
+ design: {
22
+ ...DEFAULT_DESIGN_SETTINGS,
23
+ showBoardCoordinates: false,
24
+ autoQueenPromotion: false,
25
+ showPossibleMoveIndicators: true,
26
+ border: false,
27
+ woodTexture: false,
28
+ boardGradient: true,
29
+ },
30
+ };
@@ -1,26 +1,8 @@
1
- import { Draggable, PieceInputMode } from "@connectorvol/chessboard";
1
+ import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
2
2
  /**
3
3
  * Представляет настройки редактируемой доски по умолчанию для конструктора позиции и мастера задач.
4
4
  */
5
5
  export declare const DEFAULT_EDITABLE_BOARD_SETTINGS: {
6
- autoQueenPromotion: false;
7
- isResizable: true;
8
- showBoardCoordinates: false;
9
- orientation: "w";
10
- boardSize: number;
11
- draggable: Draggable.BOTH;
12
- pieceInputMode: PieceInputMode.SOURCE_DRAG;
13
- allowPreMove: true;
14
- border: false;
15
- allowDrawMarkers: false;
16
- clearMarkersOnLeftClick: true;
17
- showSquareBadges: true;
18
- animationTime: number;
19
- editSettings: {
20
- mode: {
21
- type: "delete";
22
- piece: null;
23
- color: null;
24
- };
25
- };
6
+ play: TChessBoardPlaySettings;
7
+ design: TChessBoardDesignSettings;
26
8
  };
@@ -1,27 +1,32 @@
1
- import { Draggable, PieceInputMode } from "@connectorvol/chessboard";
1
+ import { DEFAULT_DESIGN_SETTINGS, Draggable, PieceInputMode } from "@connectorvol/chessboard";
2
2
  import { Color } from "@connectorvol/shared";
3
3
  /**
4
4
  * Представляет настройки редактируемой доски по умолчанию для конструктора позиции и мастера задач.
5
5
  */
6
6
  export const DEFAULT_EDITABLE_BOARD_SETTINGS = {
7
- autoQueenPromotion: false,
8
- isResizable: true,
9
- showBoardCoordinates: false,
10
- orientation: Color.WHITE,
11
- boardSize: 38,
12
- draggable: Draggable.BOTH,
13
- pieceInputMode: PieceInputMode.SOURCE_DRAG,
14
- allowPreMove: true,
15
- border: false,
16
- allowDrawMarkers: false,
17
- clearMarkersOnLeftClick: true,
18
- showSquareBadges: true,
19
- animationTime: 300,
20
- editSettings: {
21
- mode: {
22
- type: "delete",
23
- piece: null,
24
- color: null,
7
+ play: {
8
+ isResizable: true,
9
+ orientation: Color.WHITE,
10
+ boardSize: 38,
11
+ draggable: Draggable.BOTH,
12
+ pieceInputMode: PieceInputMode.SOURCE_DRAG,
13
+ allowPreMove: true,
14
+ clearMarkersOnLeftClick: true,
15
+ showSquareBadges: true,
16
+ animationTime: 300,
17
+ editSettings: {
18
+ mode: {
19
+ type: "delete",
20
+ piece: null,
21
+ color: null,
22
+ },
25
23
  },
24
+ needBorderRadius: false,
25
+ },
26
+ design: {
27
+ ...DEFAULT_DESIGN_SETTINGS,
28
+ border: false,
29
+ showBoardCoordinates: false,
30
+ markerConfig: null,
26
31
  },
27
32
  };
@@ -1,83 +1,87 @@
1
1
  <script lang="ts">
2
- import type { TGameAnalyzerProps } from "./types.js";
2
+ import type { TGameAnalyzerProps } from "./types.js";
3
3
 
4
- import { Chessboard } from "@connectorvol/chessboard";
5
- import { TreeViewer, TreeViewerPanelManager } from "@connectorvol/tree";
6
- import { Game } from "./gameAnalyzer.svelte.js";
7
- import { untrack } from "svelte";
4
+ import { Chessboard } from "@connectorvol/chessboard";
5
+ import {
6
+ TREE_VIEWER_LIGHT_THEME,
7
+ TreeViewer,
8
+ TreeViewerPanelManager,
9
+ } from "@connectorvol/tree";
10
+ import { Game } from "./gameAnalyzer.svelte.js";
11
+ import { untrack } from "svelte";
12
+ import { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "../constants/default-board-appearance-settings.js";
8
13
 
9
- let {
10
- pgn,
11
- onChangePGN,
12
- boardTheme,
13
- boardAppearanceSettings,
14
- editMode = true,
15
- class: className,
16
- }: TGameAnalyzerProps = $props();
14
+ let {
15
+ pgn,
16
+ onChangePGN,
17
+ boardTheme,
18
+ boardAppearanceSettings = DEFAULT_BOARD_APPEARANCE_SETTINGS,
19
+ editMode = true,
20
+ treeViewerTheme = TREE_VIEWER_LIGHT_THEME,
21
+ class: className,
22
+ }: TGameAnalyzerProps = $props();
17
23
 
18
- let game = $derived(
19
- new Game(
20
- untrack(() => pgn),
21
- {
22
- ...boardAppearanceSettings,
23
- },
24
- "chess",
25
- boardTheme,
26
- ),
27
- );
24
+ let game = $derived(
25
+ new Game(
26
+ untrack(() => pgn),
27
+ boardAppearanceSettings?.play ?? {},
28
+ "chess",
29
+ boardTheme,
30
+ ),
31
+ );
28
32
 
29
- let boardSize = $derived(
30
- game.chessboard.getBoard().settingsService.boardSize,
31
- );
32
- let isAutoSize = $derived(boardSize === "auto");
33
+ let boardSize = $derived(
34
+ game.chessboard.getBoard().settingsService.boardSize,
35
+ );
36
+ let isAutoSize = $derived(boardSize === "auto");
33
37
 
34
- function handleChangeDirty(_setIsDirty: (value: boolean) => void) {
35
- console.log("handleChangeDirty");
36
- const exported = game.exportPgn();
37
- onChangePGN?.(exported);
38
- }
38
+ function handleChangeDirty(_setIsDirty: (value: boolean) => void) {
39
+ console.log("handleChangeDirty");
40
+ const exported = game.exportPgn();
41
+ onChangePGN?.(exported);
42
+ }
39
43
  </script>
40
44
 
41
45
  <div class={["mx-4 lg:container lg:mx-auto gap-4", className]}>
42
- <div class="flex justify-center relative lg:gap-4 flex-col lg:flex-row">
43
- <div
44
- class={[
45
- "w-full",
46
- isAutoSize ? "lg:w-2/5" : "lg:w-auto lg:min-w-[3.75rem]",
47
- ]}
48
- style={!isAutoSize && typeof boardSize === "number"
49
- ? `flex-basis: ${boardSize}rem;`
50
- : ""}
51
- >
52
- <Chessboard facade={game.chessboard} />
53
- </div>
46
+ <div class="flex justify-center relative lg:gap-4 flex-col lg:flex-row">
47
+ <div
48
+ class={[
49
+ "w-full",
50
+ isAutoSize ? "lg:w-2/5" : "lg:w-auto lg:min-w-[3.75rem]",
51
+ ]}
52
+ style={!isAutoSize && typeof boardSize === "number"
53
+ ? `flex-basis: ${boardSize}rem;`
54
+ : ""}
55
+ >
56
+ <Chessboard facade={game.chessboard} design={{ theme: game.theme }} />
57
+ </div>
54
58
 
55
- <div
56
- class={[
57
- "w-full flex lg:flex-col flex-col-reverse h-full",
58
- isAutoSize ? "lg:w-3/5" : "lg:flex-1 lg:min-w-[20rem]",
59
- ]}
60
- >
61
- <div class="h-full">
62
- <TreeViewer
63
- className="h-[calc(100dvh-8rem)]"
64
- chessTree={game.chessTree}
65
- onSelectNode={game.onSelectNode}
66
- onDeleteVariant={game.onDeleteVariant}
67
- setChessFen={game.setChessFen}
68
- setChessboardFen={game.setChessboardFen}
69
- pieceSet={game.chessboard.chessSet}
70
- {editMode}
71
- onChangeDirty={handleChangeDirty}
72
- />
73
- </div>
59
+ <div
60
+ class={[
61
+ "w-full flex lg:flex-col flex-col-reverse h-full",
62
+ isAutoSize ? "lg:w-3/5" : "lg:flex-1 lg:min-w-[20rem]",
63
+ ]}
64
+ >
65
+ <div class="h-full">
66
+ <TreeViewer
67
+ className="h-[calc(100dvh-8rem)]"
68
+ chessTree={game.chessTree}
69
+ onSelectNode={game.onSelectNode}
70
+ onDeleteVariant={game.onDeleteVariant}
71
+ setChessFen={game.setChessFen}
72
+ setChessboardFen={game.setChessboardFen}
73
+ {editMode}
74
+ theme={treeViewerTheme}
75
+ onChangeDirty={handleChangeDirty}
76
+ />
77
+ </div>
74
78
 
75
- <TreeViewerPanelManager
76
- className="flex w-full justify-center select-none col-span-1 my-4"
77
- chessTree={game.chessTree}
78
- setChessFen={game.setChessFen}
79
- setChessboardFen={game.setChessboardFen}
80
- />
81
- </div>
79
+ <TreeViewerPanelManager
80
+ className="flex w-full justify-center select-none col-span-1 my-4"
81
+ chessTree={game.chessTree}
82
+ setChessFen={game.setChessFen}
83
+ setChessboardFen={game.setChessboardFen}
84
+ />
82
85
  </div>
86
+ </div>
83
87
  </div>
@@ -1,5 +1,5 @@
1
1
  import { Color, calculatePly, generateId } from "@connectorvol/shared";
2
- import { CHESSBOARD_THEMES, createBoardApi, Draggable, syncMoveEvaluationNagBadge, } from "@connectorvol/chessboard";
2
+ import { CHESSBOARD_THEMES, createBoardApi, DEFAULT_PLAY_SETTINGS, Draggable, syncMoveEvaluationNagBadge, } from "@connectorvol/chessboard";
3
3
  import { PgnOps } from "@connectorvol/chessops/pgnOps.svelte";
4
4
  import { transformPgnToChessNode, createPgnFromTree } from "@connectorvol/tree";
5
5
  import { ChessTree } from "@connectorvol/tree";
@@ -9,23 +9,25 @@ export class Game {
9
9
  chessboard;
10
10
  chessTree;
11
11
  previousNode;
12
- boardSettings;
12
+ playSettings;
13
13
  variant;
14
14
  boardTheme;
15
+ /** Возвращает активную тему доски, реактивный `$state` для биндинга в UI. */
16
+ theme = $state(CHESSBOARD_THEMES.blue);
15
17
  constructor(pgn, chessBoardSettings, variant = "chess", boardTheme) {
16
- this.boardSettings = chessBoardSettings;
18
+ this.playSettings = { ...DEFAULT_PLAY_SETTINGS, ...chessBoardSettings };
17
19
  this.variant = variant;
18
20
  this.boardTheme = boardTheme ?? CHESSBOARD_THEMES.blue;
21
+ this.theme = this.boardTheme;
19
22
  const { rootNode, initialFen: position } = transformPgnToChessNode(pgn);
20
23
  this.chessTree = new ChessTree(rootNode);
21
24
  this.chess = new PgnOps(position, variant);
22
25
  this.chessboard = createBoardApi({
23
26
  fen: this.chess.fen(),
24
- settings: {
25
- ...this.boardSettings,
27
+ playSettings: {
28
+ ...this.playSettings,
26
29
  draggable: this.chess.turn() === Color.WHITE ? Draggable.WHITE : Draggable.BLACK,
27
30
  },
28
- theme: this.boardTheme,
29
31
  });
30
32
  this.chessboard.setActions(this.buildActions());
31
33
  this.previousNode = this.chessTree.currentNode;
@@ -1,9 +1,13 @@
1
1
  import type { ChessboardTheme } from "@connectorvol/chessboard";
2
- import type { ChessBoardSettings } from "@connectorvol/chessboard";
2
+ import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
3
+ import type { TTreeViewerTheme } from "@connectorvol/tree";
3
4
  /**
4
5
  * Представляет тип визуальных настроек шахматной доски для анализатора партии.
5
6
  */
6
- export type TChessboardAppearanceSettings = Omit<Partial<ChessBoardSettings>, "boardSize" | "orientation" | "draggable">;
7
+ export type TChessboardAppearanceSettings = {
8
+ play?: Omit<TChessBoardPlaySettings, "boardSize" | "orientation" | "draggable">;
9
+ design?: Partial<TChessBoardDesignSettings>;
10
+ };
7
11
  /**
8
12
  * Представляет свойства компонента анализатора партии.
9
13
  */
@@ -23,11 +27,15 @@ export interface TGameAnalyzerProps {
23
27
  /**
24
28
  * Возвращает дополнительные визуальные настройки шахматной доски (кроме `boardSize`, `orientation`, `draggable`).
25
29
  */
26
- boardAppearanceSettings: TChessboardAppearanceSettings;
30
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
27
31
  /**
28
32
  * Возвращает признак режима правки дерева (по умолчанию `true`).
29
33
  */
30
34
  editMode?: boolean;
35
+ /**
36
+ * Возвращает тему `TreeViewer` (фон, границы, текст). По умолчанию `TREE_VIEWER_LIGHT_THEME`.
37
+ */
38
+ treeViewerTheme?: TTreeViewerTheme;
31
39
  /**
32
40
  * Возвращает дополнительные классы Tailwind для корневого контейнера.
33
41
  */
package/dist/index.d.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  export { default as PuzzleCreationWizard } from "./puzzle-creation/PuzzleCreationWizard.svelte";
2
+ export { default as PuzzleWizardTagsStep } from "./puzzle-creation/PuzzleWizardTagsStep.svelte";
2
3
  export { default as PuzzlePgnBoardTreeEditor } from "./puzzle-creation/PuzzlePgnBoardTreeEditor.svelte";
3
4
  export type { TPuzzleCreationWizardProps, TPuzzlePgnBoardTreeEditorOutcome, TPuzzlePgnBoardTreeEditorProps, } from "./puzzle-creation/types.js";
4
- export type { TPuzzleCreatedPayload } from "./puzzle/puzzleCreatedPayload.js";
5
+ export type { TPuzzleWizardCoreState, TPuzzleWizardState, } from "./puzzle-creation/puzzleWizardState.js";
6
+ export { PUZZLE_WIZARD_CORE_STEPS, PUZZLE_WIZARD_TAGS_STEP, createInitialPuzzleWizardCoreState, createInitialPuzzleWizardState, puzzleWizardSeedFen, } from "./puzzle-creation/puzzleWizardState.js";
7
+ export type { TWizardStep, TWizardStepContext, } from "@connectorvol/shared";
8
+ export { Wizard, getWizardContext, setWizardContext } from "@connectorvol/shared";
9
+ export type { TPuzzleCreatedPayload, TPuzzleTags } from "./puzzle/puzzleCreatedPayload.js";
5
10
  export { puzzlePartsFromFullPgn } from "./puzzle/puzzleCreatedPayload.js";
6
11
  export type { PuzzleData } from "./puzzle/puzzleData.js";
7
12
  export { PUZZLE_EMPTY_BOARD_FEN, createInitialPuzzleData, createEmptyTreeFromFen, getMainLineFromTree, } from "./puzzle/puzzleData.js";
8
13
  export type { TSolverMoveOutcome } from "./puzzle/puzzleStepPreviewSolver.js";
9
14
  export { PREVIEW_WRONG_NO_VARIANT_MESSAGE } from "./puzzle/puzzlePreviewConstants.js";
15
+ export { validatePuzzleSolverForkAnnotations } from "./puzzle/puzzleSolverForkAnnotations.js";
16
+ export { normalizePuzzleTreeMainLine, syncPuzzleBranchNags, syncPuzzleBranchNagsOnTree, } from "./puzzle/syncPuzzleBranchNags.js";
10
17
  export { DEFAULT_EDITABLE_BOARD_SETTINGS } from "./constants/editable-board-settings.js";
18
+ export { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "./constants/default-board-appearance-settings.js";
11
19
  export { default as EditFen } from "./position-editor/EditFen.svelte";
12
20
  export { default as EditMove } from "./position-editor/EditMove.svelte";
13
21
  export { default as EditPanel } from "./position-editor/EditPanel.svelte";
package/dist/index.js CHANGED
@@ -1,9 +1,15 @@
1
1
  export { default as PuzzleCreationWizard } from "./puzzle-creation/PuzzleCreationWizard.svelte";
2
+ export { default as PuzzleWizardTagsStep } from "./puzzle-creation/PuzzleWizardTagsStep.svelte";
2
3
  export { default as PuzzlePgnBoardTreeEditor } from "./puzzle-creation/PuzzlePgnBoardTreeEditor.svelte";
4
+ export { PUZZLE_WIZARD_CORE_STEPS, PUZZLE_WIZARD_TAGS_STEP, createInitialPuzzleWizardCoreState, createInitialPuzzleWizardState, puzzleWizardSeedFen, } from "./puzzle-creation/puzzleWizardState.js";
5
+ export { Wizard, getWizardContext, setWizardContext } from "@connectorvol/shared";
3
6
  export { puzzlePartsFromFullPgn } from "./puzzle/puzzleCreatedPayload.js";
4
7
  export { PUZZLE_EMPTY_BOARD_FEN, createInitialPuzzleData, createEmptyTreeFromFen, getMainLineFromTree, } from "./puzzle/puzzleData.js";
5
8
  export { PREVIEW_WRONG_NO_VARIANT_MESSAGE } from "./puzzle/puzzlePreviewConstants.js";
9
+ export { validatePuzzleSolverForkAnnotations } from "./puzzle/puzzleSolverForkAnnotations.js";
10
+ export { normalizePuzzleTreeMainLine, syncPuzzleBranchNags, syncPuzzleBranchNagsOnTree, } from "./puzzle/syncPuzzleBranchNags.js";
6
11
  export { DEFAULT_EDITABLE_BOARD_SETTINGS } from "./constants/editable-board-settings.js";
12
+ export { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "./constants/default-board-appearance-settings.js";
7
13
  export { default as EditFen } from "./position-editor/EditFen.svelte";
8
14
  export { default as EditMove } from "./position-editor/EditMove.svelte";
9
15
  export { default as EditPanel } from "./position-editor/EditPanel.svelte";
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { BoardApi } from "@connectorvol/chessboard";
3
+ import { CHESSBOARD_PIECE_SETS } from "@connectorvol/chessboard";
3
4
 
4
5
  interface Props {
5
6
  color: "w" | "b";
@@ -8,6 +9,8 @@
8
9
 
9
10
  const { color, api }: Props = $props();
10
11
 
12
+ const pieceSet = $derived(CHESSBOARD_PIECE_SETS[api.chessSet]);
13
+
11
14
  function onSelect(
12
15
  promotion: "q" | "r" | "b" | "n" | "k" | "p" | "delete" | "drag"
13
16
  ) {
@@ -78,7 +81,7 @@
78
81
  class="flex aspect-square w-1/8"
79
82
  onclick={() => onSelect("p")}
80
83
  >
81
- {@render api.chessSet?.pawn(color === "w" ? "w" : "b")}
84
+ {@render pieceSet.pawn(color === "w" ? "w" : "b")}
82
85
  </button>
83
86
  <button
84
87
  data-testid="edit-panel-knight-{color}"
@@ -89,7 +92,7 @@
89
92
  class="flex aspect-square w-1/8"
90
93
  onclick={() => onSelect("n")}
91
94
  >
92
- {@render api.chessSet?.knight(color)}
95
+ {@render pieceSet.knight(color)}
93
96
  </button>
94
97
  <button
95
98
  data-testid="edit-panel-bishop-{color}"
@@ -100,7 +103,7 @@
100
103
  class="flex aspect-square w-1/8"
101
104
  onclick={() => onSelect("b")}
102
105
  >
103
- {@render api.chessSet?.bishop(color)}
106
+ {@render pieceSet.bishop(color)}
104
107
  </button>
105
108
  <button
106
109
  data-testid="edit-panel-rook-{color}"
@@ -111,7 +114,7 @@
111
114
  class="flex aspect-square w-1/8"
112
115
  onclick={() => onSelect("r")}
113
116
  >
114
- {@render api.chessSet?.rook(color)}
117
+ {@render pieceSet.rook(color)}
115
118
  </button>
116
119
  <button
117
120
  data-testid="edit-panel-queen-{color}"
@@ -122,7 +125,7 @@
122
125
  class="flex aspect-square w-1/8"
123
126
  onclick={() => onSelect("q")}
124
127
  >
125
- {@render api.chessSet?.queen(color)}
128
+ {@render pieceSet.queen(color)}
126
129
  </button>
127
130
  <button
128
131
  data-testid="edit-panel-king-{color}"
@@ -133,7 +136,7 @@
133
136
  class="flex aspect-square w-1/8"
134
137
  onclick={() => onSelect("k")}
135
138
  >
136
- {@render api.chessSet?.king(color)}
139
+ {@render pieceSet.king(color)}
137
140
  </button>
138
141
  <button
139
142
  aria-label="Delete piece"
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Представляет выбранные теги задачи (дебют и тактические приёмы).
3
+ */
4
+ export interface TPuzzleTags {
5
+ /**
6
+ * Возвращает идентификатор выбранного дебютного тега или `undefined`, если дебют не выбран.
7
+ */
8
+ opening?: string;
9
+ /**
10
+ * Возвращает идентификаторы выбранных тактических тегов (не более трёх).
11
+ */
12
+ tactics: string[];
13
+ }
1
14
  /**
2
15
  * Представляет данные задачи, передаваемые колбэку после завершения мастера.
3
16
  */
@@ -10,6 +23,10 @@ export interface TPuzzleCreatedPayload {
10
23
  * Возвращает начальный FEN задачи (из тега FEN или начальная позиция по умолчанию).
11
24
  */
12
25
  fen: string;
26
+ /**
27
+ * Возвращает выбранные теги задачи, если в мастере включён шаг тегов.
28
+ */
29
+ tags?: TPuzzleTags;
13
30
  }
14
31
  /**
15
32
  * Представляет разбор полного PGN из дерева задачи на FEN и чистую строку ходов.
@@ -25,6 +25,10 @@ export declare function createInitialPuzzleData(initialFen?: string): PuzzleData
25
25
  * Возвращает корень дерева (TChessTree) для использования в ChessTree.
26
26
  */
27
27
  export declare function createEmptyTreeFromFen(initialFen: string): TChessTree;
28
+ /**
29
+ * Представляет создание дерева превью для ученика с опциональным вступительным комментарием PGN до первого хода.
30
+ */
31
+ export declare function createStudentPreviewTreeFromFen(initialFen: string, introComments?: string[]): TChessTree;
28
32
  /**
29
33
  * Представляет извлечение главной линии (первого варианта) из дерева ходов.
30
34
  * Возвращает массив ходов в нотации SAN.
@@ -35,6 +35,16 @@ export function createEmptyTreeFromFen(initialFen) {
35
35
  headers,
36
36
  };
37
37
  }
38
+ /**
39
+ * Представляет создание дерева превью для ученика с опциональным вступительным комментарием PGN до первого хода.
40
+ */
41
+ export function createStudentPreviewTreeFromFen(initialFen, introComments) {
42
+ const tree = createEmptyTreeFromFen(initialFen);
43
+ if (introComments !== undefined && introComments.length > 0) {
44
+ tree.comments = [...introComments];
45
+ }
46
+ return tree;
47
+ }
38
48
  /**
39
49
  * Представляет извлечение главной линии (первого варианта) из дерева ходов.
40
50
  * Возвращает массив ходов в нотации SAN.
@@ -2,3 +2,7 @@
2
2
  * Представляет текст сообщения «нет такого продолжения» для превью задачи (как на шаге 3).
3
3
  */
4
4
  export declare const PREVIEW_WRONG_NO_VARIANT_MESSAGE = "\u0422\u0430\u043A\u043E\u0433\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u044F \u043D\u0435\u0442 \u0441\u0440\u0435\u0434\u0438 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447\u0438.";
5
+ /**
6
+ * Представляет задержку (мс) перед откатом ошибочного хода на доске после показа NAG ✗.
7
+ */
8
+ export declare const PREVIEW_WRONG_MOVE_REVEAL_MS = 700;
@@ -2,3 +2,7 @@
2
2
  * Представляет текст сообщения «нет такого продолжения» для превью задачи (как на шаге 3).
3
3
  */
4
4
  export const PREVIEW_WRONG_NO_VARIANT_MESSAGE = "Такого продолжения нет среди вариантов задачи.";
5
+ /**
6
+ * Представляет задержку (мс) перед откатом ошибочного хода на доске после показа NAG ✗.
7
+ */
8
+ export const PREVIEW_WRONG_MOVE_REVEAL_MS = 700;
@@ -0,0 +1,6 @@
1
+ import type { ChessTree } from "@connectorvol/tree";
2
+ import type { Color } from "@connectorvol/shared";
3
+ /**
4
+ * Представляет синхронизацию NAG ✓ ходов решателя в дереве решения по пройденному пути превью.
5
+ */
6
+ export declare function applyPlayedSolverNagsAlongPath(solution: ChessTree, path: number[], solverColor: Color): boolean;
@@ -0,0 +1,35 @@
1
+ import { PUZZLE_BRANCH_CORRECT_NAG_ID, PUZZLE_BRANCH_WRONG_NAG_ID, } from "@connectorvol/shared";
2
+ import { puzzlePreviewSideToMoveFromFen } from "./puzzleStepPreviewSolver.js";
3
+ /**
4
+ * Представляет синхронизацию NAG ✓ ходов решателя в дереве решения по пройденному пути превью.
5
+ */
6
+ export function applyPlayedSolverNagsAlongPath(solution, path, solverColor) {
7
+ let src = solution.rootNode.moves;
8
+ let changed = false;
9
+ for (const idx of path) {
10
+ const next = src.children[idx];
11
+ if (!next)
12
+ break;
13
+ const mover = puzzlePreviewSideToMoveFromFen(src.data.fen);
14
+ const isSolverMove = mover === solverColor;
15
+ if (isSolverMove) {
16
+ let nags = next.data.nags ? [...next.data.nags] : [];
17
+ if (!nags.includes(PUZZLE_BRANCH_CORRECT_NAG_ID)) {
18
+ nags.push(PUZZLE_BRANCH_CORRECT_NAG_ID);
19
+ }
20
+ nags = nags.filter((n) => n !== PUZZLE_BRANCH_WRONG_NAG_ID);
21
+ const normalizedNags = nags.length > 0 ? nags : undefined;
22
+ const prevKey = JSON.stringify(next.data.nags ?? []);
23
+ const nextKey = JSON.stringify(normalizedNags ?? []);
24
+ if (prevKey !== nextKey) {
25
+ next.data.nags = normalizedNags ? [...normalizedNags] : undefined;
26
+ changed = true;
27
+ }
28
+ }
29
+ src = next;
30
+ }
31
+ if (changed) {
32
+ solution.mutationVersion++;
33
+ }
34
+ return changed;
35
+ }
@@ -1,10 +1,8 @@
1
1
  import type { ChessTree } from "@connectorvol/tree";
2
2
  import { Color } from "@connectorvol/shared";
3
3
  /**
4
- * Представляет проверку линии решения задачи: метки ✓/✗ только на ходах из развилки,
5
- * запрет ✓/✗ ниже по дереву после ✗ на ходе решателя, разметка развилок со стороны
6
- * решателя и окончание вариантов на развилке соперника ходом решателя вне поддерева
7
- * после хода решателя с меткой «неверное решение».
4
+ * Представляет проверку линии решения задачи: разметка развилок со стороны решателя
5
+ * и окончание вариантов на развилке соперника ходом решателя.
8
6
  */
9
7
  export declare function validatePuzzleSolverForkAnnotations(tree: ChessTree, solverColor: Color): {
10
8
  ok: true;