@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.
Files changed (35) hide show
  1. package/dist/button-variants.d.ts +73 -0
  2. package/dist/button-variants.js +31 -0
  3. package/dist/constants/editable-board-settings.d.ts +26 -0
  4. package/dist/constants/editable-board-settings.js +27 -0
  5. package/dist/index.d.ts +12 -0
  6. package/dist/index.js +8 -0
  7. package/dist/position-editor/EditFen.svelte +164 -0
  8. package/dist/position-editor/EditFen.svelte.d.ts +16 -0
  9. package/dist/position-editor/EditMove.svelte +180 -0
  10. package/dist/position-editor/EditMove.svelte.d.ts +9 -0
  11. package/dist/position-editor/EditPanel.svelte +164 -0
  12. package/dist/position-editor/EditPanel.svelte.d.ts +8 -0
  13. package/dist/position-editor/fen.svelte.d.ts +26 -0
  14. package/dist/position-editor/fen.svelte.js +177 -0
  15. package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
  16. package/dist/puzzle/puzzleCreatedPayload.js +24 -0
  17. package/dist/puzzle/puzzleData.d.ts +32 -0
  18. package/dist/puzzle/puzzleData.js +51 -0
  19. package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +14 -0
  20. package/dist/puzzle/puzzleSolverForkAnnotations.js +94 -0
  21. package/dist/puzzle/puzzleStepPreviewSolver.d.ts +39 -0
  22. package/dist/puzzle/puzzleStepPreviewSolver.js +87 -0
  23. package/dist/puzzle-creation/PuzzleCreationWizard.svelte +247 -0
  24. package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +5 -0
  25. package/dist/puzzle-creation/StepMoves.svelte +225 -0
  26. package/dist/puzzle-creation/StepMoves.svelte.d.ts +15 -0
  27. package/dist/puzzle-creation/StepPosition.svelte +210 -0
  28. package/dist/puzzle-creation/StepPosition.svelte.d.ts +11 -0
  29. package/dist/puzzle-creation/StepPreview.svelte +589 -0
  30. package/dist/puzzle-creation/StepPreview.svelte.d.ts +23 -0
  31. package/dist/puzzle-creation/types.d.ts +27 -0
  32. package/dist/puzzle-creation/types.js +1 -0
  33. package/dist/utils.d.ts +15 -0
  34. package/dist/utils.js +8 -0
  35. package/package.json +76 -0
