@connectorvol/chess-widgets 8.0.1 → 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.
Files changed (48) hide show
  1. package/dist/(components)/BoardSettingsTrigger.svelte +10 -0
  2. package/dist/(components)/BoardSettingsTrigger.svelte.d.ts +18 -0
  3. package/dist/constants/default-board-appearance-settings.d.ts +9 -0
  4. package/dist/constants/default-board-appearance-settings.js +30 -0
  5. package/dist/constants/editable-board-settings.d.ts +3 -21
  6. package/dist/constants/editable-board-settings.js +24 -19
  7. package/dist/game-analyzer/GameAnalyzer.svelte +28 -10
  8. package/dist/game-analyzer/gameAnalyzer.svelte.js +6 -9
  9. package/dist/game-analyzer/types.d.ts +13 -8
  10. package/dist/index.d.ts +9 -1
  11. package/dist/index.js +6 -0
  12. package/dist/position-editor/EditPanel.svelte +9 -6
  13. package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
  14. package/dist/puzzle/puzzleData.d.ts +4 -0
  15. package/dist/puzzle/puzzleData.js +10 -0
  16. package/dist/puzzle/puzzlePreviewConstants.d.ts +4 -0
  17. package/dist/puzzle/puzzlePreviewConstants.js +4 -0
  18. package/dist/puzzle/puzzlePreviewPathNags.d.ts +6 -0
  19. package/dist/puzzle/puzzlePreviewPathNags.js +35 -0
  20. package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +2 -4
  21. package/dist/puzzle/puzzleSolverForkAnnotations.js +13 -22
  22. package/dist/puzzle/puzzleStepPreviewSolver.d.ts +13 -1
  23. package/dist/puzzle/puzzleStepPreviewSolver.js +69 -9
  24. package/dist/puzzle/syncPuzzleBranchNags.d.ts +14 -0
  25. package/dist/puzzle/syncPuzzleBranchNags.js +125 -0
  26. package/dist/puzzle-creation/OpeningTagHoverPreview.svelte +81 -0
  27. package/dist/puzzle-creation/OpeningTagHoverPreview.svelte.d.ts +11 -0
  28. package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte +198 -98
  29. package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte.d.ts +20 -2
  30. package/dist/puzzle-creation/PuzzleCreationWizard.svelte +100 -106
  31. package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +47 -3
  32. package/dist/puzzle-creation/PuzzlePgnBoardTreeEditor.svelte +945 -498
  33. package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte +36 -0
  34. package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte.d.ts +12 -0
  35. package/dist/puzzle-creation/StepMoves.svelte +210 -188
  36. package/dist/puzzle-creation/StepPosition.svelte +35 -9
  37. package/dist/puzzle-creation/StepPreview.svelte +24 -11
  38. package/dist/puzzle-creation/StepPreview.svelte.d.ts +3 -1
  39. package/dist/puzzle-creation/StepTags.svelte +270 -0
  40. package/dist/puzzle-creation/StepTags.svelte.d.ts +19 -0
  41. package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.d.ts +9 -0
  42. package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.js +19 -0
  43. package/dist/puzzle-creation/createPuzzleLineEditingBoard.d.ts +10 -3
  44. package/dist/puzzle-creation/createPuzzleLineEditingBoard.js +14 -9
  45. package/dist/puzzle-creation/puzzleWizardState.d.ts +35 -0
  46. package/dist/puzzle-creation/puzzleWizardState.js +37 -0
  47. package/dist/puzzle-creation/types.d.ts +36 -10
  48. package/package.json +22 -17
