@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.
Files changed (47) hide show
  1. package/dist/constants/default-board-appearance-settings.d.ts +9 -0
  2. package/dist/constants/default-board-appearance-settings.js +30 -0
  3. package/dist/constants/editable-board-settings.d.ts +3 -21
  4. package/dist/constants/editable-board-settings.js +24 -19
  5. package/dist/game-analyzer/GameAnalyzer.svelte +38 -19
  6. package/dist/game-analyzer/gameAnalyzer.svelte.js +9 -7
  7. package/dist/game-analyzer/types.d.ts +10 -2
  8. package/dist/index.d.ts +9 -1
  9. package/dist/index.js +6 -0
  10. package/dist/position-editor/EditPanel.svelte +9 -6
  11. package/dist/puzzle/puzzleCreatedPayload.d.ts +17 -0
  12. package/dist/puzzle/puzzleData.d.ts +4 -0
  13. package/dist/puzzle/puzzleData.js +10 -0
  14. package/dist/puzzle/puzzlePreviewConstants.d.ts +4 -0
  15. package/dist/puzzle/puzzlePreviewConstants.js +4 -0
  16. package/dist/puzzle/puzzlePreviewPathNags.d.ts +6 -0
  17. package/dist/puzzle/puzzlePreviewPathNags.js +35 -0
  18. package/dist/puzzle/puzzleSolverForkAnnotations.d.ts +2 -4
  19. package/dist/puzzle/puzzleSolverForkAnnotations.js +13 -22
  20. package/dist/puzzle/puzzleStepPreviewSolver.d.ts +13 -1
  21. package/dist/puzzle/puzzleStepPreviewSolver.js +69 -9
  22. package/dist/puzzle/syncPuzzleBranchNags.d.ts +14 -0
  23. package/dist/puzzle/syncPuzzleBranchNags.js +125 -0
  24. package/dist/puzzle-creation/OpeningTagHoverPreview.svelte +81 -0
  25. package/dist/puzzle-creation/OpeningTagHoverPreview.svelte.d.ts +11 -0
  26. package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte +104 -32
  27. package/dist/puzzle-creation/PuzzleBoardTreeViewerPane.svelte.d.ts +16 -2
  28. package/dist/puzzle-creation/PuzzleCreationWizard.svelte +192 -202
  29. package/dist/puzzle-creation/PuzzleCreationWizard.svelte.d.ts +47 -3
  30. package/dist/puzzle-creation/PuzzlePgnBoardTreeEditor.svelte +485 -76
  31. package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte +36 -0
  32. package/dist/puzzle-creation/PuzzleWizardTagsStep.svelte.d.ts +12 -0
  33. package/dist/puzzle-creation/StepMoves.svelte +38 -18
  34. package/dist/puzzle-creation/StepMoves.svelte.d.ts +2 -1
  35. package/dist/puzzle-creation/StepPosition.svelte +15 -9
  36. package/dist/puzzle-creation/StepPreview.svelte +23 -10
  37. package/dist/puzzle-creation/StepPreview.svelte.d.ts +2 -0
  38. package/dist/puzzle-creation/StepTags.svelte +270 -0
  39. package/dist/puzzle-creation/StepTags.svelte.d.ts +19 -0
  40. package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.d.ts +9 -0
  41. package/dist/puzzle-creation/buildPuzzleWizardBoardSettings.js +19 -0
  42. package/dist/puzzle-creation/createPuzzleLineEditingBoard.d.ts +10 -3
  43. package/dist/puzzle-creation/createPuzzleLineEditingBoard.js +15 -11
  44. package/dist/puzzle-creation/puzzleWizardState.d.ts +35 -0
  45. package/dist/puzzle-creation/puzzleWizardState.js +37 -0
  46. package/dist/puzzle-creation/types.d.ts +35 -5
  47. package/package.json +20 -17
@@ -1,5 +1,5 @@
1
1
  import { PgnOps } from "@connectorvol/chessops/pgnOps.svelte";