@@ -0,0 +1,73 @@
1
+ import type { WithElementRef } from "./utils.js";
2
+ import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
3
+ import { type VariantProps } from "tailwind-variants";
4
+ /**
5
+ * Представляет набор классов Tailwind и вариантов оформления кнопки (как в doc-приложении).
6
+ */
7
+ export declare const buttonVariants: import("tailwind-variants").TVReturnType<{
8
+ variant: {
9
+ default: string;
10
+ outline: string;
11
+ secondary: string;
12
+ ghost: string;
13
+ destructive: string;
14
+ link: string;
15
+ };
16
+ size: {
17
+ default: string;
18
+ xs: string;
19
+ sm: string;
20
+ lg: string;
21
+ icon: string;
22
+ "icon-xs": string;
23
+ "icon-sm": string;
24
+ "icon-lg": string;
25
+ };
26
+ }, undefined, "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
27
+ variant: {
28
+ default: string;
29
+ outline: string;
30
+ secondary: string;
31
+ ghost: string;
32
+ destructive: string;
33
+ link: string;
34
+ };
35
+ size: {
36
+ default: string;
37
+ xs: string;
38
+ sm: string;
39
+ lg: string;
40
+ icon: string;
41
+ "icon-xs": string;
42
+ "icon-sm": string;
43
+ "icon-lg": string;
44
+ };
45
+ }, undefined, import("tailwind-variants").TVReturnType<{
46
+ variant: {
47
+ default: string;
48
+ outline: string;
49
+ secondary: string;
50
+ ghost: string;
51
+ destructive: string;
52
+ link: string;
53
+ };
54
+ size: {
55
+ default: string;
56
+ xs: string;
57
+ sm: string;
58
+ lg: string;
59
+ icon: string;
60
+ "icon-xs": string;
61
+ "icon-sm": string;
62
+ "icon-lg": string;
63
+ };
64
+ }, undefined, "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", unknown, unknown, undefined>>;
65
+ /** Представляет допустимый вариант заливки кнопки. */
66
+ export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
67
+ /** Представляет допустимый размер кнопки. */
68
+ export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
69
+ /** Представляет пропсы кнопки (совместимость с shadcn-паттерном). */
70
+ export type ButtonProps = WithElementRef<HTMLButtonAttributes> & WithElementRef<HTMLAnchorAttributes> & {
71
+ variant?: ButtonVariant;
72
+ size?: ButtonSize;
73
+ };
@@ -0,0 +1,31 @@
1
+ import { tv } from "tailwind-variants";
2
+ /**
3
+ * Представляет набор классов Tailwind и вариантов оформления кнопки (как в doc-приложении).
4
+ */
5
+ export const buttonVariants = tv({
6
+ base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
7
+ variants: {
8
+ variant: {
9
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
10
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
11
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
12
+ ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
13
+ destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
14
+ link: "text-primary underline-offset-4 hover:underline",
15
+ },
16
+ size: {
17
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
18
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
19
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
20
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
21
+ icon: "size-8",
22
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
23
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
24
+ "icon-lg": "size-9",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: "default",
29
+ size: "default",
30
+ },
31
+ });
@@ -0,0 +1,26 @@
1
+ import { Draggable, PieceInputMode } from "@connectorvol/chessboard";
2
+ /**
3
+ * Представляет настройки редактируемой доски по умолчанию для конструктора позиции и мастера задач.
4
+ */
5
+ export declare const DEFAULT_EDITABLE_BOARD_SETTINGS: {
6
+ autoQueenPromotion: false;
7
+ isResizable: true;
8
+ isDisplayCoordinate: 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
+ };
26
+ };
@@ -0,0 +1,27 @@
1
+ import { Draggable, PieceInputMode } from "@connectorvol/chessboard";
2
+ import { Color } from "@connectorvol/shared";
3
+ /**
4
+ * Представляет настройки редактируемой доски по умолчанию для конструктора позиции и мастера задач.
5
+ */
6
+ export const DEFAULT_EDITABLE_BOARD_SETTINGS = {
7
+ autoQueenPromotion: false,
8
+ isResizable: true,
9
+ isDisplayCoordinate: 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,
25
+ },
26
+ },
27
+ };
@@ -0,0 +1,12 @@
1
+ export { default as PuzzleCreationWizard } from "./puzzle-creation/PuzzleCreationWizard.svelte";
2
+ export type { TPuzzleCreationWizardProps } from "./puzzle-creation/types.js";
3
+ export type { TPuzzleCreatedPayload } from "./puzzle/puzzleCreatedPayload.js";
4
+ export { puzzlePartsFromFullPgn } from "./puzzle/puzzleCreatedPayload.js";
5
+ export type { PuzzleData } from "./puzzle/puzzleData.js";
6
+ export { PUZZLE_EMPTY_BOARD_FEN, createInitialPuzzleData, createEmptyTreeFromFen, getMainLineFromTree, } from "./puzzle/puzzleData.js";
7
+ export type { TSolverMoveOutcome } from "./puzzle/puzzleStepPreviewSolver.js";
8
+ export { DEFAULT_EDITABLE_BOARD_SETTINGS } from "./constants/editable-board-settings.js";
9
+ export { default as EditFen } from "./position-editor/EditFen.svelte";
10
+ export { default as EditMove } from "./position-editor/EditMove.svelte";
11
+ export { default as EditPanel } from "./position-editor/EditPanel.svelte";
12
+ export { Fen, type Castling } from "./position-editor/fen.svelte.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { default as PuzzleCreationWizard } from "./puzzle-creation/PuzzleCreationWizard.svelte";
2
+ export { puzzlePartsFromFullPgn } from "./puzzle/puzzleCreatedPayload.js";
3
+ export { PUZZLE_EMPTY_BOARD_FEN, createInitialPuzzleData, createEmptyTreeFromFen, getMainLineFromTree, } from "./puzzle/puzzleData.js";
4
+ export { DEFAULT_EDITABLE_BOARD_SETTINGS } from "./constants/editable-board-settings.js";
5
+ export { default as EditFen } from "./position-editor/EditFen.svelte";
6
+ export { default as EditMove } from "./position-editor/EditMove.svelte";
7
+ export { default as EditPanel } from "./position-editor/EditPanel.svelte";
8
+ export { Fen } from "./position-editor/fen.svelte.js";
@@ -0,0 +1,164 @@
1
+ <script lang="ts">
2
+ import type { BoardApi } from "@connectorvol/chessboard";
3
+ import { EMPTY_FEN, INITIAL_FEN } from "@connectorvol/chessops/fen";
4
+ import CopyIcon from "@lucide/svelte/icons/copy";
5
+ import { tick } from "svelte";
6
+ import type { Fen } from "./fen.svelte.js";
7
+ import { buttonVariants } from "../button-variants.js";
8
+ import { cn } from "../utils.js";
9
+
10
+ /**
11
+ * Представляет свойства компонента редактирования FEN.
12
+ */
13
+ interface Props {
14
+ api: BoardApi;
15
+ fen: Fen;
16
+ /**
17
+ * Возвращает признак ограничения ширины блока шириной доски (страница конструктора под колонкой доски).
18
+ */
19
+ constrainToBoardWidth?: boolean;
20
+ }
21
+
22
+ const {
23
+ api,
24
+ fen,
25
+ constrainToBoardWidth = true,
26
+ }: Props = $props();
27
+
28
+ // oxlint-disable-next-line no-unassigned-vars
29
+ let inputRef: HTMLInputElement;
30
+
31
+ function handlePositionInput() {
32
+ const prevFen = fen.fullFen;
33
+ const start = inputRef.selectionStart;
34
+ const end = inputRef.selectionEnd;
35
+ try {
36
+ fen.fullFen = inputRef.value;
37
+ } catch (e) {
38
+ console.error(e);
39
+ inputRef.value = prevFen;
40
+ }
41
+ setTimeout(() => {
42
+ inputRef.setSelectionRange(start, end);
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Представляет нормализацию текста из буфера для проверки как полного FEN.
48
+ */
49
+ function normalizeClipboardFen(raw: string): string {
50
+ return raw.trim().replace(/\s+/g, " ");
51
+ }
52
+
53
+ /**
54
+ * Представляет полную подстановку FEN из строки буфера при успешной валидации.
55
+ */
56
+ async function applyFullFenFromClipboardText(raw: string): Promise<boolean> {
57
+ const normalized = normalizeClipboardFen(raw);
58
+ if (!fen.validateFullFen(normalized)) return false;
59
+ try {
60
+ fen.fullFen = normalized;
61
+ await tick();
62
+ const len = fen.fullFen.length;
63
+ inputRef.setSelectionRange(len, len);
64
+ return true;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Представляет подстановку полного FEN пресета и синхронизацию поля ввода с доской.
72
+ */
73
+ async function applyPresetFullFen(fullFen: string): Promise<void> {
74
+ if (!fen.validateFullFen(fullFen)) return;
75
+ try {
76
+ fen.fullFen = fullFen;
77
+ await tick();
78
+ const len = fen.fullFen.length;
79
+ inputRef.setSelectionRange(len, len);
80
+ } catch {
81
+ /* невалидный FEN для правил Fen-класса */
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Представляет обработку вставки: только целиком валидный FEN заменяет поле.
87
+ */
88
+ function handlePaste(e: ClipboardEvent) {
89
+ e.preventDefault();
90
+ const text = e.clipboardData?.getData("text/plain") ?? "";
91
+ void applyFullFenFromClipboardText(text);
92
+ }
93
+
94
+ /**
95
+ * Представляет Ctrl/Cmd+C: полная замена поля содержимым буфера при валидном FEN (кнопка «Скопировать» остаётся для копирования наружу).
96
+ */
97
+ function handleKeydown(e: KeyboardEvent) {
98
+ const mod = e.ctrlKey || e.metaKey;
99
+ if (!mod || e.key.toLowerCase() !== "c") return;
100
+ e.preventDefault();
101
+ void navigator.clipboard.readText().then((text) => {
102
+ void applyFullFenFromClipboardText(text);
103
+ });
104
+ }
105
+
106
+ const fullFen = $derived(fen.fullFen);
107
+
108
+ const outerStyle = $derived(
109
+ constrainToBoardWidth ? `max-width: ${api.getBoardSize()}rem` : undefined,
110
+ );
111
+ </script>
112
+
113
+ <div style={outerStyle} class="flex w-full flex-col gap-2">
114
+ <div class="flex flex-wrap gap-2">
115
+ <button
116
+ type="button"
117
+ class={cn(
118
+ buttonVariants({ variant: "outline", size: "sm" }),
119
+ "h-8 text-xs sm:text-sm",
120
+ )}
121
+ onclick={() => {
122
+ void applyPresetFullFen(INITIAL_FEN);
123
+ }}
124
+ >
125
+ Начальная позиция
126
+ </button>
127
+ <button
128
+ type="button"
129
+ class={cn(
130
+ buttonVariants({ variant: "outline", size: "sm" }),
131
+ "h-8 text-xs sm:text-sm",
132
+ )}
133
+ onclick={() => {
134
+ void applyPresetFullFen(EMPTY_FEN);
135
+ }}
136
+ >
137
+ Пустая доска
138
+ </button>
139
+ </div>
140
+ <div class="flex w-full items-center gap-1.5">
141
+ <input
142
+ bind:this={inputRef}
143
+ oninput={handlePositionInput}
144
+ onpaste={handlePaste}
145
+ onkeydown={handleKeydown}
146
+ class={cn(
147
+ "border-input bg-background ring-offset-background shadow-xs flex h-9 min-w-0 flex-1 rounded-md border px-3 py-1 text-base outline-none transition-[color,box-shadow] md:text-sm",
148
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
149
+ )}
150
+ value={fullFen}
151
+ />
152
+
153
+ <button
154
+ type="button"
155
+ aria-label="Скопировать FEN"
156
+ onclick={() => {
157
+ void navigator.clipboard.writeText(fen.fullFen);
158
+ }}
159
+ class={cn(buttonVariants({ variant: "outline", size: "icon-xs" }))}
160
+ >
161
+ <CopyIcon aria-hidden="true" />
162
+ </button>
163
+ </div>
164
+ </div>
@@ -0,0 +1,16 @@
1
+ import type { BoardApi } from "@connectorvol/chessboard";
2
+ import type { Fen } from "./fen.svelte.js";
3
+ /**
4
+ * Представляет свойства компонента редактирования FEN.
5
+ */
6
+ interface Props {
7
+ api: BoardApi;
8
+ fen: Fen;
9
+ /**
10
+ * Возвращает признак ограничения ширины блока шириной доски (страница конструктора под колонкой доски).
11
+ */
12
+ constrainToBoardWidth?: boolean;
13
+ }
14
+ declare const EditFen: import("svelte").Component<Props, {}, "">;
15
+ type EditFen = ReturnType<typeof EditFen>;
16
+ export default EditFen;
@@ -0,0 +1,180 @@
1
+ <script lang="ts">
2
+ import type { BoardApi } from "@connectorvol/chessboard";
3
+ import type { Castling, Fen } from "./fen.svelte.js";
4
+ import { cn } from "../utils.js";
5
+
6
+ let isPossibleShortCastleWhite = $state(true);
7
+ let isPossibleShortCastleBlack = $state(true);
8
+ let isPossibleLongCastleWhite = $state(true);
9
+ let isPossibleLongCastleBlack = $state(true);
10
+
11
+ interface Props {
12
+ fen: Fen;
13
+ api: BoardApi;
14
+ }
15
+
16
+ const { fen, api: _api }: Props = $props();
17
+
18
+ $effect(() => {
19
+ isPossibleShortCastleWhite = fen.castling.includes("K");
20
+ isPossibleShortCastleBlack = fen.castling.includes("k");
21
+ isPossibleLongCastleWhite = fen.castling.includes("Q");
22
+ isPossibleLongCastleBlack = fen.castling.includes("q");
23
+ });
24
+
25
+ $effect(() => {
26
+ const castling = [
27
+ isPossibleShortCastleWhite,
28
+ isPossibleLongCastleWhite,
29
+ isPossibleShortCastleBlack,
30
+ isPossibleLongCastleBlack,
31
+ ]
32
+ .map((c, i) => (c ? "KQkq"[i] : ""))
33
+ .join("");
34
+ if (!castling) {
35
+ fen.castling = "-";
36
+ } else {
37
+ fen.castling = castling as Castling;
38
+ }
39
+ });
40
+
41
+ const enPassantMoves = $derived(fen.genPossibleEnPassantMoves());
42
+ $effect(() => {
43
+ if (enPassantMoves.length === 0) {
44
+ fen.enPassant = "-";
45
+ }
46
+ });
47
+ </script>
48
+
49
+ <div class="space-y-3 md:space-y-4">
50
+ <div class="space-y-2">
51
+ <label
52
+ for="move-select"
53
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
54
+ >
55
+ Установить ход
56
+ </label>
57
+ <select
58
+ id="move-select"
59
+ bind:value={fen.move}
60
+ class={cn(
61
+ "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] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
62
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
63
+ )}
64
+ >
65
+ <option value="w">w</option>
66
+ <option value="b">b</option>
67
+ </select>
68
+ </div>
69
+
70
+ <div class="space-y-2">
71
+ <label
72
+ for="enpassant-select"
73
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
74
+ >
75
+ Enpassant
76
+ </label>
77
+ <select
78
+ id="enpassant-select"
79
+ bind:value={fen.enPassant}
80
+ class={cn(
81
+ "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] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
82
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
83
+ )}
84
+ >
85
+ <option value="-">-</option>
86
+ {#each enPassantMoves as move}
87
+ <option value={move}>{move}</option>
88
+ {/each}
89
+ </select>
90
+ </div>
91
+
92
+ <div class="space-y-2 md:space-y-3">
93
+ <div class="text-sm font-medium leading-none">Возможность рокировки</div>
94
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-4">
95
+ <div class="flex items-center space-x-2">
96
+ <input
97
+ type="checkbox"
98
+ id="isPossibleShortCastleWhite"
99
+ bind:checked={isPossibleShortCastleWhite}
100
+ class={cn(
101
+ "peer h-4 w-4 shrink-0 rounded-sm border border-input ring-offset-background transition-colors",
102
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
103
+ "disabled:cursor-not-allowed disabled:opacity-50",
104
+ "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
105
+ "accent-primary",
106
+ )}
107
+ />
108
+ <label
109
+ for="isPossibleShortCastleWhite"
110
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
111
+ >
112
+ Белые 0-0
113
+ </label>
114
+ </div>
115
+
116
+ <div class="flex items-center space-x-2">
117
+ <input
118
+ type="checkbox"
119
+ id="isPossibleShortCastleBlack"
120
+ bind:checked={isPossibleShortCastleBlack}
121
+ class={cn(
122
+ "peer h-4 w-4 shrink-0 rounded-sm border border-input ring-offset-background transition-colors",
123
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
124
+ "disabled:cursor-not-allowed disabled:opacity-50",
125
+ "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
126
+ "accent-primary",
127
+ )}
128
+ />
129
+ <label
130
+ for="isPossibleShortCastleBlack"
131
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
132
+ >
133
+ Черные 0-0
134
+ </label>
135
+ </div>
136
+
137
+ <div class="flex items-center space-x-2">
138
+ <input
139
+ type="checkbox"
140
+ id="isPossibleLongCastleWhite"
141
+ bind:checked={isPossibleLongCastleWhite}
142
+ class={cn(
143
+ "peer h-4 w-4 shrink-0 rounded-sm border border-input ring-offset-background transition-colors",
144
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
145
+ "disabled:cursor-not-allowed disabled:opacity-50",
146
+ "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
147
+ "accent-primary",
148
+ )}
149
+ />
150
+ <label
151
+ for="isPossibleLongCastleWhite"
152
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
153
+ >
154
+ Белые 0-0-0
155
+ </label>
156
+ </div>
157
+
158
+ <div class="flex items-center space-x-2">
159
+ <input
160
+ type="checkbox"
161
+ id="isPossibleLongCastleBlack"
162
+ bind:checked={isPossibleLongCastleBlack}
163
+ class={cn(
164
+ "peer h-4 w-4 shrink-0 rounded-sm border border-input ring-offset-background transition-colors",
165
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
166
+ "disabled:cursor-not-allowed disabled:opacity-50",
167
+ "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
168
+ "accent-primary",
169
+ )}
170
+ />
171
+ <label
172
+ for="isPossibleLongCastleBlack"
173
+ class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
174
+ >
175
+ Черные 0-0-0
176
+ </label>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { BoardApi } from "@connectorvol/chessboard";
2
+ import type { Fen } from "./fen.svelte.js";
3
+ interface Props {
4
+ fen: Fen;
5
+ api: BoardApi;
6
+ }
7
+ declare const EditMove: import("svelte").Component<Props, {}, "">;
8
+ type EditMove = ReturnType<typeof EditMove>;
9
+ export default EditMove;