@connectorvol/chess-widgets 1.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.
- package/dist/button-variants.d.ts +73 -0
- package/dist/button-variants.js +31 -0
- package/dist/constants/editable-board-settings.d.ts +26 -0
- package/dist/constants/editable-board-settings.js +27 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +8 -0
- package/dist/position-editor/EditFen.svelte +164 -0
- package/dist/position-editor/EditFen.svelte.d.ts +16 -0
- package/dist/position-editor/EditMove.svelte +180 -0
- package/dist/position-editor/EditMove.svelte.d.ts +9 -0
- package/dist/position-editor/EditPanel.svelte +164 -0
- package/dist/position-editor/EditPanel.svelte.d.ts +8 -0
- package/dist/position-editor/fen.svelte.d.ts +26 -0
- package/dist/position-editor/fen.svelte.js +177 -0
- package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
- package/dist/puzzle/puzzleCreatedPayload.js +24 -0
- package/dist/puzzle/puzzleData.d.ts +32 -0
- package/dist/puzzle/puzzleData.js +51 -0
- package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +14 -0
- package/dist/puzzle/puzzleSolverForkAnnotations.js +94 -0
- package/dist/puzzle/puzzleStepPreviewSolver.d.ts +39 -0
- package/dist/puzzle/puzzleStepPreviewSolver.js +87 -0
- package/dist/puzzle-creation/PuzzleCreationWizard.svelte +247 -0
- package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +5 -0
- package/dist/puzzle-creation/StepMoves.svelte +225 -0
- package/dist/puzzle-creation/StepMoves.svelte.d.ts +15 -0
- package/dist/puzzle-creation/StepPosition.svelte +210 -0
- package/dist/puzzle-creation/StepPosition.svelte.d.ts +11 -0
- package/dist/puzzle-creation/StepPreview.svelte +589 -0
- package/dist/puzzle-creation/StepPreview.svelte.d.ts +23 -0
- package/dist/puzzle-creation/types.d.ts +27 -0
- package/dist/puzzle-creation/types.js +1 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.js +8 -0
- package/package.json +76 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { untrack } from "svelte";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Chessboard,
|
|
6
|
+
CHESSBOARD_THEMES,
|
|
7
|
+
createBoardApi,
|
|
8
|
+
type ChessboardTheme,
|
|
9
|
+
} from "@connectorvol/chessboard";
|
|
10
|
+
|
|
11
|
+
import { DEFAULT_EDITABLE_BOARD_SETTINGS } from "../constants/editable-board-settings.js";
|
|
12
|
+
import EditFen from "../position-editor/EditFen.svelte";
|
|
13
|
+
import EditMove from "../position-editor/EditMove.svelte";
|
|
14
|
+
import EditPanel from "../position-editor/EditPanel.svelte";
|
|
15
|
+
import { Fen } from "../position-editor/fen.svelte.js";
|
|
16
|
+
import { Popover } from "bits-ui";
|
|
17
|
+
import { buttonVariants } from "../button-variants.js";
|
|
18
|
+
import { cn } from "../utils.js";
|
|
19
|
+
import { Color } from "@connectorvol/shared";
|
|
20
|
+
import type { TChessboardAppearanceSettings } from "./types.js";
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
initialFen: string;
|
|
24
|
+
onNext: (fen: string) => void;
|
|
25
|
+
boardTheme?: ChessboardTheme;
|
|
26
|
+
boardAppearanceSettings?: TChessboardAppearanceSettings;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const props: Props = $props();
|
|
30
|
+
|
|
31
|
+
/** Настройки доски мастера задачи: фиксированный размер, без ручного resize. */
|
|
32
|
+
const puzzleStepBoardSettings = {
|
|
33
|
+
...DEFAULT_EDITABLE_BOARD_SETTINGS,
|
|
34
|
+
...(props.boardAppearanceSettings ?? {}),
|
|
35
|
+
boardSize: 29,
|
|
36
|
+
isResizable: false,
|
|
37
|
+
editSettings: DEFAULT_EDITABLE_BOARD_SETTINGS.editSettings,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
let chessboard = $state(
|
|
41
|
+
createBoardApi({
|
|
42
|
+
fen: (() => props.initialFen)(),
|
|
43
|
+
settings: puzzleStepBoardSettings,
|
|
44
|
+
theme: props.boardTheme ?? CHESSBOARD_THEMES.blue,
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/** Один объект Fen на доску: `$derived(new Fen(...))` пересоздавал бы класс при смене orientation и сбрасывал бы «Установить ход». */
|
|
49
|
+
const fen = new Fen(chessboard);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Представляет подстановку полей FEN (ход, рокировка, счётчики) из строки родителя при смене стартовой позиции.
|
|
53
|
+
*/
|
|
54
|
+
$effect(() => {
|
|
55
|
+
const src = props.initialFen;
|
|
56
|
+
untrack(() => {
|
|
57
|
+
try {
|
|
58
|
+
fen.fullFen = src;
|
|
59
|
+
} catch {
|
|
60
|
+
/* некорректная строка FEN — оставляем текущее состояние Fen */
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
$effect(() => {
|
|
66
|
+
const sideToMove = fen.move;
|
|
67
|
+
const targetOrientation = sideToMove === "w" ? Color.WHITE : Color.BLACK;
|
|
68
|
+
if (chessboard.orientation !== targetOrientation) {
|
|
69
|
+
chessboard.orientation = targetOrientation;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
function handleNext() {
|
|
74
|
+
props.onNext(fen.fullFen);
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<div class="flex flex-col gap-3 pt-2 pb-0">
|
|
79
|
+
<div class="flex w-full flex-wrap items-center gap-2">
|
|
80
|
+
<h2 class="text-xl font-semibold">Шаг 1: Создание позиции</h2>
|
|
81
|
+
<Popover.Root>
|
|
82
|
+
<Popover.Trigger type="button">
|
|
83
|
+
{#snippet child({ props })}
|
|
84
|
+
<button
|
|
85
|
+
{...props}
|
|
86
|
+
type="button"
|
|
87
|
+
class={cn(
|
|
88
|
+
buttonVariants({ variant: "outline", size: "icon-sm" }),
|
|
89
|
+
"size-7 shrink-0 rounded-full text-sm font-semibold",
|
|
90
|
+
)}
|
|
91
|
+
aria-label="Справка: создание стартовой позиции"
|
|
92
|
+
>
|
|
93
|
+
?
|
|
94
|
+
</button>
|
|
95
|
+
{/snippet}
|
|
96
|
+
</Popover.Trigger>
|
|
97
|
+
<Popover.Portal>
|
|
98
|
+
<Popover.Content
|
|
99
|
+
side="bottom"
|
|
100
|
+
align="start"
|
|
101
|
+
sideOffset={8}
|
|
102
|
+
class={cn(
|
|
103
|
+
"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",
|
|
104
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
105
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
106
|
+
)}
|
|
107
|
+
>
|
|
108
|
+
<p class="text-muted-foreground leading-relaxed">
|
|
109
|
+
Установите начальную позицию, с которой ученик будет искать решение.
|
|
110
|
+
В поле «Установить ход» доска поворачивается так, что ходящая
|
|
111
|
+
сторона оказывается у нижнего края, а палитры фигур меняются
|
|
112
|
+
местами.
|
|
113
|
+
</p>
|
|
114
|
+
</Popover.Content>
|
|
115
|
+
</Popover.Portal>
|
|
116
|
+
</Popover.Root>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div class="flex flex-col md:flex-row gap-3">
|
|
120
|
+
<div class="flex w-full max-w-full flex-col justify-center gap-2">
|
|
121
|
+
{#snippet boardRow()}
|
|
122
|
+
<div
|
|
123
|
+
class="flex w-full max-w-full flex-col gap-3 md:flex-row md:items-start items-center"
|
|
124
|
+
>
|
|
125
|
+
<!-- Явная ширина: при родителе с w-fit/min-content BoardContainer даёт min(100%, Nrem), и 100% может схлопнуться до нуля. -->
|
|
126
|
+
<div
|
|
127
|
+
class="shrink-0 max-w-full"
|
|
128
|
+
style="width: {puzzleStepBoardSettings.boardSize}rem; max-width: 100%;"
|
|
129
|
+
>
|
|
130
|
+
<Chessboard facade={chessboard} />
|
|
131
|
+
</div>
|
|
132
|
+
<div
|
|
133
|
+
class="hidden min-w-0 flex-1 flex-col gap-3 md:flex md:min-w-[280px]"
|
|
134
|
+
>
|
|
135
|
+
<EditFen {fen} api={chessboard} constrainToBoardWidth={false} />
|
|
136
|
+
<div class="space-y-2">
|
|
137
|
+
<label
|
|
138
|
+
for="puzzle-start-fullmove-md"
|
|
139
|
+
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
140
|
+
>
|
|
141
|
+
Номер хода в записи партии
|
|
142
|
+
</label>
|
|
143
|
+
<input
|
|
144
|
+
id="puzzle-start-fullmove-md"
|
|
145
|
+
type="number"
|
|
146
|
+
min="1"
|
|
147
|
+
class={cn(
|
|
148
|
+
"border-input bg-background ring-offset-background shadow-xs flex h-9 w-full rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] md:text-sm",
|
|
149
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
150
|
+
)}
|
|
151
|
+
value={fen.fullmove}
|
|
152
|
+
oninput={(e) => {
|
|
153
|
+
const n = Number.parseInt(e.currentTarget.value, 10);
|
|
154
|
+
if (!Number.isFinite(n) || n < 1) return;
|
|
155
|
+
fen.fullmove = n;
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
<EditMove {fen} api={chessboard} />
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
{/snippet}
|
|
163
|
+
<!-- Без {#if} по fen.move: иначе при смене стороны размонтируется boardRow/EditMove и bind у select ломается до следующего взаимодействия. -->
|
|
164
|
+
<div class={fen.move === "w" ? "order-1" : "order-3"}>
|
|
165
|
+
<EditPanel api={chessboard} color="b" />
|
|
166
|
+
</div>
|
|
167
|
+
<div class="order-2">{@render boardRow()}</div>
|
|
168
|
+
<div class={fen.move === "w" ? "order-3" : "order-1"}>
|
|
169
|
+
<EditPanel api={chessboard} color="w" />
|
|
170
|
+
</div>
|
|
171
|
+
<div class="md:hidden flex w-full flex-col gap-3">
|
|
172
|
+
<EditFen {fen} api={chessboard} constrainToBoardWidth={false} />
|
|
173
|
+
<div class="space-y-2">
|
|
174
|
+
<label
|
|
175
|
+
for="puzzle-start-fullmove-mobile"
|
|
176
|
+
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
177
|
+
>
|
|
178
|
+
Номер хода в записи партии
|
|
179
|
+
</label>
|
|
180
|
+
<input
|
|
181
|
+
id="puzzle-start-fullmove-mobile"
|
|
182
|
+
type="number"
|
|
183
|
+
min="1"
|
|
184
|
+
class={cn(
|
|
185
|
+
"border-input bg-background ring-offset-background shadow-xs flex h-9 w-full rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] md:text-sm",
|
|
186
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
187
|
+
)}
|
|
188
|
+
value={fen.fullmove}
|
|
189
|
+
oninput={(e) => {
|
|
190
|
+
const n = Number.parseInt(e.currentTarget.value, 10);
|
|
191
|
+
if (!Number.isFinite(n) || n < 1) return;
|
|
192
|
+
fen.fullmove = n;
|
|
193
|
+
}}
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
196
|
+
<EditMove {fen} api={chessboard} />
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="flex justify-end">
|
|
202
|
+
<button
|
|
203
|
+
type="button"
|
|
204
|
+
class={cn(buttonVariants({ variant: "default" }))}
|
|
205
|
+
onclick={handleNext}
|
|
206
|
+
>
|
|
207
|
+
Далее
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ChessboardTheme } from "@connectorvol/chessboard";
|
|
2
|
+
import type { TChessboardAppearanceSettings } from "./types.js";
|
|
3
|
+
interface Props {
|
|
4
|
+
initialFen: string;
|
|
5
|
+
onNext: (fen: string) => void;
|
|
6
|
+
boardTheme?: ChessboardTheme;
|
|
7
|
+
boardAppearanceSettings?: TChessboardAppearanceSettings;
|
|
8
|
+
}
|
|
9
|
+
declare const StepPosition: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type StepPosition = ReturnType<typeof StepPosition>;
|
|
11
|
+
export default StepPosition;
|