@connectorvol/chess-widgets 9.0.0 → 9.0.1

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.
@@ -11,8 +11,11 @@ interface Props {
11
11
  chessboard: BoardApi;
12
12
  /**
13
13
  * Возвращает снимок дизайна, передаваемый в `<Chessboard design={...} />`.
14
+ * Опционально: если не передан, доска читает дизайн из контекста
15
+ * (`setBoardDesignSettingsContext`), что позволяет реактивно подхватывать
16
+ * изменения из `<BoardSettings />`.
14
17
  */
15
- chessboardDesign: TChessBoardDesignSettings;
18
+ chessboardDesign?: TChessBoardDesignSettings;
16
19
  /**
17
20
  * Возвращает экземпляр дерева ходов для `TreeViewer`.
18
21
  */
@@ -47,8 +50,9 @@ interface Props {
47
50
  */
48
51
  showPuzzleBranchNags?: boolean;
49
52
  /**
50
- * Возвращает тему `TreeViewer` (фон, границы, текст). Если не задана —
51
- * используется `TREE_VIEWER_LIGHT_THEME`.
53
+ * Возвращает тему `TreeViewer` (фон, границы, текст). По умолчанию
54
+ * подстраивается под `mode-watcher` (dark → `TREE_VIEWER_DARK_THEME`,
55
+ * иначе `TREE_VIEWER_LIGHT_THEME`).
52
56
  */
53
57
  theme?: TTreeViewerTheme;
54
58
  /**
@@ -1,202 +1,206 @@
1
1
  <script lang="ts" module>
2
- export type { TPuzzleCreationWizardProps } from "./types.js";
3
- export type {
4
- TPuzzleWizardCoreState,
5
- TPuzzleWizardState,
6
- } from "./puzzleWizardState.js";
2
+ export type { TPuzzleCreationWizardProps } from "./types.js";
3
+ export type {
4
+ TPuzzleWizardCoreState,
5
+ TPuzzleWizardState,
6
+ } from "./puzzleWizardState.js";
7
7
  </script>
8
8
 
9
9
  <script lang="ts" generics="S extends TPuzzleWizardCoreState">
10
- import type { Snippet } from "svelte";
10
+ import type { Snippet } from "svelte";
11
11
 
12
- import { Wizard } from "@connectorvol/shared";
13
- import type { TWizardStep, TWizardStepContext } from "@connectorvol/shared";
14
- import StepMoves from "./StepMoves.svelte";
15
- import StepPosition from "./StepPosition.svelte";
16
- import StepPreview from "./StepPreview.svelte";
17
- import type { TPuzzleCreatedPayload } from "../puzzle/puzzleCreatedPayload.js";
18
- import type { TPuzzleWizardCoreState } from "./puzzleWizardState.js";
19
- import { PUZZLE_WIZARD_CORE_STEPS } from "./puzzleWizardState.js";
20
- import { createEmptyTreeFromFen } from "../puzzle/puzzleData.js";
21
- import { PgnOps } from "@connectorvol/chessops/pgnOps.svelte";
22
- import { ChessTree, createPgnFromTree } from "@connectorvol/tree";
23
- import {
24
- CHESSBOARD_THEMES,
25
- Draggable,
26
- type ChessboardTheme,
27
- type IChessBoardActions,
28
- } from "@connectorvol/chessboard";
29
- import { calculatePly, Color, Square } from "@connectorvol/shared";
12
+ import { Wizard } from "@connectorvol/shared";
13
+ import type { TWizardStep, TWizardStepContext } from "@connectorvol/shared";
14
+ import StepMoves from "./StepMoves.svelte";
15
+ import StepPosition from "./StepPosition.svelte";
16
+ import StepPreview from "./StepPreview.svelte";
17
+ import type { TPuzzleCreatedPayload } from "../puzzle/puzzleCreatedPayload.js";
18
+ import type { TPuzzleWizardCoreState } from "./puzzleWizardState.js";
19
+ import { PUZZLE_WIZARD_CORE_STEPS } from "./puzzleWizardState.js";
20
+ import { createEmptyTreeFromFen } from "../puzzle/puzzleData.js";
21
+ import { PgnOps } from "@connectorvol/chessops/pgnOps.svelte";
22
+ import { ChessTree, createPgnFromTree } from "@connectorvol/tree";
23
+ import {
24
+ CHESSBOARD_THEMES,
25
+ Draggable,
26
+ type ChessboardTheme,
27
+ type IChessBoardActions,
28
+ } from "@connectorvol/chessboard";
29
+ import { calculatePly, Color, Square } from "@connectorvol/shared";
30
30
 
31
- import { createPuzzleLineEditingBoardApi } from "./createPuzzleLineEditingBoard.js";
32
- import { syncPuzzleBranchNagsOnTree } from "../puzzle/syncPuzzleBranchNags.js";
33
- import { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "../constants/default-board-appearance-settings.js";
34
- import type { TChessboardAppearanceSettings } from "./types.js";
31
+ import { createPuzzleLineEditingBoardApi } from "./createPuzzleLineEditingBoard.js";
32
+ import { syncPuzzleBranchNagsOnTree } from "../puzzle/syncPuzzleBranchNags.js";
33
+ import { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "../constants/default-board-appearance-settings.js";
34
+ import type { TChessboardAppearanceSettings } from "./types.js";
35
+ import { derived } from "svelte/store";
35
36
 
36
- interface Props {
37
- /** Возвращает общий state визарда (двухсторонняя привязка). */
38
- wizardState: S;
39
- /** Возвращает колбэк завершения с шага превью без доп. шагов. */
40
- onPuzzleCreated: (payload: TPuzzleCreatedPayload) => void;
41
- /** Возвращает колбэк завершения с доп. шага (`wizard.done()`). */
42
- onDone?: (state: S) => void;
43
- /** Возвращает seed FEN для сброса шага визарда при изменении. */
44
- fen?: string;
45
- /** Возвращает тему оформления шахматной доски. */
46
- boardTheme?: ChessboardTheme;
47
- /** Возвращает визуальные настройки доски. */
48
- boardAppearanceSettings?: TChessboardAppearanceSettings;
49
- /** Возвращает дополнительные шаги после превью. */
50
- additionalSteps?: readonly TWizardStep[];
51
- /** Возвращает snippet дополнительных шагов. */
52
- additionalStep?: Snippet<[TWizardStepContext<S>]>;
53
- }
37
+ interface Props {
38
+ /** Возвращает общий state визарда (двухсторонняя привязка). */
39
+ wizardState: S;
40
+ /** Возвращает колбэк завершения с шага превью без доп. шагов. */
41
+ onPuzzleCreated: (payload: TPuzzleCreatedPayload) => void;
42
+ /** Возвращает колбэк завершения с доп. шага (`wizard.done()`). */
43
+ onDone?: (state: S) => void;
44
+ /** Возвращает seed FEN для сброса шага визарда при изменении. */
45
+ fen?: string;
46
+ /** Возвращает тему оформления шахматной доски. */
47
+ boardTheme?: ChessboardTheme;
48
+ /** Возвращает визуальные настройки доски. */
49
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
50
+ /** Возвращает дополнительные шаги после превью. */
51
+ additionalSteps?: readonly TWizardStep[];
52
+ /** Возвращает snippet дополнительных шагов. */
53
+ additionalStep?: Snippet<[TWizardStepContext<S>]>;
54
+ }
54
55
 
55
- let {
56
- wizardState = $bindable(),
57
- onPuzzleCreated,
58
- onDone,
59
- fen: fenProp,
60
- boardTheme,
61
- boardAppearanceSettings = DEFAULT_BOARD_APPEARANCE_SETTINGS,
62
- additionalSteps = [],
63
- additionalStep,
64
- }: Props = $props();
56
+ let {
57
+ wizardState = $bindable(),
58
+ onPuzzleCreated,
59
+ onDone,
60
+ fen: fenProp,
61
+ boardTheme,
62
+ boardAppearanceSettings = DEFAULT_BOARD_APPEARANCE_SETTINGS,
63
+ additionalSteps = [],
64
+ additionalStep,
65
+ }: Props = $props();
65
66
 
66
- const hasAdditionalSteps = $derived(additionalSteps.length > 0);
67
- const steps = $derived([...PUZZLE_WIZARD_CORE_STEPS, ...additionalSteps]);
67
+ const hasAdditionalSteps = $derived(additionalSteps.length > 0);
68
+ const steps = $derived([...PUZZLE_WIZARD_CORE_STEPS, ...additionalSteps]);
68
69
 
69
- let chess = $derived(
70
- new PgnOps(wizardState.puzzleData.initialFen, "chess"),
71
- );
72
- let tree = $derived(
73
- new ChessTree(createEmptyTreeFromFen(wizardState.puzzleData.initialFen)),
74
- );
75
-
76
- const addMoveToTree = (san: string) => {
77
- const move = chess.makeSanMove(san);
78
- if (!move) return;
79
- const fen = chess.fen();
80
- const { halfMoves, fullMoves } = calculatePly(fen);
81
- tree.addNodeToCurrent({
82
- id: "",
83
- children: [],
84
- data: {
85
- fen,
86
- san,
87
- ply: halfMoves,
88
- fullMoves,
89
- },
90
- });
91
- return { move, fen, turn: chess.turn() };
92
- };
70
+ let chess = $derived(
71
+ new PgnOps(wizardState.puzzleData.initialFen, "chess"),
72
+ );
73
+ let tree = $derived(
74
+ new ChessTree(
75
+ createEmptyTreeFromFen(wizardState.puzzleData.initialFen),
76
+ ),
77
+ );
93
78
 
94
- const actions: IChessBoardActions = {
95
- game: {
96
- possibleMovesOnSquare: (square: Square) => chess.moves(square),
97
- beforePieceMoveSan(san: string) {
98
- const result = addMoveToTree(san);
99
- return result;
100
- },
101
- afterPieceMoveSan: () => {},
102
- beforePieceMove: (from, to, promotion) => {
103
- const san = chess.getSanForMove({ from, to, promotion });
104
- chess.makeMove({ from, to, promotion });
79
+ const addMoveToTree = (san: string) => {
80
+ const move = chess.makeSanMove(san);
81
+ if (!move) return;
105
82
  const fen = chess.fen();
106
83
  const { halfMoves, fullMoves } = calculatePly(fen);
107
84
  tree.addNodeToCurrent({
108
- id: "",
109
- children: [],
110
- data: {
111
- fen,
112
- san,
113
- ply: halfMoves,
114
- fullMoves,
115
- },
85
+ id: "",
86
+ children: [],
87
+ data: {
88
+ fen,
89
+ san,
90
+ ply: halfMoves,
91
+ fullMoves,
92
+ },
116
93
  });
117
- return fen;
118
- },
119
- afterPieceMove: () => {
120
- const side = chess.turn();
121
- chessboardApi.draggable =
122
- side === Color.WHITE ? Draggable.WHITE : Draggable.BLACK;
123
- },
124
- },
125
- };
94
+ return { move, fen, turn: chess.turn() };
95
+ };
126
96
 
127
- /**
128
- * Представляет сборку доски для шага построения линии: позиция и ориентация по стороне хода в FEN.
129
- */
130
- function createMainChessboard(fullFen: string) {
131
- return createPuzzleLineEditingBoardApi(fullFen, actions, {
132
- boardTheme: boardTheme ?? CHESSBOARD_THEMES.blue,
133
- boardAppearanceSettings,
134
- });
135
- }
97
+ const actions: IChessBoardActions = {
98
+ game: {
99
+ possibleMovesOnSquare: (square: Square) => chess.moves(square),
100
+ beforePieceMoveSan(san: string) {
101
+ const result = addMoveToTree(san);
102
+ return result;
103
+ },
104
+ afterPieceMoveSan: () => {},
105
+ beforePieceMove: (from, to, promotion) => {
106
+ const san = chess.getSanForMove({ from, to, promotion });
107
+ chess.makeMove({ from, to, promotion });
108
+ const fen = chess.fen();
109
+ const { halfMoves, fullMoves } = calculatePly(fen);
110
+ tree.addNodeToCurrent({
111
+ id: "",
112
+ children: [],
113
+ data: {
114
+ fen,
115
+ san,
116
+ ply: halfMoves,
117
+ fullMoves,
118
+ },
119
+ });
120
+ return fen;
121
+ },
122
+ afterPieceMove: () => {
123
+ const side = chess.turn();
124
+ chessboardApi.draggable =
125
+ side === Color.WHITE ? Draggable.WHITE : Draggable.BLACK;
126
+ },
127
+ },
128
+ };
136
129
 
137
- let chessboard = $derived(
138
- createMainChessboard(wizardState.puzzleData.initialFen),
139
- );
140
- let chessboardApi = $derived(chessboard.api);
141
- let chessboardDesign = $derived(chessboard.design);
130
+ /**
131
+ * Представляет сборку доски для шага построения линии: позиция и ориентация по стороне хода в FEN.
132
+ */
133
+ function createMainChessboard(fullFen: string) {
134
+ return createPuzzleLineEditingBoardApi(fullFen, actions, {
135
+ boardTheme: boardTheme ?? CHESSBOARD_THEMES.blue,
136
+ boardAppearanceSettings,
137
+ });
138
+ }
142
139
 
143
- /**
144
- * Представляет переход со шага построения линии на превью.
145
- */
146
- function handleMovesNext(moves: string[]) {
147
- wizardState.puzzleData.moves = moves;
148
- syncPuzzleBranchNagsOnTree(
149
- tree,
150
- wizardState.puzzleData.initialFen.trim().split(/\s+/)[1] === "b"
151
- ? Color.BLACK
152
- : Color.WHITE,
140
+ let chessboard = $derived(
141
+ createMainChessboard(wizardState.puzzleData.initialFen),
153
142
  );
154
- wizardState.solutionPgn = createPgnFromTree(tree.rootNode);
155
- }
143
+ let chessboardApi = $derived(chessboard.api);
144
+
145
+ // ponytail: компонент только читает контекст дизайна. Дизайн для доски шага 2,
146
+ // который раньше поднимался через `setBoardDesignSettingsContext`, теперь
147
+ // пробрасывается явно пропом `chessboardDesign` в `<StepMoves />`.
148
+ const chessboardDesign = $derived(chessboard.design);
149
+
150
+ /**
151
+ * Представляет переход со шага построения линии на превью.
152
+ */
153
+ function handleMovesNext(moves: string[]) {
154
+ wizardState.puzzleData.moves = moves;
155
+ syncPuzzleBranchNagsOnTree(
156
+ tree,
157
+ wizardState.puzzleData.initialFen.trim().split(/\s+/)[1] === "b"
158
+ ? Color.BLACK
159
+ : Color.WHITE,
160
+ );
161
+ wizardState.solutionPgn = createPgnFromTree(tree.rootNode);
162
+ }
156
163
  </script>
157
164
 
158
- <Wizard
159
- bind:wizardState={wizardState}
160
- {steps}
161
- resetSignal={fenProp}
162
- {onDone}
163
- >
164
- {#snippet children(w)}
165
- {#if w.currentStep === 1}
166
- <StepPosition
167
- initialFen={w.state.puzzleData.initialFen}
168
- onNext={(fen) => {
169
- w.state.puzzleData.initialFen = fen;
170
- w.next();
171
- }}
172
- {boardTheme}
173
- {boardAppearanceSettings}
174
- />
175
- {:else if w.currentStep === 2}
176
- <StepMoves
177
- puzzleData={w.state.puzzleData}
178
- {chess}
179
- {tree}
180
- chessboard={chessboardApi}
181
- {chessboardDesign}
182
- onBack={w.back}
183
- onNext={(moves) => {
184
- handleMovesNext(moves);
185
- w.next();
186
- }}
187
- />
188
- {:else if w.currentStep === 3}
189
- <StepPreview
190
- puzzleData={w.state.puzzleData}
191
- solutionPgn={w.state.solutionPgn}
192
- onBack={w.back}
193
- onNext={hasAdditionalSteps ? w.next : undefined}
194
- onPuzzleCreated={hasAdditionalSteps ? undefined : onPuzzleCreated}
195
- {boardTheme}
196
- {boardAppearanceSettings}
197
- />
198
- {:else if additionalStep}
199
- {@render additionalStep(w)}
200
- {/if}
201
- {/snippet}
165
+ <Wizard bind:wizardState {steps} resetSignal={fenProp} {onDone}>
166
+ {#snippet children(w)}
167
+ {#if w.currentStep === 1}
168
+ <StepPosition
169
+ initialFen={w.state.puzzleData.initialFen}
170
+ onNext={(fen) => {
171
+ w.state.puzzleData.initialFen = fen;
172
+ w.next();
173
+ }}
174
+ {boardTheme}
175
+ {boardAppearanceSettings}
176
+ />
177
+ {:else if w.currentStep === 2}
178
+ <StepMoves
179
+ puzzleData={w.state.puzzleData}
180
+ {chess}
181
+ {tree}
182
+ chessboard={chessboardApi}
183
+ {chessboardDesign}
184
+ onBack={w.back}
185
+ onNext={(moves) => {
186
+ handleMovesNext(moves);
187
+ w.next();
188
+ }}
189
+ />
190
+ {:else if w.currentStep === 3}
191
+ <StepPreview
192
+ puzzleData={w.state.puzzleData}
193
+ solutionPgn={w.state.solutionPgn}
194
+ onBack={w.back}
195
+ onNext={hasAdditionalSteps ? w.next : undefined}
196
+ onPuzzleCreated={hasAdditionalSteps
197
+ ? undefined
198
+ : onPuzzleCreated}
199
+ {boardTheme}
200
+ {boardAppearanceSettings}
201
+ />
202
+ {:else if additionalStep}
203
+ {@render additionalStep(w)}
204
+ {/if}
205
+ {/snippet}
202
206
  </Wizard>