@connectorvol/chess-widgets 1.0.0 → 1.2.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/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/position-editor/EditMove.svelte +164 -158
- package/dist/puzzle/mergePgnWithSetupFen.d.ts +8 -0
- package/dist/puzzle/mergePgnWithSetupFen.js +28 -0
- package/dist/puzzle/puzzlePreviewConstants.d.ts +4 -0
- package/dist/puzzle/puzzlePreviewConstants.js +4 -0
- package/dist/puzzle/puzzleStepPreviewSolver.d.ts +1 -1
- package/dist/puzzle/puzzleStepPreviewSolver.js +4 -1
- package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte +106 -0
- package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte.d.ts +51 -0
- package/dist/puzzle-creation/PuzzleCreationWizard.svelte +197 -232
- package/dist/puzzle-creation/PuzzlePgnBoardTreeEditor.svelte +545 -0
- package/dist/puzzle-creation/PuzzlePgnBoardTreeEditor.svelte.d.ts +5 -0
- package/dist/puzzle-creation/StepMoves.svelte +14 -40
- package/dist/puzzle-creation/StepMoves.svelte.d.ts +1 -1
- package/dist/puzzle-creation/StepPosition.svelte +196 -190
- package/dist/puzzle-creation/StepPreview.svelte +14 -438
- package/dist/puzzle-creation/StepPreview.svelte.d.ts +1 -1
- package/dist/puzzle-creation/createPuzzleLineEditingBoard.d.ts +16 -0
- package/dist/puzzle-creation/createPuzzleLineEditingBoard.js +31 -0
- package/dist/puzzle-creation/types.d.ts +42 -0
- package/package.json +11 -7
|
@@ -1,247 +1,212 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
|
|
2
|
+
export type { TPuzzleCreationWizardProps } from "./types.js";
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
puzzleData.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let chess = $derived(new PgnOps(puzzleData.initialFen, "chess"));
|
|
75
|
-
let tree = $derived(
|
|
76
|
-
new ChessTree(createEmptyTreeFromFen(puzzleData.initialFen)),
|
|
77
|
-
);
|
|
78
|
-
const addMoveToTree = (san: string) => {
|
|
79
|
-
const move = chess.makeSanMove(san);
|
|
80
|
-
if (!move) return;
|
|
81
|
-
const fen = chess.fen();
|
|
82
|
-
const { halfMoves, fullMoves } = calculatePly(fen);
|
|
83
|
-
tree.addNodeToCurrent({
|
|
84
|
-
id: "",
|
|
85
|
-
children: [],
|
|
86
|
-
data: {
|
|
87
|
-
fen,
|
|
88
|
-
san,
|
|
89
|
-
ply: halfMoves,
|
|
90
|
-
fullMoves,
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
return { move, fen, turn: chess.turn() };
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const actions: IChessBoardActions = {
|
|
97
|
-
game: {
|
|
98
|
-
possibleMovesOnSquare: (square: Square) => chess.moves(square),
|
|
99
|
-
beforePieceMoveSan(san: string) {
|
|
100
|
-
const result = addMoveToTree(san);
|
|
101
|
-
return result;
|
|
102
|
-
},
|
|
103
|
-
afterPieceMoveSan: () => {},
|
|
104
|
-
beforePieceMove: (from, to, promotion) => {
|
|
105
|
-
const san = chess.getSanForMove({ from, to, promotion });
|
|
106
|
-
chess.makeMove({ from, to, promotion });
|
|
6
|
+
import { untrack } from "svelte";
|
|
7
|
+
|
|
8
|
+
import StepMoves from "./StepMoves.svelte";
|
|
9
|
+
import StepPosition from "./StepPosition.svelte";
|
|
10
|
+
import StepPreview from "./StepPreview.svelte";
|
|
11
|
+
import type { TPuzzleCreationWizardProps } from "./types.js";
|
|
12
|
+
import {
|
|
13
|
+
createEmptyTreeFromFen,
|
|
14
|
+
createInitialPuzzleData,
|
|
15
|
+
type PuzzleData,
|
|
16
|
+
} from "../puzzle/puzzleData.js";
|
|
17
|
+
import { PgnOps } from "@connectorvol/chessops/pgnOps.svelte";
|
|
18
|
+
import { ChessTree, createPgnFromTree } from "@connectorvol/tree";
|
|
19
|
+
import {
|
|
20
|
+
CHESSBOARD_THEMES,
|
|
21
|
+
Draggable,
|
|
22
|
+
type IChessBoardActions,
|
|
23
|
+
} from "@connectorvol/chessboard";
|
|
24
|
+
import { calculatePly, Color, Square } from "@connectorvol/shared";
|
|
25
|
+
|
|
26
|
+
import { createPuzzleLineEditingBoardApi } from "./createPuzzleLineEditingBoard.js";
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
onPuzzleCreated,
|
|
30
|
+
fen: fenProp,
|
|
31
|
+
boardTheme,
|
|
32
|
+
boardAppearanceSettings,
|
|
33
|
+
}: TPuzzleCreationWizardProps = $props();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Представляет нормализацию входного FEN из пропса (пустая строка трактуется как отсутствие значения).
|
|
37
|
+
*/
|
|
38
|
+
function wizardSeedFen(prop: string | undefined): string | undefined {
|
|
39
|
+
const t = prop?.trim();
|
|
40
|
+
return t ? t : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let step = $state<1 | 2 | 3>(1);
|
|
44
|
+
let puzzleData = $state<PuzzleData>(
|
|
45
|
+
createInitialPuzzleData(wizardSeedFen(fenProp)),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/** Возвращает PGN дерева решения для превью на шаге 3. */
|
|
49
|
+
let solutionPgnForPreview = $state("");
|
|
50
|
+
|
|
51
|
+
function handleMovesBack() {
|
|
52
|
+
step = 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleMovesNext(moves: string[]) {
|
|
56
|
+
puzzleData.moves = moves;
|
|
57
|
+
solutionPgnForPreview = createPgnFromTree(tree.rootNode);
|
|
58
|
+
step = 3;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function handlePreviewBack() {
|
|
62
|
+
step = 2;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let chess = $derived(new PgnOps(puzzleData.initialFen, "chess"));
|
|
66
|
+
let tree = $derived(
|
|
67
|
+
new ChessTree(createEmptyTreeFromFen(puzzleData.initialFen)),
|
|
68
|
+
);
|
|
69
|
+
const addMoveToTree = (san: string) => {
|
|
70
|
+
const move = chess.makeSanMove(san);
|
|
71
|
+
if (!move) return;
|
|
107
72
|
const fen = chess.fen();
|
|
108
73
|
const { halfMoves, fullMoves } = calculatePly(fen);
|
|
109
74
|
tree.addNodeToCurrent({
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
75
|
+
id: "",
|
|
76
|
+
children: [],
|
|
77
|
+
data: {
|
|
78
|
+
fen,
|
|
79
|
+
san,
|
|
80
|
+
ply: halfMoves,
|
|
81
|
+
fullMoves,
|
|
82
|
+
},
|
|
118
83
|
});
|
|
119
|
-
return fen;
|
|
120
|
-
},
|
|
121
|
-
afterPieceMove: () => {
|
|
122
|
-
const side = chess.turn();
|
|
123
|
-
chessboard.draggable =
|
|
124
|
-
side === Color.WHITE ? Draggable.WHITE : Draggable.BLACK;
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Представляет сборку доски для шага построения линии: позиция и ориентация по стороне хода в FEN.
|
|
131
|
-
*/
|
|
132
|
-
function createMainChessboard(fullFen: string) {
|
|
133
|
-
const side = sideToMoveFromFullFen(fullFen);
|
|
134
|
-
const {
|
|
135
|
-
boardSize: _boardSize,
|
|
136
|
-
orientation: _orientation,
|
|
137
|
-
draggable: _draggable,
|
|
138
|
-
...appearanceSettings
|
|
139
|
-
} = (boardAppearanceSettings ?? {}) as Record<string, unknown> & {
|
|
140
|
-
boardSize?: unknown;
|
|
141
|
-
orientation?: unknown;
|
|
142
|
-
draggable?: unknown;
|
|
84
|
+
return { move, fen, turn: chess.turn() };
|
|
143
85
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
86
|
+
|
|
87
|
+
const actions: IChessBoardActions = {
|
|
88
|
+
game: {
|
|
89
|
+
possibleMovesOnSquare: (square: Square) => chess.moves(square),
|
|
90
|
+
beforePieceMoveSan(san: string) {
|
|
91
|
+
const result = addMoveToTree(san);
|
|
92
|
+
return result;
|
|
93
|
+
},
|
|
94
|
+
afterPieceMoveSan: () => {},
|
|
95
|
+
beforePieceMove: (from, to, promotion) => {
|
|
96
|
+
const san = chess.getSanForMove({ from, to, promotion });
|
|
97
|
+
chess.makeMove({ from, to, promotion });
|
|
98
|
+
const fen = chess.fen();
|
|
99
|
+
const { halfMoves, fullMoves } = calculatePly(fen);
|
|
100
|
+
tree.addNodeToCurrent({
|
|
101
|
+
id: "",
|
|
102
|
+
children: [],
|
|
103
|
+
data: {
|
|
104
|
+
fen,
|
|
105
|
+
san,
|
|
106
|
+
ply: halfMoves,
|
|
107
|
+
fullMoves,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
return fen;
|
|
111
|
+
},
|
|
112
|
+
afterPieceMove: () => {
|
|
113
|
+
const side = chess.turn();
|
|
114
|
+
chessboard.draggable =
|
|
115
|
+
side === Color.WHITE ? Draggable.WHITE : Draggable.BLACK;
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Представляет сборку доски для шага построения линии: позиция и ориентация по стороне хода в FEN.
|
|
122
|
+
*/
|
|
123
|
+
function createMainChessboard(fullFen: string) {
|
|
124
|
+
return createPuzzleLineEditingBoardApi(fullFen, actions, {
|
|
125
|
+
boardTheme: boardTheme ?? CHESSBOARD_THEMES.blue,
|
|
126
|
+
boardAppearanceSettings,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let chessboard = $derived(createMainChessboard(puzzleData.initialFen));
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Представляет сброс мастера при смене входного FEN извне (шаг 1 и черновик ходов обнуляются).
|
|
134
|
+
*/
|
|
135
|
+
$effect(() => {
|
|
136
|
+
const seed = wizardSeedFen(fenProp);
|
|
137
|
+
const next = createInitialPuzzleData(seed);
|
|
138
|
+
untrack(() => {
|
|
139
|
+
puzzleData = next;
|
|
140
|
+
step = 1;
|
|
141
|
+
solutionPgnForPreview = "";
|
|
142
|
+
});
|
|
174
143
|
});
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
tree = new ChessTree(createEmptyTreeFromFen(fen));
|
|
181
|
-
chessboard = createMainChessboard(fen);
|
|
182
|
-
step = 2;
|
|
183
|
-
}
|
|
144
|
+
|
|
145
|
+
function handlePositionNext(fen: string) {
|
|
146
|
+
puzzleData.initialFen = fen;
|
|
147
|
+
step = 2;
|
|
148
|
+
}
|
|
184
149
|
</script>
|
|
185
150
|
|
|
186
151
|
<div class="mx-4 lg:container lg:mx-auto">
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
152
|
+
<div class="flex flex-col gap-4 pt-4 pb-0">
|
|
153
|
+
<div class="flex items-center gap-2">
|
|
154
|
+
<span
|
|
155
|
+
class="flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium"
|
|
156
|
+
class:bg-primary={step >= 1}
|
|
157
|
+
class:bg-muted={step < 1}
|
|
158
|
+
class:text-primary-foreground={step >= 1}
|
|
159
|
+
>
|
|
160
|
+
1
|
|
161
|
+
</span>
|
|
162
|
+
<span class="text-sm">Позиция</span>
|
|
163
|
+
<span class="h-px flex-1 bg-border"></span>
|
|
164
|
+
<span
|
|
165
|
+
class="flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium"
|
|
166
|
+
class:bg-primary={step >= 2}
|
|
167
|
+
class:bg-muted={step < 2}
|
|
168
|
+
class:text-primary-foreground={step >= 2}
|
|
169
|
+
>
|
|
170
|
+
2
|
|
171
|
+
</span>
|
|
172
|
+
<span class="text-sm">Ходы</span>
|
|
173
|
+
<span class="h-px flex-1 bg-border"></span>
|
|
174
|
+
<span
|
|
175
|
+
class="flex h-8 w-8 items-center justify-center rounded-full text-sm font-medium"
|
|
176
|
+
class:bg-primary={step >= 3}
|
|
177
|
+
class:bg-muted={step < 3}
|
|
178
|
+
class:text-primary-foreground={step >= 3}
|
|
179
|
+
>
|
|
180
|
+
3
|
|
181
|
+
</span>
|
|
182
|
+
<span class="text-sm">Превью</span>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{#if step === 1}
|
|
186
|
+
<StepPosition
|
|
187
|
+
initialFen={puzzleData.initialFen}
|
|
188
|
+
onNext={handlePositionNext}
|
|
189
|
+
{boardTheme}
|
|
190
|
+
{boardAppearanceSettings}
|
|
191
|
+
/>
|
|
192
|
+
{:else if step === 2}
|
|
193
|
+
<StepMoves
|
|
194
|
+
{puzzleData}
|
|
195
|
+
{chess}
|
|
196
|
+
{tree}
|
|
197
|
+
{chessboard}
|
|
198
|
+
onBack={handleMovesBack}
|
|
199
|
+
onNext={handleMovesNext}
|
|
200
|
+
/>
|
|
201
|
+
{:else}
|
|
202
|
+
<StepPreview
|
|
203
|
+
{puzzleData}
|
|
204
|
+
solutionPgn={solutionPgnForPreview}
|
|
205
|
+
onBack={handlePreviewBack}
|
|
206
|
+
{onPuzzleCreated}
|
|
207
|
+
{boardTheme}
|
|
208
|
+
{boardAppearanceSettings}
|
|
209
|
+
/>
|
|
210
|
+
{/if}
|
|
218
211
|
</div>
|
|
219
|
-
|
|
220
|
-
{#if step === 1}
|
|
221
|
-
<StepPosition
|
|
222
|
-
initialFen={puzzleData.initialFen}
|
|
223
|
-
onNext={handlePositionNext}
|
|
224
|
-
{boardTheme}
|
|
225
|
-
boardAppearanceSettings={boardAppearanceSettings}
|
|
226
|
-
/>
|
|
227
|
-
{:else if step === 2}
|
|
228
|
-
<StepMoves
|
|
229
|
-
{puzzleData}
|
|
230
|
-
{chess}
|
|
231
|
-
{tree}
|
|
232
|
-
{chessboard}
|
|
233
|
-
onBack={handleMovesBack}
|
|
234
|
-
onNext={handleMovesNext}
|
|
235
|
-
/>
|
|
236
|
-
{:else}
|
|
237
|
-
<StepPreview
|
|
238
|
-
{puzzleData}
|
|
239
|
-
solutionPgn={solutionPgnForPreview}
|
|
240
|
-
onBack={handlePreviewBack}
|
|
241
|
-
{onPuzzleCreated}
|
|
242
|
-
{boardTheme}
|
|
243
|
-
boardAppearanceSettings={boardAppearanceSettings}
|
|
244
|
-
/>
|
|
245
|
-
{/if}
|
|
246
|
-
</div>
|
|
247
212
|
</div>
|