@@ -0,0 +1,270 @@
1
+ <script lang="ts">
2
+ import { Popover } from "bits-ui";
3
+
4
+ import { buttonVariants } from "../button-variants.js";
5
+ import { cn } from "../utils.js";
6
+ import type { TPuzzleTags } from "../puzzle/puzzleCreatedPayload.js";
7
+ import type {
8
+ TPuzzleOpeningTagOption,
9
+ TPuzzleTagOption,
10
+ } from "@connectorvol/shared";
11
+ import OpeningTagHoverPreview from "./OpeningTagHoverPreview.svelte";
12
+
13
+ interface Props {
14
+ /** Возвращает текущие выбранные теги (двухсторонняя привязка). */
15
+ tags: TPuzzleTags;
16
+ /** Возвращает каталог дебютов для выбора одного тега. */
17
+ openingTags: readonly TPuzzleOpeningTagOption[];
18
+ /** Возвращает каталог тактических приёмов для выбора до `tacticTagsMax` тегов. */
19
+ tacticTags: readonly TPuzzleTagOption[];
20
+ /** Возвращает максимальное число выбираемых тактических тегов. */
21
+ tacticTagsMax: number;
22
+ /** Возвращает переход назад на шаг превью. */
23
+ onBack: () => void;
24
+ /** Возвращает завершение мастера с выбранными тегами. */
25
+ onDone: (tags: TPuzzleTags) => void;
26
+ }
27
+
28
+ type TOpeningPreviewState = {
29
+ tag: TPuzzleOpeningTagOption;
30
+ anchor: DOMRect;
31
+ };
32
+
33
+ let {
34
+ tags = $bindable(),
35
+ openingTags,
36
+ tacticTags,
37
+ tacticTagsMax,
38
+ onBack,
39
+ onDone,
40
+ }: Props = $props();
41
+
42
+ let openingFilter = $state("");
43
+ let tacticFilter = $state("");
44
+ let hoveredOpening = $state<TOpeningPreviewState | null>(null);
45
+
46
+ /**
47
+ * Представляет показ превью дебюта при наведении на тег.
48
+ */
49
+ function handleOpeningPointerEnter(
50
+ event: { currentTarget: EventTarget | null },
51
+ tag: TPuzzleOpeningTagOption,
52
+ ) {
53
+ const anchor = (event.currentTarget as HTMLElement).getBoundingClientRect();
54
+ hoveredOpening = { tag, anchor };
55
+ }
56
+
57
+ /**
58
+ * Представляет скрытие превью дебюта.
59
+ */
60
+ function handleOpeningPointerLeave() {
61
+ hoveredOpening = null;
62
+ }
63
+
64
+ const filteredOpenings = $derived(
65
+ openingTags.filter((tag) =>
66
+ tag.label.toLowerCase().includes(openingFilter.trim().toLowerCase()),
67
+ ),
68
+ );
69
+
70
+ const filteredTactics = $derived(
71
+ tacticTags.filter((tag) =>
72
+ tag.label.toLowerCase().includes(tacticFilter.trim().toLowerCase()),
73
+ ),
74
+ );
75
+
76
+ const tacticsLimitReached = $derived(
77
+ tags.tactics.length >= tacticTagsMax,
78
+ );
79
+
80
+ /**
81
+ * Представляет переключение выбора дебютного тега (только один).
82
+ */
83
+ function toggleOpening(id: string) {
84
+ tags = {
85
+ ...tags,
86
+ opening: tags.opening === id ? undefined : id,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Представляет переключение тактического тега с ограничением по количеству.
92
+ */
93
+ function toggleTactic(id: string) {
94
+ const selected = tags.tactics.includes(id);
95
+ if (selected) {
96
+ tags = {
97
+ ...tags,
98
+ tactics: tags.tactics.filter((t) => t !== id),
99
+ };
100
+ return;
101
+ }
102
+ if (tags.tactics.length >= tacticTagsMax) return;
103
+ tags = {
104
+ ...tags,
105
+ tactics: [...tags.tactics, id],
106
+ };
107
+ }
108
+ </script>
109
+
110
+ {#if hoveredOpening}
111
+ {#key hoveredOpening.tag.id}
112
+ <OpeningTagHoverPreview
113
+ fen={hoveredOpening.tag.fen}
114
+ label={hoveredOpening.tag.label}
115
+ anchor={hoveredOpening.anchor}
116
+ />
117
+ {/key}
118
+ {/if}
119
+
120
+ <div class="flex flex-col gap-4 pt-2 pb-0">
121
+ <div class="flex flex-wrap items-center gap-2">
122
+ <h2 class="text-xl font-semibold">Шаг 4: Теги задачи</h2>
123
+ <Popover.Root>
124
+ <Popover.Trigger type="button">
125
+ {#snippet child({ props })}
126
+ <button
127
+ {...props}
128
+ type="button"
129
+ class={cn(
130
+ buttonVariants({ variant: "outline", size: "icon-sm" }),
131
+ "size-7 shrink-0 rounded-full text-sm font-semibold",
132
+ )}
133
+ aria-label="Справка: теги задачи"
134
+ >
135
+ ?
136
+ </button>
137
+ {/snippet}
138
+ </Popover.Trigger>
139
+ <Popover.Portal>
140
+ <Popover.Content
141
+ side="bottom"
142
+ align="start"
143
+ sideOffset={8}
144
+ class={cn(
145
+ "bg-popover text-popover-foreground border-border z-50 max-h-[min(70vh,32rem)] w-[min(calc(100vw-2rem),28rem)] overflow-y-auto rounded-lg border p-4 text-sm shadow-md outline-none",
146
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
147
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
148
+ )}
149
+ >
150
+ <div class="space-y-3 text-muted-foreground leading-relaxed">
151
+ <p>
152
+ Теги помогают классифицировать задачу. Шаг опционален: можно
153
+ завершить мастер без выбора тегов.
154
+ </p>
155
+ <p>
156
+ Дебют — один тег из списка популярных дебютов. Приёмы — до трёх
157
+ тактических мотивов из каталога.
158
+ </p>
159
+ </div>
160
+ </Popover.Content>
161
+ </Popover.Portal>
162
+ </Popover.Root>
163
+ </div>
164
+
165
+ <section class="space-y-3">
166
+ <div class="flex flex-wrap items-end justify-between gap-2">
167
+ <div>
168
+ <h3 class="text-base font-medium">Дебют</h3>
169
+ <p class="text-muted-foreground text-sm">Можно выбрать не более одного</p>
170
+ </div>
171
+ <input
172
+ type="search"
173
+ class="border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:ring-ring h-9 w-full max-w-xs rounded-md border px-3 text-sm outline-none focus-visible:ring-2"
174
+ placeholder="Поиск дебюта…"
175
+ bind:value={openingFilter}
176
+ aria-label="Поиск дебюта"
177
+ />
178
+ </div>
179
+ <div
180
+ class="border-border flex max-h-48 flex-wrap gap-2 overflow-y-auto rounded-lg border p-3"
181
+ role="radiogroup"
182
+ aria-label="Дебют"
183
+ >
184
+ {#each filteredOpenings as tag (tag.id)}
185
+ <button
186
+ type="button"
187
+ role="radio"
188
+ aria-checked={tags.opening === tag.id}
189
+ class={cn(
190
+ "rounded-full border px-3 py-1.5 text-sm transition-colors",
191
+ tags.opening === tag.id
192
+ ? "border-primary bg-primary text-primary-foreground"
193
+ : "border-border bg-background hover:bg-muted",
194
+ )}
195
+ onpointerenter={(event) => handleOpeningPointerEnter(event, tag)}
196
+ onpointerleave={handleOpeningPointerLeave}
197
+ onfocus={(event) => handleOpeningPointerEnter(event, tag)}
198
+ onblur={handleOpeningPointerLeave}
199
+ onclick={() => toggleOpening(tag.id)}
200
+ >
201
+ {tag.label}
202
+ </button>
203
+ {:else}
204
+ <p class="text-muted-foreground text-sm">Ничего не найдено</p>
205
+ {/each}
206
+ </div>
207
+ </section>
208
+
209
+ <section class="space-y-3">
210
+ <div class="flex flex-wrap items-end justify-between gap-2">
211
+ <div>
212
+ <h3 class="text-base font-medium">Приёмы</h3>
213
+ <p class="text-muted-foreground text-sm">
214
+ Выбрано {tags.tactics.length} из {tacticTagsMax}
215
+ </p>
216
+ </div>
217
+ <input
218
+ type="search"
219
+ class="border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:ring-ring h-9 w-full max-w-xs rounded-md border px-3 text-sm outline-none focus-visible:ring-2"
220
+ placeholder="Поиск приёма…"
221
+ bind:value={tacticFilter}
222
+ aria-label="Поиск приёма"
223
+ />
224
+ </div>
225
+ <div
226
+ class="border-border flex max-h-56 flex-wrap gap-2 overflow-y-auto rounded-lg border p-3"
227
+ role="group"
228
+ aria-label="Тактические приёмы"
229
+ >
230
+ {#each filteredTactics as tag (tag.id)}
231
+ {@const selected = tags.tactics.includes(tag.id)}
232
+ {@const disabled = !selected && tacticsLimitReached}
233
+ <button
234
+ type="button"
235
+ aria-pressed={selected}
236
+ {disabled}
237
+ class={cn(
238
+ "rounded-full border px-3 py-1.5 text-sm transition-colors",
239
+ selected
240
+ ? "border-primary bg-primary text-primary-foreground"
241
+ : "border-border bg-background hover:bg-muted",
242
+ disabled && "cursor-not-allowed opacity-50 hover:bg-background",
243
+ )}
244
+ onclick={() => toggleTactic(tag.id)}
245
+ >
246
+ {tag.label}
247
+ </button>
248
+ {:else}
249
+ <p class="text-muted-foreground text-sm">Ничего не найдено</p>
250
+ {/each}
251
+ </div>
252
+ </section>
253
+
254
+ <div class="flex justify-between gap-2">
255
+ <button
256
+ type="button"
257
+ class={cn(buttonVariants({ variant: "outline" }))}
258
+ onclick={onBack}
259
+ >
260
+ Назад
261
+ </button>
262
+ <button
263
+ type="button"
264
+ class={cn(buttonVariants({ variant: "default" }))}
265
+ onclick={() => onDone(tags)}
266
+ >
267
+ Готово
268
+ </button>
269
+ </div>
270
+ </div>
@@ -0,0 +1,19 @@
1
+ import type { TPuzzleTags } from "../puzzle/puzzleCreatedPayload.js";
2
+ import type { TPuzzleOpeningTagOption, TPuzzleTagOption } from "@connectorvol/shared";
3
+ interface Props {
4
+ /** Возвращает текущие выбранные теги (двухсторонняя привязка). */
5
+ tags: TPuzzleTags;
6
+ /** Возвращает каталог дебютов для выбора одного тега. */
7
+ openingTags: readonly TPuzzleOpeningTagOption[];
8
+ /** Возвращает каталог тактических приёмов для выбора до `tacticTagsMax` тегов. */
9
+ tacticTags: readonly TPuzzleTagOption[];
10
+ /** Возвращает максимальное число выбираемых тактических тегов. */
11
+ tacticTagsMax: number;
12
+ /** Возвращает переход назад на шаг превью. */
13
+ onBack: () => void;
14
+ /** Возвращает завершение мастера с выбранными тегами. */
15
+ onDone: (tags: TPuzzleTags) => void;
16
+ }
17
+ declare const StepTags: import("svelte").Component<Props, {}, "tags">;
18
+ type StepTags = ReturnType<typeof StepTags>;
19
+ export default StepTags;
@@ -0,0 +1,9 @@
1
+ import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
2
+ import type { TChessboardAppearanceSettings } from "./types.js";
3
+ /**
4
+ * Представляет визуальные настройки доски для шагов мастера задачи (построение линии и превью).
5
+ */
6
+ export declare function buildPuzzleWizardBoardSettings(boardAppearanceSettings?: TChessboardAppearanceSettings): {
7
+ play: TChessBoardPlaySettings;
8
+ design: TChessBoardDesignSettings;
9
+ };
@@ -0,0 +1,19 @@
1
+ import { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "../constants/default-board-appearance-settings.js";
2
+ import { DEFAULT_EDITABLE_BOARD_SETTINGS } from "../constants/editable-board-settings.js";
3
+ /**
4
+ * Представляет визуальные настройки доски для шагов мастера задачи (построение линии и превью).
5
+ */
6
+ export function buildPuzzleWizardBoardSettings(boardAppearanceSettings) {
7
+ return {
8
+ play: {
9
+ ...DEFAULT_BOARD_APPEARANCE_SETTINGS.play,
10
+ ...boardAppearanceSettings?.play,
11
+ boardSize: DEFAULT_EDITABLE_BOARD_SETTINGS.play.boardSize,
12
+ isResizable: false,
13
+ },
14
+ design: {
15
+ ...DEFAULT_BOARD_APPEARANCE_SETTINGS.design,
16
+ ...boardAppearanceSettings?.design,
17
+ },
18
+ };
19
+ }
@@ -1,4 +1,4 @@
1
- import type { ChessboardTheme } from "@connectorvol/chessboard";
1
+ import type { BoardApi, ChessboardTheme, TChessBoardDesignSettings } from "@connectorvol/chessboard";
2
2
  import { type IChessBoardActions } from "@connectorvol/chessboard";
3
3
  import { Color } from "@connectorvol/shared";
4
4
  import type { TChessboardAppearanceSettings } from "./types.js";
@@ -6,11 +6,18 @@ import type { TChessboardAppearanceSettings } from "./types.js";
6
6
  * Представляет определение стороны хода по полному FEN.
7
7
  */
8
8
  export declare function sideToMoveFromFullFen(fullFen: string): Color;
9
+ interface ICreatePuzzleLineEditingBoardResult {
10
+ api: BoardApi;
11
+ design: TChessBoardDesignSettings;
12
+ }
9
13
  /**
10
14
  * Представляет создание API доски для интерактивного построения линии (как в мастере задач):
11
15
  * ориентация и перетаскивание по стороне хода, фиксированный размер доски.
16
+ * Возвращает пару `{ api, design }` — `design` следует передать в `<Chessboard design={...} />`,
17
+ * чтобы доска разделяла снимок с превью/настройками через контекст.
12
18
  */
13
19
  export declare function createPuzzleLineEditingBoardApi(fullFen: string, actions: IChessBoardActions, opts: {
14
20
  boardTheme?: ChessboardTheme;
15
- boardAppearanceSettings: TChessboardAppearanceSettings;
16
- }): import("@connectorvol/chessboard").BoardApi;
21
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
22
+ }): ICreatePuzzleLineEditingBoardResult;
23
+ export {};
@@ -1,6 +1,6 @@
1
- import { CHESSBOARD_THEMES, createBoardApi, Draggable, } from "@connectorvol/chessboard";
1
+ import { CHESSBOARD_THEMES, createBoardApi, DEFAULT_DESIGN_SETTINGS, Draggable, } from "@connectorvol/chessboard";
2
2
  import { Color } from "@connectorvol/shared";
3
- import { DEFAULT_EDITABLE_BOARD_SETTINGS } from "../constants/editable-board-settings.js";
3
+ import { buildPuzzleWizardBoardSettings } from "./buildPuzzleWizardBoardSettings.js";
4
4
  /**
5
5
  * Представляет определение стороны хода по полному FEN.
6
6
  */
@@ -11,20 +11,25 @@ export function sideToMoveFromFullFen(fullFen) {
11
11
  /**
12
12
  * Представляет создание API доски для интерактивного построения линии (как в мастере задач):
13
13
  * ориентация и перетаскивание по стороне хода, фиксированный размер доски.
14
+ * Возвращает пару `{ api, design }` — `design` следует передать в `<Chessboard design={...} />`,
15
+ * чтобы доска разделяла снимок с превью/настройками через контекст.
14
16
  */
15
17
  export function createPuzzleLineEditingBoardApi(fullFen, actions, opts) {
16
18
  const side = sideToMoveFromFullFen(fullFen);
17
- const { boardSize: _boardSize, orientation: _orientation, draggable: _draggable, ...appearanceSettings } = opts.boardAppearanceSettings;
18
- return createBoardApi({
19
+ const { play, design } = buildPuzzleWizardBoardSettings(opts.boardAppearanceSettings);
20
+ const api = createBoardApi({
19
21
  fen: fullFen,
20
- settings: {
21
- ...appearanceSettings,
22
- boardSize: DEFAULT_EDITABLE_BOARD_SETTINGS.boardSize,
23
- isResizable: false,
22
+ playSettings: {
23
+ ...play,
24
24
  orientation: side,
25
25
  draggable: side === Color.WHITE ? Draggable.WHITE : Draggable.BLACK,
26
26
  },
27
27
  actions,
28
- theme: opts.boardTheme ?? CHESSBOARD_THEMES.blue,
29
28
  });
29
+ const finalDesign = {
30
+ ...DEFAULT_DESIGN_SETTINGS,
31
+ ...design,
32
+ theme: opts.boardTheme ?? CHESSBOARD_THEMES.blue,
33
+ };
34
+ return { api, design: finalDesign };
30
35
  }
@@ -0,0 +1,35 @@
1
+ import { type PuzzleData } from "../puzzle/puzzleData.js";
2
+ import type { TPuzzleTags } from "../puzzle/puzzleCreatedPayload.js";
3
+ import type { TWizardStep } from "@connectorvol/shared";
4
+ /**
5
+ * Представляет базовый state мастера: поля, которые используют шаги 1–3.
6
+ */
7
+ export type TPuzzleWizardCoreState = {
8
+ /** Возвращает данные задачи (FEN и главная линия SAN). */
9
+ puzzleData: PuzzleData;
10
+ /** Возвращает PGN дерева решения для превью на шаге 3. */
11
+ solutionPgn: string;
12
+ };
13
+ /**
14
+ * Представляет расширенный state мастера с полем тегов для шага `PuzzleWizardTagsStep`.
15
+ */
16
+ export type TPuzzleWizardState = TPuzzleWizardCoreState & {
17
+ /** Возвращает выбранные теги задачи. */
18
+ puzzleTags: TPuzzleTags;
19
+ };
20
+ /** Возвращает базовые шаги мастера: позиция, ходы, превью. */
21
+ export declare const PUZZLE_WIZARD_CORE_STEPS: readonly TWizardStep[];
22
+ /** Возвращает описание опционального шага выбора тегов. */
23
+ export declare const PUZZLE_WIZARD_TAGS_STEP: TWizardStep;
24
+ /**
25
+ * Представляет создание базового state мастера из seed FEN.
26
+ */
27
+ export declare function createInitialPuzzleWizardCoreState(seedFen: string | undefined): TPuzzleWizardCoreState;
28
+ /**
29
+ * Представляет создание расширенного state мастера с тегами из seed FEN.
30
+ */
31
+ export declare function createInitialPuzzleWizardState(seedFen: string | undefined): TPuzzleWizardState;
32
+ /**
33
+ * Представляет нормализацию входного FEN (пустая строка трактуется как отсутствие значения).
34
+ */
35
+ export declare function puzzleWizardSeedFen(prop: string | undefined): string | undefined;
@@ -0,0 +1,37 @@
1
+ import { createInitialPuzzleData } from "../puzzle/puzzleData.js";
2
+ /** Возвращает базовые шаги мастера: позиция, ходы, превью. */
3
+ export const PUZZLE_WIZARD_CORE_STEPS = [
4
+ { id: "position", title: "Позиция" },
5
+ { id: "moves", title: "Ходы" },
6
+ { id: "preview", title: "Превью" },
7
+ ];
8
+ /** Возвращает описание опционального шага выбора тегов. */
9
+ export const PUZZLE_WIZARD_TAGS_STEP = {
10
+ id: "tags",
11
+ title: "Теги",
12
+ };
13
+ /**
14
+ * Представляет создание базового state мастера из seed FEN.
15
+ */
16
+ export function createInitialPuzzleWizardCoreState(seedFen) {
17
+ return {
18
+ puzzleData: createInitialPuzzleData(seedFen),
19
+ solutionPgn: "",
20
+ };
21
+ }
22
+ /**
23
+ * Представляет создание расширенного state мастера с тегами из seed FEN.
24
+ */
25
+ export function createInitialPuzzleWizardState(seedFen) {
26
+ return {
27
+ ...createInitialPuzzleWizardCoreState(seedFen),
28
+ puzzleTags: { tactics: [] },
29
+ };
30
+ }
31
+ /**
32
+ * Представляет нормализацию входного FEN (пустая строка трактуется как отсутствие значения).
33
+ */
34
+ export function puzzleWizardSeedFen(prop) {
35
+ const t = prop?.trim();
36
+ return t ? t : undefined;
37
+ }
@@ -1,9 +1,15 @@
1
+ import type { Snippet } from "svelte";
1
2
  import type { TPuzzleCreatedPayload } from "../puzzle/puzzleCreatedPayload.js";
2
- import type { ChessBoardSettings, ChessboardTheme } from "@connectorvol/chessboard";
3
+ import type { TWizardStep, TWizardStepContext } from "@connectorvol/shared";
4
+ import type { TPuzzleWizardCoreState } from "./puzzleWizardState.js";
5
+ import type { ChessboardTheme, TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
3
6
  /**
4
7
  * Представляет тип визуальных настроек шахматной доски для мастера создания задачи.
5
8
  */
6
- export type TChessboardAppearanceSettings = Omit<Partial<ChessBoardSettings>, "boardSize" | "orientation" | "draggable">;
9
+ export type TChessboardAppearanceSettings = {
10
+ play?: Omit<TChessBoardPlaySettings, "boardSize" | "orientation" | "draggable">;
11
+ design?: Partial<TChessBoardDesignSettings>;
12
+ };
7
13
  /**
8
14
  * Представляет исход решения или ошибки в превью задачи для колбэка `onOutcome`.
9
15
  */
@@ -34,13 +40,17 @@ export interface TPuzzlePgnBoardTreeEditorProps {
34
40
  */
35
41
  solved?: boolean;
36
42
  /**
37
- * Возвращает тему оформления шахматной доски (по умолчанию `CHESSBOARD_THEMES.blue`).
43
+ * Возвращает дополнительные визуальные настройки шахматной доски (кроме `boardSize`, `orientation`, `draggable`).
38
44
  */
39
- boardTheme?: ChessboardTheme;
45
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
40
46
  /**
41
- * Возвращает дополнительные визуальные настройки шахматной доски (кроме `boardSize`, `orientation`, `draggable`).
47
+ * Возвращает начальный размер доски в rem (применяется только при создании API доски).
42
48
  */
43
- boardAppearanceSettings: TChessboardAppearanceSettings;
49
+ boardSize?: number;
50
+ /**
51
+ * Возвращает колбэк после завершения изменения размера доски перетаскиванием ручки resize.
52
+ */
53
+ onResizeAction?: (size: number) => void;
44
54
  /**
45
55
  * Возвращает дополнительные классы Tailwind для корневого контейнера раскладки.
46
56
  */
@@ -49,11 +59,19 @@ export interface TPuzzlePgnBoardTreeEditorProps {
49
59
  /**
50
60
  * Представляет свойства компонента мастера создания шахматной задачи.
51
61
  */
52
- export interface TPuzzleCreationWizardProps {
62
+ export type TPuzzleCreationWizardProps<S extends TPuzzleWizardCoreState = TPuzzleWizardCoreState> = {
63
+ /**
64
+ * Возвращает общий state визарда (двухсторонняя привязка; инициализация и сброс — на стороне родителя).
65
+ */
66
+ wizardState: S;
53
67
  /**
54
- * Возвращает функцию, вызываемую при завершении мастера с FEN и строкой ходов.
68
+ * Возвращает функцию, вызываемую при завершении мастера с FEN и строкой ходов (шаг 3 без доп. шагов).
55
69
  */
56
70
  onPuzzleCreated: (payload: TPuzzleCreatedPayload) => void;
71
+ /**
72
+ * Возвращает колбэк завершения с финальным state (вызывается из `wizard.done()` на доп. шагах).
73
+ */
74
+ onDone?: (state: S) => void;
57
75
  /**
58
76
  * Возвращает начальный полный FEN для шага 1; если не задан — пустая доска.
59
77
  */
@@ -65,5 +83,13 @@ export interface TPuzzleCreationWizardProps {
65
83
  /**
66
84
  * Возвращает дополнительные визуальные настройки шахматной доски (кроме `boardSize`, `orientation`, `draggable`).
67
85
  */
68
- boardAppearanceSettings: TChessboardAppearanceSettings;
69
- }
86
+ boardAppearanceSettings?: TChessboardAppearanceSettings;
87
+ /**
88
+ * Возвращает дополнительные шаги после превью (для индикатора прогресса и навигации).
89
+ */
90
+ additionalSteps?: readonly TWizardStep[];
91
+ /**
92
+ * Возвращает snippet содержимого дополнительных шагов (вызывается при `currentStep > 3`).
93
+ */
94
+ additionalStep?: Snippet<[TWizardStepContext<S>]>;
95
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@connectorvol/chess-widgets",
3
- "version": "8.0.1",
3
+ "version": "9.0.1",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist",
@@ -29,7 +29,7 @@
29
29
  "start": "vite",
30
30
  "dev": "vite",
31
31
  "build": "vite build",
32
- "preview": "vite preview",
32
+ "preview": "bun build/index.js",
33
33
  "package": "svelte-kit sync && svelte-package && bun pm pack",
34
34
  "publish": "bun publish --access public",
35
35
  "prepublishOnly": "bun pm pack",
@@ -37,44 +37,49 @@
37
37
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
38
38
  "format": "oxfmt --write . && prettier --write \"**/*.svelte\"",
39
39
  "prettier": "prettier --write \"**/*.svelte\"",
40
- "test": "",
40
+ "test": "vitest run",
41
41
  "test:e2e": "node node_modules/@playwright/test/cli.js test",
42
42
  "test:e2e:ui": "node node_modules/@playwright/test/cli.js test --ui"
43
43
  },
44
44
  "dependencies": {
45
- "@connectorvol/chessboard": "8.0.1",
46
- "@connectorvol/chessops": "8.0.1",
47
- "@connectorvol/shared": "8.0.1",
48
- "@connectorvol/tree": "8.0.1",
45
+ "@connectorvol/chessboard": "9.0.1",
46
+ "@connectorvol/chessops": "9.0.1",
47
+ "@connectorvol/shared": "9.0.1",
48
+ "@connectorvol/tree": "9.0.1",
49
49
  "bits-ui": "2.16.4",
50
- "clsx": "^2.1.1",
51
- "svelte-toolbelt": "^0.10.6",
50
+ "clsx": "2.1.1",
51
+ "svelte-toolbelt": "0.10.6",
52
52
  "tailwind-merge": "3.3.1",
53
- "tailwind-variants": "^3.2.2"
53
+ "tailwind-variants": "3.2.2"
54
54
  },
55
55
  "devDependencies": {
56
+ "@connectorvol/chess-shadcn": "9.0.1",
56
57
  "@ianvs/prettier-plugin-sort-imports": "4.5.1",
57
- "@lucide/svelte": "^1.16.0",
58
+ "@lucide/svelte": "1.21.0",
58
59
  "@playwright/test": "1.54.1",
59
- "@sveltejs/adapter-static": "3.0.10",
60
- "@sveltejs/kit": "2.48.0",
60
+ "@sveltejs/kit": "2.49.1",
61
61
  "@sveltejs/package": "2.4.0",
62
62
  "@sveltejs/vite-plugin-svelte": "7.0.0",
63
63
  "@tailwindcss/forms": "0.5.10",
64
64
  "@tailwindcss/postcss": "4.1.11",
65
65
  "@tailwindcss/typography": "0.5.16",
66
66
  "@tailwindcss/vite": "4.1.11",
67
+ "@testing-library/svelte": "5.3.1",
68
+ "@vitest/coverage-v8": "3.2.4",
67
69
  "autoprefixer": "10.4.21",
68
- "svelte-check": "4.3.2",
70
+ "svelte-adapter-bun": "^1.0.1",
71
+ "svelte-check": "4.6.0",
69
72
  "tailwindcss": "4.1.11",
70
73
  "tw-animate-css": "1.4.0",
71
74
  "typescript": "6.0.2",
72
75
  "vite": "8.0.7",
73
76
  "vite-plugin-checker": "0.12.0",
74
- "vite-plugin-svelte-checker": "0.1.2"
77
+ "vite-plugin-svelte-checker": "0.1.2",
78
+ "vitest": "4.0.17"
75
79
  },
76
80
  "peerDependencies": {
77
- "@lucide/svelte": "^1.16.0",
78
- "svelte": "^5.53.12"
81
+ "@lucide/svelte": "1.21.0",
82
+ "mode-watcher": "1.1.0",
83
+ "svelte": "5.56.3"
79
84
  }
80
85
  }