@connectorvol/chess-widgets 8.0.0 → 9.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/constants/default-board-appearance-settings.d.ts +9 -0
- package/dist/constants/default-board-appearance-settings.js +30 -0
- package/dist/constants/editable-board-settings.d.ts +3 -21
- package/dist/constants/editable-board-settings.js +24 -19
- package/dist/game-analyzer/GameAnalyzer.svelte +38 -19
- package/dist/game-analyzer/gameAnalyzer.svelte.js +9 -7
- package/dist/game-analyzer/types.d.ts +10 -2
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -0
- package/dist/position-editor/EditPanel.svelte +9 -6
- package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
- package/dist/puzzle/puzzleData.d.ts +4 -0
- package/dist/puzzle/puzzleData.js +10 -0
- package/dist/puzzle/puzzlePreviewConstants.d.ts +4 -0
- package/dist/puzzle/puzzlePreviewConstants.js +4 -0
- package/dist/puzzle/puzzlePreviewPathNags.d.ts +6 -0
- package/dist/puzzle/puzzlePreviewPathNags.js +35 -0
- package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +2 -4
- package/dist/puzzle/puzzleSolverForkAnnotations.js +13 -22
- package/dist/puzzle/puzzleStepPreviewSolver.d.ts +13 -1
- package/dist/puzzle/puzzleStepPreviewSolver.js +69 -9
- package/dist/puzzle/syncPuzzleBranchNags.d.ts +14 -0
- package/dist/puzzle/syncPuzzleBranchNags.js +125 -0
- package/dist/puzzle-creation/OpeningTagHoverPreview.svelte +81 -0
- package/dist/puzzle-creation/OpeningTagHoverPreview.svelte.d.ts +11 -0
- package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte +104 -32
- package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte.d.ts +16 -2
- package/dist/puzzle-creation/PuzzleCreationWizard.svelte +192 -202
- package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +47 -3
- package/dist/puzzle-creation/PuzzlePgnBoardTreeEditor.svelte +485 -76
- package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte +36 -0
- package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte.d.ts +12 -0
- package/dist/puzzle-creation/StepMoves.svelte +38 -18
- package/dist/puzzle-creation/StepMoves.svelte.d.ts +2 -1
- package/dist/puzzle-creation/StepPosition.svelte +15 -9
- package/dist/puzzle-creation/StepPreview.svelte +23 -10
- package/dist/puzzle-creation/StepPreview.svelte.d.ts +2 -0
- package/dist/puzzle-creation/StepTags.svelte +270 -0
- package/dist/puzzle-creation/StepTags.svelte.d.ts +19 -0
- package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.d.ts +9 -0
- package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.js +19 -0
- package/dist/puzzle-creation/createPuzzleLineEditingBoard.d.ts +10 -3
- package/dist/puzzle-creation/createPuzzleLineEditingBoard.js +15 -11
- package/dist/puzzle-creation/puzzleWizardState.d.ts +35 -0
- package/dist/puzzle-creation/puzzleWizardState.js +37 -0
- package/dist/puzzle-creation/types.d.ts +35 -5
- package/package.json +20 -17
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
|
|
2
|
+
/**
|
|
3
|
+
* Представляет визуальные настройки шахматной доски по умолчанию для виджетов chess-widgets
|
|
4
|
+
* (без `boardSize`, `orientation`, `draggable` — их подставляет вызывающий код).
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_BOARD_APPEARANCE_SETTINGS: {
|
|
7
|
+
play: TChessBoardPlaySettings;
|
|
8
|
+
design: TChessBoardDesignSettings;
|
|
9
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DEFAULT_DESIGN_SETTINGS, Draggable, PieceInputMode } from "@connectorvol/chessboard";
|
|
2
|
+
import { Color } from "@connectorvol/shared";
|
|
3
|
+
/**
|
|
4
|
+
* Представляет визуальные настройки шахматной доски по умолчанию для виджетов chess-widgets
|
|
5
|
+
* (без `boardSize`, `orientation`, `draggable` — их подставляет вызывающий код).
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_BOARD_APPEARANCE_SETTINGS = {
|
|
8
|
+
play: {
|
|
9
|
+
isResizable: true,
|
|
10
|
+
orientation: Color.WHITE,
|
|
11
|
+
boardSize: 38,
|
|
12
|
+
draggable: Draggable.NONE,
|
|
13
|
+
pieceInputMode: PieceInputMode.SOURCE_DRAG,
|
|
14
|
+
allowPreMove: true,
|
|
15
|
+
clearMarkersOnLeftClick: true,
|
|
16
|
+
showSquareBadges: true,
|
|
17
|
+
animationTime: 300,
|
|
18
|
+
editSettings: null,
|
|
19
|
+
needBorderRadius: false,
|
|
20
|
+
},
|
|
21
|
+
design: {
|
|
22
|
+
...DEFAULT_DESIGN_SETTINGS,
|
|
23
|
+
showBoardCoordinates: false,
|
|
24
|
+
autoQueenPromotion: false,
|
|
25
|
+
showPossibleMoveIndicators: true,
|
|
26
|
+
border: false,
|
|
27
|
+
woodTexture: false,
|
|
28
|
+
boardGradient: true,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
|
|
2
2
|
/**
|
|
3
3
|
* Представляет настройки редактируемой доски по умолчанию для конструктора позиции и мастера задач.
|
|
4
4
|
*/
|
|
5
5
|
export declare const DEFAULT_EDITABLE_BOARD_SETTINGS: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
showBoardCoordinates: 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
|
-
};
|
|
6
|
+
play: TChessBoardPlaySettings;
|
|
7
|
+
design: TChessBoardDesignSettings;
|
|
26
8
|
};
|
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
import { Draggable, PieceInputMode } from "@connectorvol/chessboard";
|
|
1
|
+
import { DEFAULT_DESIGN_SETTINGS, Draggable, PieceInputMode } from "@connectorvol/chessboard";
|
|
2
2
|
import { Color } from "@connectorvol/shared";
|
|
3
3
|
/**
|
|
4
4
|
* Представляет настройки редактируемой доски по умолчанию для конструктора позиции и мастера задач.
|
|
5
5
|
*/
|
|
6
6
|
export const DEFAULT_EDITABLE_BOARD_SETTINGS = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
piece: null,
|
|
24
|
-
color: null,
|
|
7
|
+
play: {
|
|
8
|
+
isResizable: true,
|
|
9
|
+
orientation: Color.WHITE,
|
|
10
|
+
boardSize: 38,
|
|
11
|
+
draggable: Draggable.BOTH,
|
|
12
|
+
pieceInputMode: PieceInputMode.SOURCE_DRAG,
|
|
13
|
+
allowPreMove: true,
|
|
14
|
+
clearMarkersOnLeftClick: true,
|
|
15
|
+
showSquareBadges: true,
|
|
16
|
+
animationTime: 300,
|
|
17
|
+
editSettings: {
|
|
18
|
+
mode: {
|
|
19
|
+
type: "delete",
|
|
20
|
+
piece: null,
|
|
21
|
+
color: null,
|
|
22
|
+
},
|
|
25
23
|
},
|
|
24
|
+
needBorderRadius: false,
|
|
25
|
+
},
|
|
26
|
+
design: {
|
|
27
|
+
...DEFAULT_DESIGN_SETTINGS,
|
|
28
|
+
border: false,
|
|
29
|
+
showBoardCoordinates: false,
|
|
30
|
+
markerConfig: null,
|
|
26
31
|
},
|
|
27
32
|
};
|
|
@@ -1,48 +1,67 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { TGameAnalyzerProps } from "./types.js";
|
|
3
3
|
|
|
4
|
-
import { Chessboard
|
|
5
|
-
import {
|
|
4
|
+
import { Chessboard } from "@connectorvol/chessboard";
|
|
5
|
+
import {
|
|
6
|
+
TREE_VIEWER_LIGHT_THEME,
|
|
7
|
+
TreeViewer,
|
|
8
|
+
TreeViewerPanelManager,
|
|
9
|
+
} from "@connectorvol/tree";
|
|
6
10
|
import { Game } from "./gameAnalyzer.svelte.js";
|
|
7
11
|
import { untrack } from "svelte";
|
|
12
|
+
import { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "../constants/default-board-appearance-settings.js";
|
|
8
13
|
|
|
9
14
|
let {
|
|
10
15
|
pgn,
|
|
11
16
|
onChangePGN,
|
|
12
17
|
boardTheme,
|
|
13
|
-
boardAppearanceSettings,
|
|
18
|
+
boardAppearanceSettings = DEFAULT_BOARD_APPEARANCE_SETTINGS,
|
|
14
19
|
editMode = true,
|
|
20
|
+
treeViewerTheme = TREE_VIEWER_LIGHT_THEME,
|
|
15
21
|
class: className,
|
|
16
22
|
}: TGameAnalyzerProps = $props();
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
...DEFAULT_BOARD_SETTINGS,
|
|
23
|
-
...boardAppearanceSettings,
|
|
24
|
-
boardSize: "auto",
|
|
25
|
-
},
|
|
24
|
+
let game = $derived(
|
|
25
|
+
new Game(
|
|
26
|
+
untrack(() => pgn),
|
|
27
|
+
boardAppearanceSettings?.play ?? {},
|
|
26
28
|
"chess",
|
|
27
29
|
boardTheme,
|
|
28
|
-
)
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
let boardSize = $derived(
|
|
34
|
+
game.chessboard.getBoard().settingsService.boardSize,
|
|
35
|
+
);
|
|
36
|
+
let isAutoSize = $derived(boardSize === "auto");
|
|
29
37
|
|
|
30
38
|
function handleChangeDirty(_setIsDirty: (value: boolean) => void) {
|
|
31
|
-
console.log(
|
|
39
|
+
console.log("handleChangeDirty");
|
|
32
40
|
const exported = game.exportPgn();
|
|
33
41
|
onChangePGN?.(exported);
|
|
34
42
|
}
|
|
35
|
-
|
|
36
43
|
</script>
|
|
37
44
|
|
|
38
45
|
<div class={["mx-4 lg:container lg:mx-auto gap-4", className]}>
|
|
39
46
|
<div class="flex justify-center relative lg:gap-4 flex-col lg:flex-row">
|
|
40
|
-
<div
|
|
41
|
-
|
|
47
|
+
<div
|
|
48
|
+
class={[
|
|
49
|
+
"w-full",
|
|
50
|
+
isAutoSize ? "lg:w-2/5" : "lg:w-auto lg:min-w-[3.75rem]",
|
|
51
|
+
]}
|
|
52
|
+
style={!isAutoSize && typeof boardSize === "number"
|
|
53
|
+
? `flex-basis: ${boardSize}rem;`
|
|
54
|
+
: ""}
|
|
55
|
+
>
|
|
56
|
+
<Chessboard facade={game.chessboard} design={{ theme: game.theme }} />
|
|
42
57
|
</div>
|
|
43
58
|
|
|
44
|
-
|
|
45
|
-
|
|
59
|
+
<div
|
|
60
|
+
class={[
|
|
61
|
+
"w-full flex lg:flex-col flex-col-reverse h-full",
|
|
62
|
+
isAutoSize ? "lg:w-3/5" : "lg:flex-1 lg:min-w-[20rem]",
|
|
63
|
+
]}
|
|
64
|
+
>
|
|
46
65
|
<div class="h-full">
|
|
47
66
|
<TreeViewer
|
|
48
67
|
className="h-[calc(100dvh-8rem)]"
|
|
@@ -51,8 +70,8 @@
|
|
|
51
70
|
onDeleteVariant={game.onDeleteVariant}
|
|
52
71
|
setChessFen={game.setChessFen}
|
|
53
72
|
setChessboardFen={game.setChessboardFen}
|
|
54
|
-
pieceSet={game.chessboard.chessSet}
|
|
55
73
|
{editMode}
|
|
74
|
+
theme={treeViewerTheme}
|
|
56
75
|
onChangeDirty={handleChangeDirty}
|
|
57
76
|
/>
|
|
58
77
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Color, calculatePly, generateId } from "@connectorvol/shared";
|
|
2
|
-
import { CHESSBOARD_THEMES, createBoardApi,
|
|
2
|
+
import { CHESSBOARD_THEMES, createBoardApi, DEFAULT_PLAY_SETTINGS, Draggable, syncMoveEvaluationNagBadge, } from "@connectorvol/chessboard";
|
|
3
3
|
import { PgnOps } from "@connectorvol/chessops/pgnOps.svelte";
|
|
4
4
|
import { transformPgnToChessNode, createPgnFromTree } from "@connectorvol/tree";
|
|
5
5
|
import { ChessTree } from "@connectorvol/tree";
|
|
@@ -9,23 +9,25 @@ export class Game {
|
|
|
9
9
|
chessboard;
|
|
10
10
|
chessTree;
|
|
11
11
|
previousNode;
|
|
12
|
-
|
|
12
|
+
playSettings;
|
|
13
13
|
variant;
|
|
14
14
|
boardTheme;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/** Возвращает активную тему доски, реактивный `$state` для биндинга в UI. */
|
|
16
|
+
theme = $state(CHESSBOARD_THEMES.blue);
|
|
17
|
+
constructor(pgn, chessBoardSettings, variant = "chess", boardTheme) {
|
|
18
|
+
this.playSettings = { ...DEFAULT_PLAY_SETTINGS, ...chessBoardSettings };
|
|
17
19
|
this.variant = variant;
|
|
18
20
|
this.boardTheme = boardTheme ?? CHESSBOARD_THEMES.blue;
|
|
21
|
+
this.theme = this.boardTheme;
|
|
19
22
|
const { rootNode, initialFen: position } = transformPgnToChessNode(pgn);
|
|
20
23
|
this.chessTree = new ChessTree(rootNode);
|
|
21
24
|
this.chess = new PgnOps(position, variant);
|
|
22
25
|
this.chessboard = createBoardApi({
|
|
23
26
|
fen: this.chess.fen(),
|
|
24
|
-
|
|
25
|
-
...this.
|
|
27
|
+
playSettings: {
|
|
28
|
+
...this.playSettings,
|
|
26
29
|
draggable: this.chess.turn() === Color.WHITE ? Draggable.WHITE : Draggable.BLACK,
|
|
27
30
|
},
|
|
28
|
-
theme: this.boardTheme,
|
|
29
31
|
});
|
|
30
32
|
this.chessboard.setActions(this.buildActions());
|
|
31
33
|
this.previousNode = this.chessTree.currentNode;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { ChessboardTheme } from "@connectorvol/chessboard";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TChessBoardDesignSettings, TChessBoardPlaySettings } from "@connectorvol/chessboard";
|
|
3
|
+
import type { TTreeViewerTheme } from "@connectorvol/tree";
|
|
3
4
|
/**
|
|
4
5
|
* Представляет тип визуальных настроек шахматной доски для анализатора партии.
|
|
5
6
|
*/
|
|
6
|
-
export type TChessboardAppearanceSettings =
|
|
7
|
+
export type TChessboardAppearanceSettings = {
|
|
8
|
+
play?: Omit<TChessBoardPlaySettings, "boardSize" | "orientation" | "draggable">;
|
|
9
|
+
design?: Partial<TChessBoardDesignSettings>;
|
|
10
|
+
};
|
|
7
11
|
/**
|
|
8
12
|
* Представляет свойства компонента анализатора партии.
|
|
9
13
|
*/
|
|
@@ -28,6 +32,10 @@ export interface TGameAnalyzerProps {
|
|
|
28
32
|
* Возвращает признак режима правки дерева (по умолчанию `true`).
|
|
29
33
|
*/
|
|
30
34
|
editMode?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Возвращает тему `TreeViewer` (фон, границы, текст). По умолчанию `TREE_VIEWER_LIGHT_THEME`.
|
|
37
|
+
*/
|
|
38
|
+
treeViewerTheme?: TTreeViewerTheme;
|
|
31
39
|
/**
|
|
32
40
|
* Возвращает дополнительные классы Tailwind для корневого контейнера.
|
|
33
41
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
export { default as PuzzleCreationWizard } from "./puzzle-creation/PuzzleCreationWizard.svelte";
|
|
2
|
+
export { default as PuzzleWizardTagsStep } from "./puzzle-creation/PuzzleWizardTagsStep.svelte";
|
|
2
3
|
export { default as PuzzlePgnBoardTreeEditor } from "./puzzle-creation/PuzzlePgnBoardTreeEditor.svelte";
|
|
3
4
|
export type { TPuzzleCreationWizardProps, TPuzzlePgnBoardTreeEditorOutcome, TPuzzlePgnBoardTreeEditorProps, } from "./puzzle-creation/types.js";
|
|
4
|
-
export type {
|
|
5
|
+
export type { TPuzzleWizardCoreState, TPuzzleWizardState, } from "./puzzle-creation/puzzleWizardState.js";
|
|
6
|
+
export { PUZZLE_WIZARD_CORE_STEPS, PUZZLE_WIZARD_TAGS_STEP, createInitialPuzzleWizardCoreState, createInitialPuzzleWizardState, puzzleWizardSeedFen, } from "./puzzle-creation/puzzleWizardState.js";
|
|
7
|
+
export type { TWizardStep, TWizardStepContext, } from "@connectorvol/shared";
|
|
8
|
+
export { Wizard, getWizardContext, setWizardContext } from "@connectorvol/shared";
|
|
9
|
+
export type { TPuzzleCreatedPayload, TPuzzleTags } from "./puzzle/puzzleCreatedPayload.js";
|
|
5
10
|
export { puzzlePartsFromFullPgn } from "./puzzle/puzzleCreatedPayload.js";
|
|
6
11
|
export type { PuzzleData } from "./puzzle/puzzleData.js";
|
|
7
12
|
export { PUZZLE_EMPTY_BOARD_FEN, createInitialPuzzleData, createEmptyTreeFromFen, getMainLineFromTree, } from "./puzzle/puzzleData.js";
|
|
8
13
|
export type { TSolverMoveOutcome } from "./puzzle/puzzleStepPreviewSolver.js";
|
|
9
14
|
export { PREVIEW_WRONG_NO_VARIANT_MESSAGE } from "./puzzle/puzzlePreviewConstants.js";
|
|
15
|
+
export { validatePuzzleSolverForkAnnotations } from "./puzzle/puzzleSolverForkAnnotations.js";
|
|
16
|
+
export { normalizePuzzleTreeMainLine, syncPuzzleBranchNags, syncPuzzleBranchNagsOnTree, } from "./puzzle/syncPuzzleBranchNags.js";
|
|
10
17
|
export { DEFAULT_EDITABLE_BOARD_SETTINGS } from "./constants/editable-board-settings.js";
|
|
18
|
+
export { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "./constants/default-board-appearance-settings.js";
|
|
11
19
|
export { default as EditFen } from "./position-editor/EditFen.svelte";
|
|
12
20
|
export { default as EditMove } from "./position-editor/EditMove.svelte";
|
|
13
21
|
export { default as EditPanel } from "./position-editor/EditPanel.svelte";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export { default as PuzzleCreationWizard } from "./puzzle-creation/PuzzleCreationWizard.svelte";
|
|
2
|
+
export { default as PuzzleWizardTagsStep } from "./puzzle-creation/PuzzleWizardTagsStep.svelte";
|
|
2
3
|
export { default as PuzzlePgnBoardTreeEditor } from "./puzzle-creation/PuzzlePgnBoardTreeEditor.svelte";
|
|
4
|
+
export { PUZZLE_WIZARD_CORE_STEPS, PUZZLE_WIZARD_TAGS_STEP, createInitialPuzzleWizardCoreState, createInitialPuzzleWizardState, puzzleWizardSeedFen, } from "./puzzle-creation/puzzleWizardState.js";
|
|
5
|
+
export { Wizard, getWizardContext, setWizardContext } from "@connectorvol/shared";
|
|
3
6
|
export { puzzlePartsFromFullPgn } from "./puzzle/puzzleCreatedPayload.js";
|
|
4
7
|
export { PUZZLE_EMPTY_BOARD_FEN, createInitialPuzzleData, createEmptyTreeFromFen, getMainLineFromTree, } from "./puzzle/puzzleData.js";
|
|
5
8
|
export { PREVIEW_WRONG_NO_VARIANT_MESSAGE } from "./puzzle/puzzlePreviewConstants.js";
|
|
9
|
+
export { validatePuzzleSolverForkAnnotations } from "./puzzle/puzzleSolverForkAnnotations.js";
|
|
10
|
+
export { normalizePuzzleTreeMainLine, syncPuzzleBranchNags, syncPuzzleBranchNagsOnTree, } from "./puzzle/syncPuzzleBranchNags.js";
|
|
6
11
|
export { DEFAULT_EDITABLE_BOARD_SETTINGS } from "./constants/editable-board-settings.js";
|
|
12
|
+
export { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "./constants/default-board-appearance-settings.js";
|
|
7
13
|
export { default as EditFen } from "./position-editor/EditFen.svelte";
|
|
8
14
|
export { default as EditMove } from "./position-editor/EditMove.svelte";
|
|
9
15
|
export { default as EditPanel } from "./position-editor/EditPanel.svelte";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { BoardApi } from "@connectorvol/chessboard";
|
|
3
|
+
import { CHESSBOARD_PIECE_SETS } from "@connectorvol/chessboard";
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
color: "w" | "b";
|
|
@@ -8,6 +9,8 @@
|
|
|
8
9
|
|
|
9
10
|
const { color, api }: Props = $props();
|
|
10
11
|
|
|
12
|
+
const pieceSet = $derived(CHESSBOARD_PIECE_SETS[api.chessSet]);
|
|
13
|
+
|
|
11
14
|
function onSelect(
|
|
12
15
|
promotion: "q" | "r" | "b" | "n" | "k" | "p" | "delete" | "drag"
|
|
13
16
|
) {
|
|
@@ -78,7 +81,7 @@
|
|
|
78
81
|
class="flex aspect-square w-1/8"
|
|
79
82
|
onclick={() => onSelect("p")}
|
|
80
83
|
>
|
|
81
|
-
{@render
|
|
84
|
+
{@render pieceSet.pawn(color === "w" ? "w" : "b")}
|
|
82
85
|
</button>
|
|
83
86
|
<button
|
|
84
87
|
data-testid="edit-panel-knight-{color}"
|
|
@@ -89,7 +92,7 @@
|
|
|
89
92
|
class="flex aspect-square w-1/8"
|
|
90
93
|
onclick={() => onSelect("n")}
|
|
91
94
|
>
|
|
92
|
-
{@render
|
|
95
|
+
{@render pieceSet.knight(color)}
|
|
93
96
|
</button>
|
|
94
97
|
<button
|
|
95
98
|
data-testid="edit-panel-bishop-{color}"
|
|
@@ -100,7 +103,7 @@
|
|
|
100
103
|
class="flex aspect-square w-1/8"
|
|
101
104
|
onclick={() => onSelect("b")}
|
|
102
105
|
>
|
|
103
|
-
{@render
|
|
106
|
+
{@render pieceSet.bishop(color)}
|
|
104
107
|
</button>
|
|
105
108
|
<button
|
|
106
109
|
data-testid="edit-panel-rook-{color}"
|
|
@@ -111,7 +114,7 @@
|
|
|
111
114
|
class="flex aspect-square w-1/8"
|
|
112
115
|
onclick={() => onSelect("r")}
|
|
113
116
|
>
|
|
114
|
-
{@render
|
|
117
|
+
{@render pieceSet.rook(color)}
|
|
115
118
|
</button>
|
|
116
119
|
<button
|
|
117
120
|
data-testid="edit-panel-queen-{color}"
|
|
@@ -122,7 +125,7 @@
|
|
|
122
125
|
class="flex aspect-square w-1/8"
|
|
123
126
|
onclick={() => onSelect("q")}
|
|
124
127
|
>
|
|
125
|
-
{@render
|
|
128
|
+
{@render pieceSet.queen(color)}
|
|
126
129
|
</button>
|
|
127
130
|
<button
|
|
128
131
|
data-testid="edit-panel-king-{color}"
|
|
@@ -133,7 +136,7 @@
|
|
|
133
136
|
class="flex aspect-square w-1/8"
|
|
134
137
|
onclick={() => onSelect("k")}
|
|
135
138
|
>
|
|
136
|
-
{@render
|
|
139
|
+
{@render pieceSet.king(color)}
|
|
137
140
|
</button>
|
|
138
141
|
<button
|
|
139
142
|
aria-label="Delete piece"
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Представляет выбранные теги задачи (дебют и тактические приёмы).
|
|
3
|
+
*/
|
|
4
|
+
export interface TPuzzleTags {
|
|
5
|
+
/**
|
|
6
|
+
* Возвращает идентификатор выбранного дебютного тега или `undefined`, если дебют не выбран.
|
|
7
|
+
*/
|
|
8
|
+
opening?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Возвращает идентификаторы выбранных тактических тегов (не более трёх).
|
|
11
|
+
*/
|
|
12
|
+
tactics: string[];
|
|
13
|
+
}
|
|
1
14
|
/**
|
|
2
15
|
* Представляет данные задачи, передаваемые колбэку после завершения мастера.
|
|
3
16
|
*/
|
|
@@ -10,6 +23,10 @@ export interface TPuzzleCreatedPayload {
|
|
|
10
23
|
* Возвращает начальный FEN задачи (из тега FEN или начальная позиция по умолчанию).
|
|
11
24
|
*/
|
|
12
25
|
fen: string;
|
|
26
|
+
/**
|
|
27
|
+
* Возвращает выбранные теги задачи, если в мастере включён шаг тегов.
|
|
28
|
+
*/
|
|
29
|
+
tags?: TPuzzleTags;
|
|
13
30
|
}
|
|
14
31
|
/**
|
|
15
32
|
* Представляет разбор полного PGN из дерева задачи на FEN и чистую строку ходов.
|
|
@@ -25,6 +25,10 @@ export declare function createInitialPuzzleData(initialFen?: string): PuzzleData
|
|
|
25
25
|
* Возвращает корень дерева (TChessTree) для использования в ChessTree.
|
|
26
26
|
*/
|
|
27
27
|
export declare function createEmptyTreeFromFen(initialFen: string): TChessTree;
|
|
28
|
+
/**
|
|
29
|
+
* Представляет создание дерева превью для ученика с опциональным вступительным комментарием PGN до первого хода.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createStudentPreviewTreeFromFen(initialFen: string, introComments?: string[]): TChessTree;
|
|
28
32
|
/**
|
|
29
33
|
* Представляет извлечение главной линии (первого варианта) из дерева ходов.
|
|
30
34
|
* Возвращает массив ходов в нотации SAN.
|
|
@@ -35,6 +35,16 @@ export function createEmptyTreeFromFen(initialFen) {
|
|
|
35
35
|
headers,
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Представляет создание дерева превью для ученика с опциональным вступительным комментарием PGN до первого хода.
|
|
40
|
+
*/
|
|
41
|
+
export function createStudentPreviewTreeFromFen(initialFen, introComments) {
|
|
42
|
+
const tree = createEmptyTreeFromFen(initialFen);
|
|
43
|
+
if (introComments !== undefined && introComments.length > 0) {
|
|
44
|
+
tree.comments = [...introComments];
|
|
45
|
+
}
|
|
46
|
+
return tree;
|
|
47
|
+
}
|
|
38
48
|
/**
|
|
39
49
|
* Представляет извлечение главной линии (первого варианта) из дерева ходов.
|
|
40
50
|
* Возвращает массив ходов в нотации SAN.
|
|
@@ -2,3 +2,7 @@
|
|
|
2
2
|
* Представляет текст сообщения «нет такого продолжения» для превью задачи (как на шаге 3).
|
|
3
3
|
*/
|
|
4
4
|
export declare const PREVIEW_WRONG_NO_VARIANT_MESSAGE = "\u0422\u0430\u043A\u043E\u0433\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u044F \u043D\u0435\u0442 \u0441\u0440\u0435\u0434\u0438 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432 \u0437\u0430\u0434\u0430\u0447\u0438.";
|
|
5
|
+
/**
|
|
6
|
+
* Представляет задержку (мс) перед откатом ошибочного хода на доске после показа NAG ✗.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PREVIEW_WRONG_MOVE_REVEAL_MS = 700;
|
|
@@ -2,3 +2,7 @@
|
|
|
2
2
|
* Представляет текст сообщения «нет такого продолжения» для превью задачи (как на шаге 3).
|
|
3
3
|
*/
|
|
4
4
|
export const PREVIEW_WRONG_NO_VARIANT_MESSAGE = "Такого продолжения нет среди вариантов задачи.";
|
|
5
|
+
/**
|
|
6
|
+
* Представляет задержку (мс) перед откатом ошибочного хода на доске после показа NAG ✗.
|
|
7
|
+
*/
|
|
8
|
+
export const PREVIEW_WRONG_MOVE_REVEAL_MS = 700;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ChessTree } from "@connectorvol/tree";
|
|
2
|
+
import type { Color } from "@connectorvol/shared";
|
|
3
|
+
/**
|
|
4
|
+
* Представляет синхронизацию NAG ✓ ходов решателя в дереве решения по пройденному пути превью.
|
|
5
|
+
*/
|
|
6
|
+
export declare function applyPlayedSolverNagsAlongPath(solution: ChessTree, path: number[], solverColor: Color): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PUZZLE_BRANCH_CORRECT_NAG_ID, PUZZLE_BRANCH_WRONG_NAG_ID, } from "@connectorvol/shared";
|
|
2
|
+
import { puzzlePreviewSideToMoveFromFen } from "./puzzleStepPreviewSolver.js";
|
|
3
|
+
/**
|
|
4
|
+
* Представляет синхронизацию NAG ✓ ходов решателя в дереве решения по пройденному пути превью.
|
|
5
|
+
*/
|
|
6
|
+
export function applyPlayedSolverNagsAlongPath(solution, path, solverColor) {
|
|
7
|
+
let src = solution.rootNode.moves;
|
|
8
|
+
let changed = false;
|
|
9
|
+
for (const idx of path) {
|
|
10
|
+
const next = src.children[idx];
|
|
11
|
+
if (!next)
|
|
12
|
+
break;
|
|
13
|
+
const mover = puzzlePreviewSideToMoveFromFen(src.data.fen);
|
|
14
|
+
const isSolverMove = mover === solverColor;
|
|
15
|
+
if (isSolverMove) {
|
|
16
|
+
let nags = next.data.nags ? [...next.data.nags] : [];
|
|
17
|
+
if (!nags.includes(PUZZLE_BRANCH_CORRECT_NAG_ID)) {
|
|
18
|
+
nags.push(PUZZLE_BRANCH_CORRECT_NAG_ID);
|
|
19
|
+
}
|
|
20
|
+
nags = nags.filter((n) => n !== PUZZLE_BRANCH_WRONG_NAG_ID);
|
|
21
|
+
const normalizedNags = nags.length > 0 ? nags : undefined;
|
|
22
|
+
const prevKey = JSON.stringify(next.data.nags ?? []);
|
|
23
|
+
const nextKey = JSON.stringify(normalizedNags ?? []);
|
|
24
|
+
if (prevKey !== nextKey) {
|
|
25
|
+
next.data.nags = normalizedNags ? [...normalizedNags] : undefined;
|
|
26
|
+
changed = true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
src = next;
|
|
30
|
+
}
|
|
31
|
+
if (changed) {
|
|
32
|
+
solution.mutationVersion++;
|
|
33
|
+
}
|
|
34
|
+
return changed;
|
|
35
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { ChessTree } from "@connectorvol/tree";
|
|
2
2
|
import { Color } from "@connectorvol/shared";
|
|
3
3
|
/**
|
|
4
|
-
* Представляет проверку линии решения задачи:
|
|
5
|
-
*
|
|
6
|
-
* решателя и окончание вариантов на развилке соперника ходом решателя вне поддерева
|
|
7
|
-
* после хода решателя с меткой «неверное решение».
|
|
4
|
+
* Представляет проверку линии решения задачи: разметка развилок со стороны решателя
|
|
5
|
+
* и окончание вариантов на развилке соперника ходом решателя.
|
|
8
6
|
*/
|
|
9
7
|
export declare function validatePuzzleSolverForkAnnotations(tree: ChessTree, solverColor: Color): {
|
|
10
8
|
ok: true;
|
|
@@ -23,31 +23,15 @@ function solverJustPlayedLastOnLine(fullFen, solverColor) {
|
|
|
23
23
|
return sideToMoveFromFullFen(fullFen) !== solverColor;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Представляет проверку линии решения задачи:
|
|
27
|
-
*
|
|
28
|
-
* решателя и окончание вариантов на развилке соперника ходом решателя вне поддерева
|
|
29
|
-
* после хода решателя с меткой «неверное решение».
|
|
26
|
+
* Представляет проверку линии решения задачи: разметка развилок со стороны решателя
|
|
27
|
+
* и окончание вариантов на развилке соперника ходом решателя.
|
|
30
28
|
*/
|
|
31
29
|
export function validatePuzzleSolverForkAnnotations(tree, solverColor) {
|
|
32
|
-
function walk(node,
|
|
33
|
-
const puzzleForkNags = node.data.nags ?? [];
|
|
34
|
-
const hasPuzzleForkMarker = puzzleForkNags.includes(PUZZLE_BRANCH_CORRECT_NAG_ID) ||
|
|
35
|
-
puzzleForkNags.includes(PUZZLE_BRANCH_WRONG_NAG_ID);
|
|
36
|
-
if (hasPuzzleForkMarker && (!parent || parent.children.length <= 1)) {
|
|
37
|
-
return {
|
|
38
|
-
ok: false,
|
|
39
|
-
reason: "Метки верного или неверного хода задачи (✓ / ✗) можно ставить только на ход из развилки: у родительской позиции должно быть несколько вариантов хода.",
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
if (ancestorsContainWrongMarkedSolver && hasPuzzleForkMarker) {
|
|
43
|
-
return {
|
|
44
|
-
ok: false,
|
|
45
|
-
reason: "Внутри продолжения после хода решателя с меткой ✗ «неверное решение» не ставьте метки ✓ и ✗ на последующих развилках.",
|
|
46
|
-
};
|
|
47
|
-
}
|
|
30
|
+
function walk(node, ancestorsContainWrongMarkedSolver) {
|
|
48
31
|
if (node.children.length > 1) {
|
|
49
32
|
const mover = sideToMoveFromFullFen(node.data.fen);
|
|
50
33
|
if (mover === solverColor && !ancestorsContainWrongMarkedSolver) {
|
|
34
|
+
let hasAnyCorrect = false;
|
|
51
35
|
for (const child of node.children) {
|
|
52
36
|
const nags = child.data.nags ?? [];
|
|
53
37
|
const markedCorrect = nags.includes(PUZZLE_BRANCH_CORRECT_NAG_ID);
|
|
@@ -59,6 +43,7 @@ export function validatePuzzleSolverForkAnnotations(tree, solverColor) {
|
|
|
59
43
|
};
|
|
60
44
|
}
|
|
61
45
|
if (markedCorrect) {
|
|
46
|
+
hasAnyCorrect = true;
|
|
62
47
|
const leaf = tailAlongFirstVariation(child);
|
|
63
48
|
if (!solverJustPlayedLastOnLine(leaf.data.fen, solverColor)) {
|
|
64
49
|
return {
|
|
@@ -68,6 +53,12 @@ export function validatePuzzleSolverForkAnnotations(tree, solverColor) {
|
|
|
68
53
|
}
|
|
69
54
|
}
|
|
70
55
|
}
|
|
56
|
+
if (!hasAnyCorrect) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
reason: "На каждой развилке со стороны решателя хотя бы один вариант должен быть отмечен как верный ход (✓).",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
71
62
|
}
|
|
72
63
|
else if (mover !== solverColor && !ancestorsContainWrongMarkedSolver) {
|
|
73
64
|
for (const child of node.children) {
|
|
@@ -84,11 +75,11 @@ export function validatePuzzleSolverForkAnnotations(tree, solverColor) {
|
|
|
84
75
|
const wrongMarkedAmongAncestorsForChildren = ancestorsContainWrongMarkedSolver ||
|
|
85
76
|
(node.data.nags ?? []).includes(PUZZLE_BRANCH_WRONG_NAG_ID);
|
|
86
77
|
for (const child of node.children) {
|
|
87
|
-
const sub = walk(child,
|
|
78
|
+
const sub = walk(child, wrongMarkedAmongAncestorsForChildren);
|
|
88
79
|
if (!sub.ok)
|
|
89
80
|
return sub;
|
|
90
81
|
}
|
|
91
82
|
return { ok: true };
|
|
92
83
|
}
|
|
93
|
-
return walk(tree.rootNode.moves,
|
|
84
|
+
return walk(tree.rootNode.moves, false);
|
|
94
85
|
}
|
|
@@ -28,7 +28,19 @@ export declare function puzzlePreviewFindChildIndexForPlayedSan(cursorFen: strin
|
|
|
28
28
|
/**
|
|
29
29
|
* Представляет классификацию хода решателя на развилке (или на единственном продолжении).
|
|
30
30
|
*/
|
|
31
|
-
export declare function puzzlePreviewClassifySolverMove(cursorNode: ChessTreeNode, cursorFen: string, playedSan: string,
|
|
31
|
+
export declare function puzzlePreviewClassifySolverMove(cursorNode: ChessTreeNode, cursorFen: string, playedSan: string, _solverColor: Color): TSolverMoveOutcome;
|
|
32
|
+
/**
|
|
33
|
+
* Представляет сбор данных узла для произвольного неверного хода решателя (нет в PGN).
|
|
34
|
+
*/
|
|
35
|
+
export declare function puzzlePreviewBuildAdHocWrongMoveData(cursorFen: string, san: string): ChessTreeNode["data"] | null;
|
|
36
|
+
/**
|
|
37
|
+
* Представляет добавление отсутствующего в PGN хода как новой ветки дерева решения с NAG ✗.
|
|
38
|
+
*/
|
|
39
|
+
export declare function puzzlePreviewAddAdHocWrongMoveToSolutionTree(solutionTree: ChessTree, cursorPath: number[], cursorFen: string, san: string): ChessTreeNode | null;
|
|
40
|
+
/**
|
|
41
|
+
* Представляет пересчёт пути индексов после перестановки детей на развилках.
|
|
42
|
+
*/
|
|
43
|
+
export declare function puzzlePreviewRemapPathAfterReorder(rootMoves: ChessTreeNode, path: number[]): number[];
|
|
32
44
|
/**
|
|
33
45
|
* Представляет проверку: линия закончилась ключевым ходом решателя (ход соперника, лист дерева).
|
|
34
46
|
*/
|