2
- import { Color, PUZZLE_BRANCH_WRONG_NAG_ID } from "@connectorvol/shared";
2
+ import { Color, PUZZLE_BRANCH_WRONG_NAG_ID, calculatePly } from "@connectorvol/shared";
3
3
  /**
4
4
  * Представляет извлечение стороны, имеющей ход, из полной строки FEN.
5
5
  */
@@ -49,23 +49,83 @@ export function puzzlePreviewFindChildIndexForPlayedSan(cursorFen, children, pla
49
49
  /**
50
50
  * Представляет классификацию хода решателя на развилке (или на единственном продолжении).
51
51
  */
52
- export function puzzlePreviewClassifySolverMove(cursorNode, cursorFen, playedSan, solverColor) {
52
+ export function puzzlePreviewClassifySolverMove(cursorNode, cursorFen, playedSan, _solverColor) {
53
53
  const children = cursorNode.children;
54
54
  const idx = puzzlePreviewFindChildIndexForPlayedSan(cursorFen, children, playedSan);
55
55
  if (idx < 0) {
56
56
  return { kind: "no_matching_variant" };
57
57
  }
58
58
  const child = children[idx];
59
- const mover = puzzlePreviewSideToMoveFromFen(cursorFen);
60
- const isSolverFork = children.length > 1 && mover === solverColor;
61
- if (isSolverFork) {
62
- const nags = child.data.nags ?? [];
63
- if (nags.includes(PUZZLE_BRANCH_WRONG_NAG_ID)) {
64
- return { kind: "wrong_marked_variant", wrongBranchRoot: child };
65
- }
59
+ const nags = child.data.nags ?? [];
60
+ if (nags.includes(PUZZLE_BRANCH_WRONG_NAG_ID)) {
61
+ return { kind: "wrong_marked_variant", wrongBranchRoot: child };
66
62
  }
67
63
  return { kind: "correct", childIndex: idx };
68
64
  }
65
+ /**
66
+ * Представляет сбор данных узла для произвольного неверного хода решателя (нет в PGN).
67
+ */
68
+ export function puzzlePreviewBuildAdHocWrongMoveData(cursorFen, san) {
69
+ const probe = new PgnOps(cursorFen, "chess");
70
+ try {
71
+ const move = probe.makeSanMove(san);
72
+ const fen = probe.fen();
73
+ const { halfMoves, fullMoves } = calculatePly(fen);
74
+ return {
75
+ fen,
76
+ san,
77
+ ply: halfMoves,
78
+ fullMoves,
79
+ lastMove: move,
80
+ nags: [PUZZLE_BRANCH_WRONG_NAG_ID],
81
+ };
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ /**
88
+ * Представляет добавление отсутствующего в PGN хода как новой ветки дерева решения с NAG ✗.
89
+ */
90
+ export function puzzlePreviewAddAdHocWrongMoveToSolutionTree(solutionTree, cursorPath, cursorFen, san) {
91
+ const data = puzzlePreviewBuildAdHocWrongMoveData(cursorFen, san);
92
+ if (!data)
93
+ return null;
94
+ const cursorNode = puzzlePreviewNodeAtPath(solutionTree.rootNode.moves, cursorPath);
95
+ if (!cursorNode)
96
+ return null;
97
+ solutionTree.currentNode = cursorNode;
98
+ solutionTree.addNodeToCurrent({
99
+ id: "",
100
+ children: [],
101
+ data,
102
+ });
103
+ return solutionTree.currentNode;
104
+ }
105
+ /**
106
+ * Представляет пересчёт пути индексов после перестановки детей на развилках.
107
+ */
108
+ export function puzzlePreviewRemapPathAfterReorder(rootMoves, path) {
109
+ const nodeIds = [];
110
+ let node = rootMoves;
111
+ for (const idx of path) {
112
+ const child = node.children[idx];
113
+ if (!child)
114
+ return path;
115
+ nodeIds.push(child.id);
116
+ node = child;
117
+ }
118
+ node = rootMoves;
119
+ const remapped = [];
120
+ for (const id of nodeIds) {
121
+ const idx = node.children.findIndex((c) => c.id === id);
122
+ if (idx < 0)
123
+ return path;
124
+ remapped.push(idx);
125
+ node = node.children[idx];
126
+ }
127
+ return remapped;
128
+ }
69
129
  /**
70
130
  * Представляет проверку: линия закончилась ключевым ходом решателя (ход соперника, лист дерева).
71
131
  */
@@ -0,0 +1,14 @@
1
+ import type { ChessTree, ChessTreeNode } from "@connectorvol/tree";
2
+ import { Color } from "@connectorvol/shared";
3
+ /**
4
+ * Представляет нормализацию дерева задачи: NAG ✓/✗ и порядок веток (верный ход — главная линия).
5
+ */
6
+ export declare function normalizePuzzleTreeMainLine(root: ChessTreeNode, solverColor: Color): boolean;
7
+ /**
8
+ * Представляет синхронизацию NAG ✓/✗ по дереву решения (устаревшее имя, вызывает нормализацию).
9
+ */
10
+ export declare function syncPuzzleBranchNags(root: ChessTreeNode, solverColor: Color): boolean;
11
+ /**
12
+ * Представляет нормализацию главной линии во всём дереве задачи и инкремент версии мутации при изменениях.
13
+ */
14
+ export declare function syncPuzzleBranchNagsOnTree(tree: ChessTree, solverColor: Color): void;
@@ -0,0 +1,125 @@
1
+ import { Color, PUZZLE_BRANCH_CORRECT_NAG_ID, PUZZLE_BRANCH_WRONG_NAG_ID, isPuzzleBranchNag, } from "@connectorvol/shared";
2
+ /**
3
+ * Представляет извлечение стороны, имеющей ход, из полной строки FEN.
4
+ */
5
+ function sideToMoveFromFullFen(fullFen) {
6
+ const token = fullFen.trim().split(/\s+/)[1];
7
+ return token === "b" ? Color.BLACK : Color.WHITE;
8
+ }
9
+ /**
10
+ * Представляет удаление NAG «верно / неверно» из списка аннотаций узла.
11
+ */
12
+ function stripPuzzleBranchNags(nags) {
13
+ return (nags ?? []).filter((n) => !isPuzzleBranchNag(n));
14
+ }
15
+ /**
16
+ * Представляет проверку: на узле стоит NAG верного хода задачи.
17
+ */
18
+ function childHasCorrectNag(child) {
19
+ return (child.data.nags ?? []).includes(PUZZLE_BRANCH_CORRECT_NAG_ID);
20
+ }
21
+ /**
22
+ * Представляет проверку: на узле стоит NAG неверного хода задачи.
23
+ */
24
+ function childHasWrongNag(child) {
25
+ return (child.data.nags ?? []).includes(PUZZLE_BRANCH_WRONG_NAG_ID);
26
+ }
27
+ /**
28
+ * Представляет выбор ребёнка для канонической главной линии на развилке.
29
+ */
30
+ function pickMainLineChild(parent, solverColor) {
31
+ const { children } = parent;
32
+ if (children.length === 0)
33
+ return null;
34
+ if (children.length === 1)
35
+ return children[0];
36
+ const mover = sideToMoveFromFullFen(parent.data.fen);
37
+ if (mover === solverColor) {
38
+ const correct = children.find(childHasCorrectNag);
39
+ if (correct)
40
+ return correct;
41
+ const notWrong = children.find((c) => !childHasWrongNag(c));
42
+ if (notWrong)
43
+ return notWrong;
44
+ }
45
+ else {
46
+ const notWrong = children.find((c) => !childHasWrongNag(c));
47
+ if (notWrong)
48
+ return notWrong;
49
+ }
50
+ return children[0];
51
+ }
52
+ /**
53
+ * Представляет удаление NAG ✓/✗ у ходов, которые не являются ходом решателя.
54
+ */
55
+ function cleanupInvalidPuzzleBranchNags(root, solverColor) {
56
+ let changed = false;
57
+ function walk(parent) {
58
+ const mover = sideToMoveFromFullFen(parent.data.fen);
59
+ for (const child of parent.children) {
60
+ const isSolverFork = mover === solverColor;
61
+ if (!isSolverFork) {
62
+ const before = child.data.nags ?? [];
63
+ if (before.some(isPuzzleBranchNag)) {
64
+ const next = stripPuzzleBranchNags(before);
65
+ child.data.nags = next.length > 0 ? next : undefined;
66
+ changed = true;
67
+ }
68
+ }
69
+ walk(child);
70
+ }
71
+ }
72
+ walk(root);
73
+ return changed;
74
+ }
75
+ /**
76
+ * Представляет поднятие канонической главной ветки в `children[0]` на всех развилках.
77
+ */
78
+ function reorderMainLineChildrenFirst(root, solverColor) {
79
+ let changed = false;
80
+ function walk(node) {
81
+ for (const child of node.children) {
82
+ walk(child);
83
+ }
84
+ if (node.children.length <= 1)
85
+ return;
86
+ const mainChild = pickMainLineChild(node, solverColor);
87
+ if (!mainChild)
88
+ return;
89
+ const idx = node.children.findIndex((c) => c.id === mainChild.id);
90
+ if (idx <= 0)
91
+ return;
92
+ const [moved] = node.children.splice(idx, 1);
93
+ node.children.unshift(moved);
94
+ changed = true;
95
+ }
96
+ walk(root);
97
+ return changed;
98
+ }
99
+ /**
100
+ * Представляет нормализацию дерева задачи: NAG ✓/✗ и порядок веток (верный ход — главная линия).
101
+ */
102
+ export function normalizePuzzleTreeMainLine(root, solverColor) {
103
+ let changed = false;
104
+ if (cleanupInvalidPuzzleBranchNags(root, solverColor)) {
105
+ changed = true;
106
+ }
107
+ if (reorderMainLineChildrenFirst(root, solverColor)) {
108
+ changed = true;
109
+ }
110
+ return changed;
111
+ }
112
+ /**
113
+ * Представляет синхронизацию NAG ✓/✗ по дереву решения (устаревшее имя, вызывает нормализацию).
114
+ */
115
+ export function syncPuzzleBranchNags(root, solverColor) {
116
+ return normalizePuzzleTreeMainLine(root, solverColor);
117
+ }
118
+ /**
119
+ * Представляет нормализацию главной линии во всём дереве задачи и инкремент версии мутации при изменениях.
120
+ */
121
+ export function syncPuzzleBranchNagsOnTree(tree, solverColor) {
122
+ if (normalizePuzzleTreeMainLine(tree.rootNode.moves, solverColor)) {
123
+ tree.mutationVersion++;
124
+ }
125
+ }
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import {
3
+ Chessboard,
4
+ CHESSBOARD_THEMES,
5
+ createBoardApi,
6
+ Draggable,
7
+ } from "@connectorvol/chessboard";
8
+
9
+ import { DEFAULT_BOARD_APPEARANCE_SETTINGS } from "../constants/default-board-appearance-settings.js";
10
+
11
+ interface Props {
12
+ /** Возвращает FEN позиции для превью. */
13
+ fen: string;
14
+ /** Возвращает подпись дебютного тега. */
15
+ label: string;
16
+ /** Возвращает якорь кнопки тега для позиционирования popover. */
17
+ anchor: DOMRect;
18
+ }
19
+
20
+ let { fen, label, anchor }: Props = $props();
21
+
22
+ const previewBoardSizePx = 220;
23
+ const previewGapPx = 8;
24
+ const viewportMarginPx = 12;
25
+
26
+ const previewDesign = {
27
+ ...DEFAULT_BOARD_APPEARANCE_SETTINGS.design,
28
+ showBoardCoordinates: false,
29
+ showSquareCoordinates: false,
30
+ markerConfig: null,
31
+ showPossibleMoveIndicators: false,
32
+ theme: CHESSBOARD_THEMES.blue,
33
+ };
34
+
35
+ const previewBoardApi = $derived.by(() =>
36
+ createBoardApi({
37
+ fen,
38
+ playSettings: {
39
+ ...DEFAULT_BOARD_APPEARANCE_SETTINGS.play,
40
+ boardSize: previewBoardSizePx * 10,
41
+ isResizable: false,
42
+ draggable: Draggable.NONE,
43
+ allowPreMove: false,
44
+ showSquareBadges: false,
45
+ animationTime: 0,
46
+ },
47
+ }),
48
+ );
49
+
50
+ const previewStyle = $derived.by(() => {
51
+ const belowTop = anchor.bottom + previewGapPx;
52
+ const aboveTop = anchor.top - previewBoardSizePx - previewGapPx;
53
+ const fitsBelow =
54
+ belowTop + previewBoardSizePx <= window.innerHeight - viewportMarginPx;
55
+ const top = fitsBelow
56
+ ? belowTop
57
+ : Math.max(viewportMarginPx, aboveTop);
58
+
59
+ const centeredLeft =
60
+ anchor.left + anchor.width / 2 - previewBoardSizePx / 2;
61
+ const maxLeft = Math.max(
62
+ viewportMarginPx,
63
+ window.innerWidth - previewBoardSizePx - viewportMarginPx,
64
+ );
65
+ const left = Math.min(
66
+ Math.max(centeredLeft, viewportMarginPx),
67
+ maxLeft,
68
+ );
69
+
70
+ return `left:${left}px;top:${top}px;width:${previewBoardSizePx}px;`;
71
+ });
72
+ </script>
73
+
74
+ <div
75
+ class="border-border pointer-events-none fixed z-50 overflow-hidden rounded-md border bg-white shadow-lg [&_*]:pointer-events-none"
76
+ style={previewStyle}
77
+ role="tooltip"
78
+ aria-label="Превью дебюта: {label}"
79
+ >
80
+ <Chessboard facade={previewBoardApi} design={previewDesign} />
81
+ </div>
@@ -0,0 +1,11 @@
1
+ interface Props {
2
+ /** Возвращает FEN позиции для превью. */
3
+ fen: string;
4
+ /** Возвращает подпись дебютного тега. */
5
+ label: string;
6
+ /** Возвращает якорь кнопки тега для позиционирования popover. */
7
+ anchor: DOMRect;
8
+ }
9
+ declare const OpeningTagHoverPreview: import("svelte").Component<Props, {}, "">;
10
+ type OpeningTagHoverPreview = ReturnType<typeof OpeningTagHoverPreview>;
11
+ export default OpeningTagHoverPreview;
@@ -1,10 +1,14 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
+ import { MediaQuery } from "svelte/reactivity";
3
4
 
4
- import type { BoardApi } from "@connectorvol/chessboard";
5
+ import type {
6
+ BoardApi,
7
+ TChessBoardDesignSettings,
8
+ } from "@connectorvol/chessboard";
5
9
  import { Chessboard } from "@connectorvol/chessboard";
6
- import type { ChessTree } from "@connectorvol/tree";
7
- import { TreeViewer } from "@connectorvol/tree";
10
+ import type { ChessTree, TTreeViewerTheme } from "@connectorvol/tree";
11
+ import { TREE_VIEWER_LIGHT_THEME, TreeViewer } from "@connectorvol/tree";
8
12
 
9
13
  import { cn } from "../utils.js";
10
14
 
@@ -16,6 +20,10 @@
16
20
  * Возвращает API шахматной доски для компонента `Chessboard`.
17
21
  */
18
22
  chessboard: BoardApi;
23
+ /**
24
+ * Возвращает снимок дизайна, передаваемый в `<Chessboard design={...} />`.
25
+ */
26
+ chessboardDesign: TChessBoardDesignSettings;
19
27
  /**
20
28
  * Возвращает экземпляр дерева ходов для `TreeViewer`.
21
29
  */
@@ -44,6 +52,16 @@
44
52
  * Возвращает признак возможности выбора узлов в дереве.
45
53
  */
46
54
  selectable?: boolean;
55
+ /**
56
+ * Возвращает true, если в режиме правки показывать группу NAG «Задача (развилка)»
57
+ * рядом с кнопками undo/redo на ходах из развилки.
58
+ */
59
+ showPuzzleBranchNags?: boolean;
60
+ /**
61
+ * Возвращает тему `TreeViewer` (фон, границы, текст). Если не задана —
62
+ * используется `TREE_VIEWER_LIGHT_THEME`.
63
+ */
64
+ theme?: TTreeViewerTheme;
47
65
  /**
48
66
  * Возвращает дополнительные классы Tailwind для корневого контейнера.
49
67
  */
@@ -56,6 +74,7 @@
56
74
 
57
75
  const {
58
76
  chessboard,
77
+ chessboardDesign,
59
78
  chessTree,
60
79
  onSelectNode,
61
80
  onDeleteVariant,
@@ -63,50 +82,103 @@
63
82
  setChessboardFen,
64
83
  editMode = true,
65
84
  selectable = true,
85
+ showPuzzleBranchNags = false,
86
+ theme = TREE_VIEWER_LIGHT_THEME,
66
87
  class: className,
67
88
  belowChessboard,
68
89
  }: Props = $props();
69
90
 
70
- const hasBoardCoordinatesWithoutBorder = $derived(
71
- chessboard.showBoardCoordinates && !chessboard.border,
91
+ /** ponytail: совпадает с Tailwind `lg` (1024px), как в разметке панели. */
92
+ const isMobile = new MediaQuery("(max-width: 1023px)");
93
+
94
+ const boardSize = $derived(chessboard.getBoard().settingsService.boardSize);
95
+ const useAutoBoardLayout = $derived(isMobile.current || boardSize === "auto");
96
+
97
+ const boardColumnStyle = $derived(
98
+ !useAutoBoardLayout && typeof boardSize === "number"
99
+ ? `flex: 0 0 min(100%, ${boardSize}rem);`
100
+ : undefined,
101
+ );
102
+
103
+ let savedDesktopBoardSettings = $state<{
104
+ boardSize: number | "auto";
105
+ isResizable: boolean;
106
+ } | null>(null);
107
+
108
+ $effect(() => {
109
+ const mobile = isMobile.current;
110
+ const settings = chessboard.getBoard().settingsService;
111
+
112
+ if (mobile) {
113
+ if (savedDesktopBoardSettings === null) {
114
+ savedDesktopBoardSettings = {
115
+ boardSize: settings.boardSize,
116
+ isResizable: chessboard.isResizable,
117
+ };
118
+ }
119
+ chessboard.isResizable = false;
120
+ settings.boardSize = "auto";
121
+ } else if (savedDesktopBoardSettings !== null) {
122
+ chessboard.isResizable = savedDesktopBoardSettings.isResizable;
123
+ settings.boardSize = savedDesktopBoardSettings.boardSize;
124
+ savedDesktopBoardSettings = null;
125
+ }
126
+ });
127
+
128
+ let boardHeight = $state(0);
129
+
130
+ const treeHeightLocked = $derived(boardHeight > 0);
131
+
132
+ const treeColumnStyle = $derived(
133
+ treeHeightLocked ? `--pane-board-height: ${boardHeight}px` : undefined,
72
134
  );
73
135
  </script>
74
136
 
75
- <div
76
- class={cn(
77
- "relative flex min-h-0 flex-col justify-start lg:flex-row lg:items-stretch lg:justify-start lg:gap-4",
78
- className,
79
- )}
80
- >
137
+ <div class={cn("flex w-full flex-col gap-2", className)}>
81
138
  <div
82
- class="order-2 flex min-h-48 w-full flex-1 flex-col gap-2 lg:order-2 lg:min-h-0 lg:min-w-0"
139
+ class="relative flex min-h-0 flex-col gap-4 lg:flex-row lg:items-stretch"
83
140
  >
84
141
  <div
85
142
  class={cn(
86
- "flex min-h-28 min-w-0 flex-1 flex-col overflow-hidden rounded-md border border-border lg:min-h-0",
87
- hasBoardCoordinatesWithoutBorder && "mb-4",
143
+ "order-1 flex w-full shrink-0 flex-col lg:order-1 lg:min-h-0",
144
+ useAutoBoardLayout && "lg:max-w-[min(100%,38rem)]",
88
145
  )}
146
+ style={boardColumnStyle}
89
147
  >
90
- <TreeViewer
91
- chessTree={chessTree}
92
- {onSelectNode}
93
- {onDeleteVariant}
94
- {setChessFen}
95
- {setChessboardFen}
96
- pieceSet={chessboard.chessSet}
97
- className="min-h-0 flex-1 border-x-0 border-t-0"
98
- {editMode}
99
- {selectable}
100
- />
148
+ <div
149
+ class="aspect-square w-full shrink-0"
150
+ bind:clientHeight={boardHeight}
151
+ >
152
+ <Chessboard facade={chessboard} design={chessboardDesign} />
153
+ </div>
101
154
  </div>
102
- </div>
103
155
 
104
- <div
105
- class="order-1 flex w-full max-w-[38rem] shrink-0 flex-col gap-2 lg:order-1 lg:min-h-0"
106
- >
107
- <div class="w-full shrink-0">
108
- <Chessboard facade={chessboard} />
156
+ <div
157
+ class={cn(
158
+ "order-2 flex min-h-48 min-w-0 flex-1 flex-col lg:order-2 lg:min-w-[20rem] lg:shrink-0",
159
+ treeHeightLocked &&
160
+ "lg:h-[var(--pane-board-height)] lg:max-h-[var(--pane-board-height)] lg:min-h-0 lg:overflow-hidden",
161
+ )}
162
+ style={treeColumnStyle}
163
+ >
164
+ <div
165
+ class="flex min-h-0 flex-1 flex-col overflow-hidden border border-border"
166
+ >
167
+ <TreeViewer
168
+ {chessTree}
169
+ {onSelectNode}
170
+ {onDeleteVariant}
171
+ {setChessFen}
172
+ {setChessboardFen}
173
+ className="min-h-0 flex-1 rounded-none border-0"
174
+ {editMode}
175
+ {selectable}
176
+ {showPuzzleBranchNags}
177
+ {theme}
178
+ />
179
+ </div>
109
180
  </div>
110
- {@render belowChessboard?.()}
111
181
  </div>
182
+
183
+ {@render belowChessboard?.()}
112
184
  </div>
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from "svelte";
2
- import type { BoardApi } from "@connectorvol/chessboard";
3
- import type { ChessTree } from "@connectorvol/tree";
2
+ import type { BoardApi, TChessBoardDesignSettings } from "@connectorvol/chessboard";
3
+ import type { ChessTree, TTreeViewerTheme } from "@connectorvol/tree";
4
4
  /**
5
5
  * Представляет свойства панели разметки «доска и просмотр дерева партии».
6
6
  */
@@ -9,6 +9,10 @@ interface Props {
9
9
  * Возвращает API шахматной доски для компонента `Chessboard`.
10
10
  */
11
11
  chessboard: BoardApi;
12
+ /**
13
+ * Возвращает снимок дизайна, передаваемый в `<Chessboard design={...} />`.
14
+ */
15
+ chessboardDesign: TChessBoardDesignSettings;
12
16
  /**
13
17
  * Возвращает экземпляр дерева ходов для `TreeViewer`.
14
18
  */
@@ -37,6 +41,16 @@ interface Props {
37
41
  * Возвращает признак возможности выбора узлов в дереве.
38
42
  */
39
43
  selectable?: boolean;
44
+ /**
45
+ * Возвращает true, если в режиме правки показывать группу NAG «Задача (развилка)»
46
+ * рядом с кнопками undo/redo на ходах из развилки.
47
+ */
48
+ showPuzzleBranchNags?: boolean;
49
+ /**
50
+ * Возвращает тему `TreeViewer` (фон, границы, текст). Если не задана —
51
+ * используется `TREE_VIEWER_LIGHT_THEME`.
52
+ */
53
+ theme?: TTreeViewerTheme;
40
54
  /**
41
55
  * Возвращает дополнительные классы Tailwind для корневого контейнера.
42
56
  */