@connectorvol/tree 2.0.0 → 2.1.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/(classes)/chessTree.svelte.d.ts +5 -0
- package/dist/(classes)/chessTree.svelte.js +7 -0
- package/dist/(components)/Move.svelte +5 -1
- package/dist/(components)/Move.svelte.d.ts +2 -0
- package/dist/(components)/TreeViewer.svelte +107 -17
- package/dist/(constants)/png.d.ts +4 -4
- package/dist/(constants)/png.js +4 -4
- package/dist/(utils)/scrollToActiveMove.js +13 -0
- package/dist/(utils)/scrollToStart.js +6 -0
- package/dist/(utils)/treeViewerVirtualScroll.d.ts +14 -0
- package/dist/(utils)/treeViewerVirtualScroll.js +9 -0
- package/dist/(utils)/treeVirtualRows.d.ts +14 -0
- package/dist/(utils)/treeVirtualRows.js +43 -0
- package/package.json +4 -4
|
@@ -31,6 +31,11 @@ export declare class ChessTree {
|
|
|
31
31
|
* Возвращает родительский узел или null, если текущий узел — корень.
|
|
32
32
|
*/
|
|
33
33
|
getCurrentNodeParent(): ChessTreeNode | null;
|
|
34
|
+
/**
|
|
35
|
+
* Представляет поиск родительского узла для произвольного узла дерева.
|
|
36
|
+
* Возвращает родителя или null, если родитель не найден в индексе.
|
|
37
|
+
*/
|
|
38
|
+
getParentNode(node: ChessTreeNode): ChessTreeNode | null;
|
|
34
39
|
/**
|
|
35
40
|
* Представляет функцию удаления варианта из дерева.
|
|
36
41
|
* Удаляет указанный узел и все его дочерние узлы.
|
|
@@ -110,6 +110,13 @@ export class ChessTree {
|
|
|
110
110
|
getCurrentNodeParent() {
|
|
111
111
|
return this.findParentNode(this.currentNode);
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Представляет поиск родительского узла для произвольного узла дерева.
|
|
115
|
+
* Возвращает родителя или null, если родитель не найден в индексе.
|
|
116
|
+
*/
|
|
117
|
+
getParentNode(node) {
|
|
118
|
+
return this.findParentNode(node);
|
|
119
|
+
}
|
|
113
120
|
/**
|
|
114
121
|
* Представляет функцию удаления варианта из дерева.
|
|
115
122
|
* Удаляет указанный узел и все его дочерние узлы.
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
needSpace: boolean;
|
|
25
25
|
needRenderParentChildrens?: boolean;
|
|
26
26
|
pieceSet: PieceSet;
|
|
27
|
+
/** Представляет режим виртуализации: не рекурсировать в первого ребёнка (следующая строка списка). */
|
|
28
|
+
virtualStopMainRecursion?: boolean;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
const {
|
|
@@ -33,6 +35,7 @@
|
|
|
33
35
|
needSpace,
|
|
34
36
|
needRenderParentChildrens = true,
|
|
35
37
|
pieceSet,
|
|
38
|
+
virtualStopMainRecursion = false,
|
|
36
39
|
}: Props = $props();
|
|
37
40
|
|
|
38
41
|
const { chessTree, onSelectNode, onDeleteVariant, selectable } = getGameContext();
|
|
@@ -64,6 +67,7 @@
|
|
|
64
67
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
65
68
|
<ContextMenu.Root>
|
|
66
69
|
<ContextMenu.Trigger
|
|
70
|
+
data-node-id={chessNode.id}
|
|
67
71
|
class={{
|
|
68
72
|
"cursor-pointer ": selectable,
|
|
69
73
|
"cursor-default": !selectable,
|
|
@@ -273,7 +277,7 @@
|
|
|
273
277
|
{@render emptyMove()}
|
|
274
278
|
{/if}
|
|
275
279
|
{#if childMoves?.[0]}
|
|
276
|
-
{#if !isForcedNodeId}
|
|
280
|
+
{#if !isForcedNodeId && !virtualStopMainRecursion}
|
|
277
281
|
<Move
|
|
278
282
|
{pieceSet}
|
|
279
283
|
chessNode={chessNode.children[0]}
|
|
@@ -8,6 +8,8 @@ interface Props {
|
|
|
8
8
|
needSpace: boolean;
|
|
9
9
|
needRenderParentChildrens?: boolean;
|
|
10
10
|
pieceSet: PieceSet;
|
|
11
|
+
/** Представляет режим виртуализации: не рекурсировать в первого ребёнка (следующая строка списка). */
|
|
12
|
+
virtualStopMainRecursion?: boolean;
|
|
11
13
|
}
|
|
12
14
|
declare const Move: import("svelte").Component<Props, {}, "">;
|
|
13
15
|
type Move = ReturnType<typeof Move>;
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ClassValue } from "svelte/elements";
|
|
3
|
+
import { onMount } from "svelte";
|
|
4
|
+
import { get } from "svelte/store";
|
|
5
|
+
import { createVirtualizer } from "@tanstack/svelte-virtual";
|
|
6
|
+
|
|
3
7
|
import { ChessTree } from "../(classes)/chessTree.svelte.js";
|
|
4
8
|
|
|
5
9
|
import Move from "./Move.svelte";
|
|
6
10
|
import { initGameContext } from "../(utils)/context.js";
|
|
11
|
+
import {
|
|
12
|
+
buildMainLineNodes,
|
|
13
|
+
getVirtualRowIndexForNode,
|
|
14
|
+
needSpaceForMainLineNode,
|
|
15
|
+
} from "../(utils)/treeVirtualRows.js";
|
|
16
|
+
import { setTreeViewerVirtualScrollApi } from "../(utils)/treeViewerVirtualScroll.js";
|
|
7
17
|
import type { PieceSet } from "@connectorvol/shared";
|
|
8
18
|
|
|
9
19
|
type Props = {
|
|
@@ -35,8 +45,66 @@
|
|
|
35
45
|
onDeleteVariant,
|
|
36
46
|
setChessFen,
|
|
37
47
|
setChessboardFen,
|
|
38
|
-
selectable
|
|
48
|
+
selectable,
|
|
39
49
|
);
|
|
50
|
+
|
|
51
|
+
let scrollEl = $state<HTMLDivElement | null>(null);
|
|
52
|
+
|
|
53
|
+
const mainLineNodes = $derived(buildMainLineNodes(chessTree.rootNode.moves));
|
|
54
|
+
|
|
55
|
+
const virtualizerStore = createVirtualizer({
|
|
56
|
+
count: 0,
|
|
57
|
+
getScrollElement: () => null,
|
|
58
|
+
estimateSize: () => 96,
|
|
59
|
+
overscan: 8,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
$effect(() => {
|
|
63
|
+
const v = get(virtualizerStore);
|
|
64
|
+
v.setOptions({
|
|
65
|
+
count: mainLineNodes.length,
|
|
66
|
+
getScrollElement: scrollEl ? () => scrollEl : () => null,
|
|
67
|
+
estimateSize: () => 96,
|
|
68
|
+
overscan: 8,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
function measureRow(node: HTMLDivElement) {
|
|
73
|
+
queueMicrotask(() => {
|
|
74
|
+
get(virtualizerStore).measureElement(node);
|
|
75
|
+
});
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
onMount(() => {
|
|
80
|
+
setTreeViewerVirtualScrollApi({
|
|
81
|
+
scrollToCurrentNode: (behavior: ScrollBehavior = "instant") => {
|
|
82
|
+
const v = get(virtualizerStore);
|
|
83
|
+
const line = buildMainLineNodes(chessTree.rootNode.moves);
|
|
84
|
+
if (line.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const idx = getVirtualRowIndexForNode(
|
|
88
|
+
chessTree.currentNode,
|
|
89
|
+
line,
|
|
90
|
+
(n) => chessTree.getParentNode(n),
|
|
91
|
+
);
|
|
92
|
+
v.scrollToIndex(Math.min(idx, line.length - 1), {
|
|
93
|
+
align: "center",
|
|
94
|
+
behavior,
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
scrollToStart: (behavior: ScrollBehavior = "instant") => {
|
|
98
|
+
const v = get(virtualizerStore);
|
|
99
|
+
const line = buildMainLineNodes(chessTree.rootNode.moves);
|
|
100
|
+
if (line.length === 0) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
v.scrollToIndex(0, { align: "start", behavior });
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
return () => setTreeViewerVirtualScrollApi(null);
|
|
107
|
+
});
|
|
40
108
|
</script>
|
|
41
109
|
|
|
42
110
|
{#snippet comment(text: string)}
|
|
@@ -52,31 +120,53 @@
|
|
|
52
120
|
{/snippet}
|
|
53
121
|
|
|
54
122
|
<div
|
|
123
|
+
bind:this={scrollEl}
|
|
55
124
|
id="tree-viewer-container"
|
|
56
125
|
class={[
|
|
57
|
-
"flex-1 relative
|
|
126
|
+
"flex-1 relative overflow-auto border border-gray-300 rounded-l-sm shadow-sm",
|
|
58
127
|
className,
|
|
59
128
|
]}
|
|
60
129
|
role="region"
|
|
61
130
|
aria-label="Chess move tree"
|
|
62
131
|
oncontextmenu={(e) => e.preventDefault()}
|
|
63
132
|
>
|
|
64
|
-
<div
|
|
65
|
-
class={{
|
|
66
|
-
"grid flex-1 grid-cols-[60px_1fr_1fr] divide-x divide-y divide-gray-300 select-none ": true,
|
|
67
|
-
}}
|
|
68
|
-
>
|
|
133
|
+
<div class="min-w-0">
|
|
69
134
|
{#if chessTree.rootNode.comments}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
135
|
+
<div
|
|
136
|
+
class="grid grid-cols-[60px_1fr_1fr] divide-x divide-y divide-gray-300 select-none"
|
|
137
|
+
>
|
|
138
|
+
{#each chessTree.rootNode.comments as firstComment}
|
|
139
|
+
{@render comment(firstComment)}
|
|
140
|
+
{/each}
|
|
141
|
+
</div>
|
|
73
142
|
{/if}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
143
|
+
|
|
144
|
+
<div class="relative w-full" style="min-height: 1px;">
|
|
145
|
+
<div
|
|
146
|
+
class="relative w-full"
|
|
147
|
+
style="height: {$virtualizerStore.getTotalSize()}px;"
|
|
148
|
+
>
|
|
149
|
+
{#each $virtualizerStore.getVirtualItems() as row (row.key)}
|
|
150
|
+
{@const rowNode = mainLineNodes[row.index]}
|
|
151
|
+
<div
|
|
152
|
+
class="absolute left-0 top-0 w-full grid grid-cols-[60px_1fr_1fr] divide-x divide-y divide-gray-300 select-none"
|
|
153
|
+
style="transform: translateY({row.start}px);"
|
|
154
|
+
data-index={row.index}
|
|
155
|
+
use:measureRow
|
|
156
|
+
>
|
|
157
|
+
<Move
|
|
158
|
+
chessNode={rowNode}
|
|
159
|
+
depth={1}
|
|
160
|
+
needSpace={needSpaceForMainLineNode(rowNode, (n) =>
|
|
161
|
+
chessTree.getParentNode(n),
|
|
162
|
+
)}
|
|
163
|
+
parentNode={chessTree.getParentNode(rowNode)}
|
|
164
|
+
// virtualStopMainRecursion={true}
|
|
165
|
+
{pieceSet}
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
{/each}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
81
171
|
</div>
|
|
82
172
|
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* https://lichess.org/study/dnp96Pfx/N9EOybPh
|
|
3
3
|
*/
|
|
4
|
-
export declare const PGN = "\n[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"Standard\"]\n[Termination \"Normal\"]\n[Annotator \"https://lichess.org/@/NaSil\"]\n[GameId \"oj3PlZ6Q\"]\n[ECO \"B33\"]\n[Opening \"Sicilian Defense: Lasker-Pelikan Variation\"]\n[StudyName \"Game Of The Month - April 2025\"]\n[ChapterName \"Ceburashka - littleplotkin\"]\n[ChapterURL \"https://lichess.org/study/dnp96Pfx/N9EOybPh\"]\n[Orientation \"white\"]\n\n{ A high level clash where IM littleplotkin playing Black berserked and played with half their time, thus seeking complications starting from the early opening stage of the game. The 2025 April Game Of The Month witnesses a very instructive attacking game played in the Sveshnikov Sicilian with many deep points!\n\nCongratulations to Ceburashka on combining nice strategic play with a cute attacking finish in one of the typical structures of the Sicilian Defense.\n\nEnjoy! }\n1. e4 { [%clk 0:05:00] } 1... c5 { [%clk 0:02:30] } 2. Nf3 { [%clk 0:04:59] } 2... Nc6 { [%clk 0:02:29] } 3. d4 { [%clk 0:04:59] } 3... cxd4 { [%clk 0:02:29] } 4. Nxd4 { [%clk 0:04:58] } 4... Nf6 { [%clk 0:02:27] } 5. Nc3 { [%clk 0:04:53] } 5... e5 { [%clk 0:02:27] } (5... e6 6. Ndb5 Bc5 { Please compare this line with the game continuation. } 7. Nd6+ Ke7 8. Bf4 e5 9. Nf5+ Kf8 10. Bg5 { with a transposition to our game. }) 6. Ndb5! { This move is almost the only respectable move in this position and yet one of the most instructive maneuvers in the Sicilian Defense, at least for me!\nIf you see this move for the first time, you might wonder why White wants their opponent to play 6...d6 while their knight will eventually get kicked away and end up on the edge of the board, namely on the a3 square. In order to understand what is going on we should check the alternatives. } { [%cal Gb5d6] [%clk 0:04:52] } (6. Nb3 Bb4! $10 { [%cal Gd6d5,Gd7d5,Rd7d6] }) (6. Nf3 Bb4! $10 { After Black develops their so called \"bad bishop\", they can eventually play ...d6 or even the desired ...d5 if possible. The reason behind 6. Ndb5 is to keep Bf8 behind the pawn chain after 6...d6. }) (6. Nde2!? { A rare approach which gained some popularity recently. White takes the sting out of 6...Bb4 with this move, but } 6... Bc5! { Black is ready to play ...d6 next with an active \"bad bishop\". } { [%cal Gd8b6] } (6... Bb4 7. a3! { the point of 5.Nde2 } { [%cal Ge2c3,Rb4c3] }) 7. Ng3 { This rather fresh approach was seen in GM practice, but Black doesn't have a lot to worry about so far. } { [%csl Rd3,Rc4][%cal Gf1d3,Rd3c4] }) (6. Nf5!? { White prevents Black's dark-squared bishop from developing and invites a forcing sequence. } 6... d5! { [%cal Rc8f5] } 7. exd5 Bxf5 8. dxc6 bxc6! $13 { with a messy theoretical battle where Black's position is OK according to theory. Piece activity fully compensates the pawn weaknesses in Black's camp. }) 6... Bc5!? { An obscure sideline in the Sveshnikov Sicilian, also known as Lasker-Pelikan Variation of the Sicilian Defense, which will eventually transpose into the ...e6 Sicilian noted at the 5th move above. It might be a practical choice in blitz to get out of opening theory as early as possible. } { [%clk 0:02:26] } (6... d6 { is by far the main line. } { [%csl Rf8] } 7. Bg5 (7. Nd5 { a modern approach which became popular after Fabiano Caruana used it in the World Chess Championship match against Magnus Carlsen in 2018. }) 7... a6 8. Na3 { [%csl Gd5][%cal Ga3c4,Rc4e3] } 8... b5 { It seems that White has ended up with their knight on the a3-square, losing 2 tempos playing with it. However, Black only made pawn moves in the meantime, and White might eventually target the a6-b5 pawn chain, creating weaknesses in Black's camp, by playing a4 or c4. } { [%csl Ra3,Gc4][%cal Gb5b4] }) 7. Nd6+ { Even if this move looks like the most obvious follow-up, Stockfish considers } { [%clk 0:04:50] } (7. Be3! { as the best move, since our beloved silicon monster can clearly foresee and evaluate the following sequence in White's favor. } 7... d6! (7... Bxe3 8. Nd6+ { This intermediate move is the point of the previous move. } 8... Ke7 9. fxe3 $18 { Stockfish considers this position as winning because Black's king remains in the center and the White forces will join the action in a couple of moves. } { [%csl Re7,Rf7][%cal Gf1c4,Gd1d2,Ge1c1] }) 8. Bxc5 dxc5 9. Qxd8+ Kxd8 10. O-O-O+ { White has a clear initiative in this queenless middlegame position. For example: } 10... Ke7 11. Nd5+! $146 { Yes, a novelty! An important shot creating a very strong passer as an attacking unit against Black's king. It requires a precise follow-up as White's d6-pawn might be weak if White's initiative fizzles out. } 11... Nxd5 12. exd5 Nd4 13. d6+ Kd8 { Otherwise White will play Nc7, c3, and d7, winning. } (13... Kf6 14. Nc7 { Showcasing the strength of the passed d-pawn with the following sequence. } 14... Rb8 15. c3 Ne6 16. d7 Nxc7 17. d8=B+ { I like how Stockfish considers promoting the pawn into a bishop is equal to promoting it into a queen. } 17... Rxd8 18. Rxd8 $18) 14. Re1 $36 { A rather forced and interesting line. I hope I could try this line in practice if I have the chance to play against this line, and I would like to encourage the readers to do so as well! } { [%cal Gf2f4,Bb5d4,Be1e7] }) 7... Ke7! { A bold move but the only one, as after } { [%clk 0:02:25] } (7... Bxd6?! 8. Qxd6 $16 { White is much better with the blockade over the d6 and d5 squares and the bishop pair. Stockfish already considers this position as winning for White. }) 8. Nf5+ { [%clk 0:04:49] } (8. Nxc8+ { Logical to capture the valuable bishop at the cost of spending a few tempos with the knight in doing so. } 8... Rxc8 9. Bc4 Kf8 10. O-O h6 { [%csl Gg5][%cal Yc1g5] } 11. Kh1 { [%cal Gf2f4,Rc5g1] } 11... Ne7 12. f4 $40 { White has the bishop pair and Black's king is in a somewhat uncomfortable situation. } { [%csl Rf8][%cal Gf1f7,Gc4f7] }) 8... Kf8 { [%clk 0:02:24] } 9. Bg5 { It's a very well-known idea to exchange the pieces controlling the d5-square. We transposed back into the line mentioned at the 5th move. At the expense of developing their dark-squared bishop outside of the pawn chain, Black lost their castling rights. } { [%csl Gd5] [%clk 0:04:43] } 9... h6 { [%clk 0:02:17] } (9... d6 { This is sort of the \"main\" line according to the Masters Database. One recent example from GM practice: } { [%csl Yf5,Be6][%cal Gc8e6] } 10. Ne3 { [%csl Bd5][%cal Be3d5,Bc3d5] } 10... h6 11. Bxf6 { White gave up their \"good bishop\" in order to increase control over the d5-square. } 11... Qxf6 12. Bc4 { [%csl Gd5,Gc4,Gc3,Ge3,Gd1][%cal Ye3d5,Yc3d5,Bc4d5,Bd1d5] } 12... Be6 (12... Bxe3? 13. fxe3 $40 { [%cal Gf1f7,Yh1f1,Bd1d6] }) 13. O-O Kg8 { [%cal Bg8h7] } 14. Ncd5 Qd8 (14... Bxd5 15. Nxd5 Qd8 16. c3 Kh7 $16 { White has the better structure, better minor pieces, and a safer king in this middlegame with opposite-colored bishops. }) 15. c3 $14 { Huschenbeth, N. - Rakotomaharo, Fy Antenaina, 1-0, FIDE World Cup 2023, https://lichess.org/b4xGV3WX } { [%csl Yd4] }) 10. Bxf6 { [%clk 0:04:37] } 10... Qxf6 { [%clk 0:02:15] } 11. Nd5 { [%clk 0:04:27] } 11... Qd8 { [%clk 0:02:10] } 12. c3 { White will try to put pressure on Black's d-pawn and control the d5-square. Meanwhile, Black will try to play around those squares on the queenside with the minority attack (a5, b5, b4) or go for kingside play by means of ...f5. The latter plan is harder to achieve compared to the usual Sicilian Svehnikov due to Black's king position. } { [%csl Gd4,Yc6][%cal Bb2b4,Gc3d4] [%clk 0:04:20] } 12... a5 { Preventing b2-b4 but weakening the b5-square in doing so, which might make it more difficult to arrange the minority attack (b7-b5) on the queenside. } { [%csl Gb4,Rb5] [%clk 0:02:06] } 13. Qd2! { The most aggresive idea and probably the best one! White would still be better in case of } { [%cal Ge1c1] [%clk 0:04:18] } (13. Bc4 d6 14. O-O g6 15. Nfe3 Kg7 $14 { but Black's task might be relatively easier preparing the usual Sveshnikov counterplay with ...f5. }) 13... d6 { [%clk 0:02:04] } 14. O-O-O { It was a very good decision by White to create an opposite-sides castling situation, which forces both players to play on the opposite wings. } { [%clk 0:03:57] } 14... g6 { [%cal Bg6f5,Gf8g7] [%clk 0:02:00] } (14... Bxf5 15. exf5 { Black logically tries to avoid this exchange because White would dominate the light squares and have a favorable attacking position due to the fact that \"opposite-colored bishops favor the attacker\". } { [%csl Rf8][%cal Gf5f6,Gf2f4,Gf1c4,Gg2g4] }) 15. Nfe3 { [%clk 0:03:56] } 15... Be6 { [%clk 0:01:58] } 16. Kb1 { [%clk 0:03:52] } 16... Kg7 { The game has developed very logically up to this point. It seems that Black should act on the queenside, while White can attack on the kingside. } { [%cal Gb7b5,Gb5b4,Gf2f4,Gh2h4] [%clk 0:01:54] } 17. f3 { A rather slow approach in a situation where every tempo is very important. This move makes the f4-push less attractive and this loss of time might give Black the upper hand with their queenside initiative. A more aggressive pawn thrust might have been } { [%clk 0:03:47] } (17. h4 { [%csl Gh5][%cal Gh4h5] } 17... Rb8 { [%csl Gb5][%cal Gb7b5] } (17... h5 18. g3 { [%csl Gf4][%cal Gf2f4] } 18... Rb8 { [%cal Gb7b5] } 19. f4 $40 { Showcasing that White might have needed the f4-push as in the game. } { [%cal Gf4f5] }) 18. h5 b5 (18... g5 19. Nf5+ $40) 19. f4 $13 { Anything is possible in this messy position. } { [%cal Gf4f5] }) 17... a4 { Even though Black should push the queenside pawns to open up lines against White's king, Black's last move makes it more difficult to achieve the b5-b4 push after White's next move. } { [%clk 0:01:51] } (17... Rb8! { would be more direct. } { [%cal Gb7b5] }) 18. a3 { Securing the b4-square and making it impossible for Black to play ...b5-b4, but weakening the light squares as well. } { [%csl Rb4][%cal Rb7b5,Rb5b4] [%clk 0:03:45] } 18... Qa5 { [%clk 0:01:47] } (18... Bxe3! { Stockfish suggests this move, intending counterplay on the light squares, and eventually preparing ...d6-d5. } 19. Nxe3 (19. Qxe3 Ra5 20. Bc4 Ne7 { [%csl Gd5][%cal Ra5d5,Ye7d5,Be6d5] }) 19... Bb3 20. Re1 { [%csl Gc4][%cal Gf1c4] } 20... Na5! $132 $40 { This is a very instructive concept! Black gains more control over the light squares, preventing Bc4 and preparing the ...d5-push. } { [%csl Yc4,Gd5][%cal Bd6d5,Ga8c8,Gc8c5,Gd8d7,Gd7e6,Gh8d8] }) 19. Bc4 { [%clk 0:03:44] } 19... b5 { [%clk 0:01:45] } 20. Ba2 { [%clk 0:03:42] } 20... Rhd8 { [%clk 0:01:43] } 21. h4! { It's time to attack as White has secured the central squares and stopped Black's offensive on the queenside. } { [%clk 0:03:37] } 21... Qa7 { [%clk 0:01:36] } 22. Rde1 { [%clk 0:03:18] } (22. h5 { would be more direct. } { [%cal Yd2h6,Bh1h6] }) 22... Ne7 { Black feels they have no choice but to remove the monster on d5. } { [%clk 0:01:32] } 23. f4 { In hindsight, Black might use this moment to give up their strategically bad bishop for one of White's knights. } { [%csl Gh5,Gf5][%cal Gf4f5,Gh4h5] [%clk 0:03:08] } (23. h5 g5 { Hence the text move as White is intending to play h5 on the next move without allowing ...g5. }) 23... exf4 { [%clk 0:01:25] } (23... Bxe3 { As on the 18th move, Black can get rid of the dark-squared bishop. } 24. Nxe3 Bxa2+ 25. Kxa2 exf4 26. Nc2 { [%cal Gd2f4] } (26. Nd5? Nxd5 27. Qxd5 Qc5 $17) 26... Qc5 $14 { With a series of exchanges, Black managed to get rid of White's active minor pieces, reduced White's attacking potential, and eventually centralized their queen. Black can put up a good fight in this complicated position. } { [%cal Gc5e5] }) 24. Nxf4 { [%clk 0:03:07] } 24... Bxa2+ { [%clk 0:01:24] } 25. Kxa2 { [%clk 0:03:06] } 25... Re8 { [%cal Ge8e4] [%clk 0:01:15] } (25... Bxe3! { It was the last call to make this exchange! } 26. Rxe3 $14 { Even though White is structurally better and has a safer king, Black has nice control over the central squares. } { [%csl Ge5,Gc5,Yd5] }) 26. Ned5! { The right knight! } { [%clk 0:02:52] } (26. Nfd5 Bxe3! { The text move avoids this exchange. }) 26... Nxd5 { [%clk 0:01:13] } 27. Nxd5 { Now this position looks like a nice illustration of a good knight vs. bad bishop scenario. Even though Black's bishop is not such a bad piece, it has little to do in terms of attacking or defensive purposes. Meanwhile, White's knight is a monster! } { [%csl Bc5,Gd5] [%clk 0:02:49] } 27... Re6 { Black targets the weak pawn on e4 by doubling the rooks and brings pieces closer to their own king. } { [%csl Ge4][%cal Ra8e8,Ge6e4] [%clk 0:01:09] } 28. Rhf1! { The move itself might not deserve an exclamation mark, but this is the first move of the killer rook! } { [%clk 0:02:46] } 28... Rae8 { [%clk 0:01:07] } 29. h5! { Always a useful strategy: softening up the squares around the opponent's king. White sacrifices the e4-pawn to open up lines. } { [%clk 0:02:37] } 29... g5 { Black decides to keep things closed. } { [%csl Rf5] [%clk 0:01:06] } (29... Rxe4 30. Rxe4 Rxe4 31. hxg6 fxg6 32. Qd3! { [%csl Gf6,Gf3][%cal Gf1f6,Rd3g6,Yd3f3] } 32... Re6 33. Qf3! $40 { White doesn't feel the pawn deficit as their attack is very strong. } { [%cal Gf3f8] }) 30. Rf5! { White immediately seizes the opportunity to exploit the weakness created by Black's previous move. White wants to double the rooks on the f-file and targets the g5-pawn. } { [%csl Gf1,Gg5][%cal Ge1f1] [%clk 0:02:19] } 30... Rxe4? { Black's position was critical and this is the losing move! } { [%clk 0:00:55] } (30... Re5! { Preventing White's next move would be better. }) 31. Rxg5+!! { A nice illustration of the queen and knight tandem as an attacking unit. } { [%clk 0:02:07] } 31... Kf8 $7 { [%clk 0:00:43] } (31... hxg5 32. Qxg5+ Kf8 (32... Kh7 33. Nf6+ Kh8 34. Qh6#) 33. Qh6+ Kg8 34. Nf6#) 32. Rg8+! { Not the only move but a classy one! } { [%clk 0:01:23] } 32... Kxg8 { [%clk 0:00:41] } 33. Qxh6 { Even though this move doesn't come with check, it's still devastating! } { [%csl Gf6][%cal Gd5f6] [%clk 0:01:22] } 33... f5 { The only move that doesn't loose material immediately. } { [%clk 0:00:33] } (33... R8e6 34. Qg5+ Kf8 (34... Kh7 35. Rxe4 $18 { [%cal Gd5f6] }) 35. Rxe4 Rxe4 36. Qh6+ Ke8 37. Nf6+ Ke7 38. Nxe4 $18) 34. Rxe4 { [%clk 0:01:09] } 34... Rxe4 { [%clk 0:00:31] } (34... fxe4 35. Nf6+ Kf7 36. Qg6+ Ke7 37. Nxe8 $18 e3 38. h6 { Even this is winning. Although Black queens the pawn first, White wins due to the exposed king. } 38... e2 39. h7 e1=Q 40. h8=Q $18) 35. Nf6+ { [%clk 0:01:02] } 35... Kf7 { [%clk 0:00:29] } 36. Qg6+ { [%clk 0:01:02] } 36... Ke7 { [%clk 0:00:26] } 37. h6 { [%clk 0:00:50] } (37. Nxe4 fxe4 38. h6 $18 { Stockfish claims White is winning as Black's bishop cannot control the h8-square in time. }) 37... Re6 { [%clk 0:00:22] } (37... Qa8 38. h7 Rh4 39. Qg7+ Ke6 40. Qg8+ $18) 38. Nd5+ { [%clk 0:00:42] } 38... Kd7 { [%clk 0:00:21] } 39. Qf7+ { [%clk 0:00:39] } 39... Kc6 { [%clk 0:00:21] } 40. Qxe6 $18 { And rest is a matter of technique as the famous saying goes... } { [%clk 0:00:38] } 40... Qh7 { [%clk 0:00:21] } 41. Ne7+ { [%clk 0:00:37] } 41... Kb6 { [%clk 0:00:20] } 42. Nxf5 { [%clk 0:00:36] } 42... Qh8 { [%clk 0:00:17] } 43. Nxd6 { [%clk 0:00:32] } 43... Bxd6 { The knight is eliminated by the bishop, but 25 moves too late. } { [%clk 0:00:16] } 44. Qxd6+ { Queen endgames are known to be very difficult, but this is easily winning for White! } { [%clk 0:00:32] } 44... Ka5 { [%clk 0:00:16] } 45. Qe6 { [%clk 0:00:32] } 45... Qf8 { [%clk 0:00:14] } (45... Qh7 46. g4 $18) 46. h7 { [%clk 0:00:30] } 46... Qg7 { [%clk 0:00:14] } 47. Qg8 { 1-0 Black resigns. } { [%clk 0:00:29] } 1-0\n";
|
|
4
|
+
export declare const PGN = "\n[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"chess\"]\n[Termination \"Normal\"]\n[Annotator \"https://lichess.org/@/NaSil\"]\n[GameId \"oj3PlZ6Q\"]\n[ECO \"B33\"]\n[Opening \"Sicilian Defense: Lasker-Pelikan Variation\"]\n[StudyName \"Game Of The Month - April 2025\"]\n[ChapterName \"Ceburashka - littleplotkin\"]\n[ChapterURL \"https://lichess.org/study/dnp96Pfx/N9EOybPh\"]\n[Orientation \"white\"]\n\n{ A high level clash where IM littleplotkin playing Black berserked and played with half their time, thus seeking complications starting from the early opening stage of the game. The 2025 April Game Of The Month witnesses a very instructive attacking game played in the Sveshnikov Sicilian with many deep points!\n\nCongratulations to Ceburashka on combining nice strategic play with a cute attacking finish in one of the typical structures of the Sicilian Defense.\n\nEnjoy! }\n1. e4 { [%clk 0:05:00] } 1... c5 { [%clk 0:02:30] } 2. Nf3 { [%clk 0:04:59] } 2... Nc6 { [%clk 0:02:29] } 3. d4 { [%clk 0:04:59] } 3... cxd4 { [%clk 0:02:29] } 4. Nxd4 { [%clk 0:04:58] } 4... Nf6 { [%clk 0:02:27] } 5. Nc3 { [%clk 0:04:53] } 5... e5 { [%clk 0:02:27] } (5... e6 6. Ndb5 Bc5 { Please compare this line with the game continuation. } 7. Nd6+ Ke7 8. Bf4 e5 9. Nf5+ Kf8 10. Bg5 { with a transposition to our game. }) 6. Ndb5! { This move is almost the only respectable move in this position and yet one of the most instructive maneuvers in the Sicilian Defense, at least for me!\nIf you see this move for the first time, you might wonder why White wants their opponent to play 6...d6 while their knight will eventually get kicked away and end up on the edge of the board, namely on the a3 square. In order to understand what is going on we should check the alternatives. } { [%cal Gb5d6] [%clk 0:04:52] } (6. Nb3 Bb4! $10 { [%cal Gd6d5,Gd7d5,Rd7d6] }) (6. Nf3 Bb4! $10 { After Black develops their so called \"bad bishop\", they can eventually play ...d6 or even the desired ...d5 if possible. The reason behind 6. Ndb5 is to keep Bf8 behind the pawn chain after 6...d6. }) (6. Nde2!? { A rare approach which gained some popularity recently. White takes the sting out of 6...Bb4 with this move, but } 6... Bc5! { Black is ready to play ...d6 next with an active \"bad bishop\". } { [%cal Gd8b6] } (6... Bb4 7. a3! { the point of 5.Nde2 } { [%cal Ge2c3,Rb4c3] }) 7. Ng3 { This rather fresh approach was seen in GM practice, but Black doesn't have a lot to worry about so far. } { [%csl Rd3,Rc4][%cal Gf1d3,Rd3c4] }) (6. Nf5!? { White prevents Black's dark-squared bishop from developing and invites a forcing sequence. } 6... d5! { [%cal Rc8f5] } 7. exd5 Bxf5 8. dxc6 bxc6! $13 { with a messy theoretical battle where Black's position is OK according to theory. Piece activity fully compensates the pawn weaknesses in Black's camp. }) 6... Bc5!? { An obscure sideline in the Sveshnikov Sicilian, also known as Lasker-Pelikan Variation of the Sicilian Defense, which will eventually transpose into the ...e6 Sicilian noted at the 5th move above. It might be a practical choice in blitz to get out of opening theory as early as possible. } { [%clk 0:02:26] } (6... d6 { is by far the main line. } { [%csl Rf8] } 7. Bg5 (7. Nd5 { a modern approach which became popular after Fabiano Caruana used it in the World Chess Championship match against Magnus Carlsen in 2018. }) 7... a6 8. Na3 { [%csl Gd5][%cal Ga3c4,Rc4e3] } 8... b5 { It seems that White has ended up with their knight on the a3-square, losing 2 tempos playing with it. However, Black only made pawn moves in the meantime, and White might eventually target the a6-b5 pawn chain, creating weaknesses in Black's camp, by playing a4 or c4. } { [%csl Ra3,Gc4][%cal Gb5b4] }) 7. Nd6+ { Even if this move looks like the most obvious follow-up, Stockfish considers } { [%clk 0:04:50] } (7. Be3! { as the best move, since our beloved silicon monster can clearly foresee and evaluate the following sequence in White's favor. } 7... d6! (7... Bxe3 8. Nd6+ { This intermediate move is the point of the previous move. } 8... Ke7 9. fxe3 $18 { Stockfish considers this position as winning because Black's king remains in the center and the White forces will join the action in a couple of moves. } { [%csl Re7,Rf7][%cal Gf1c4,Gd1d2,Ge1c1] }) 8. Bxc5 dxc5 9. Qxd8+ Kxd8 10. O-O-O+ { White has a clear initiative in this queenless middlegame position. For example: } 10... Ke7 11. Nd5+! $146 { Yes, a novelty! An important shot creating a very strong passer as an attacking unit against Black's king. It requires a precise follow-up as White's d6-pawn might be weak if White's initiative fizzles out. } 11... Nxd5 12. exd5 Nd4 13. d6+ Kd8 { Otherwise White will play Nc7, c3, and d7, winning. } (13... Kf6 14. Nc7 { Showcasing the strength of the passed d-pawn with the following sequence. } 14... Rb8 15. c3 Ne6 16. d7 Nxc7 17. d8=B+ { I like how Stockfish considers promoting the pawn into a bishop is equal to promoting it into a queen. } 17... Rxd8 18. Rxd8 $18) 14. Re1 $36 { A rather forced and interesting line. I hope I could try this line in practice if I have the chance to play against this line, and I would like to encourage the readers to do so as well! } { [%cal Gf2f4,Bb5d4,Be1e7] }) 7... Ke7! { A bold move but the only one, as after } { [%clk 0:02:25] } (7... Bxd6?! 8. Qxd6 $16 { White is much better with the blockade over the d6 and d5 squares and the bishop pair. Stockfish already considers this position as winning for White. }) 8. Nf5+ { [%clk 0:04:49] } (8. Nxc8+ { Logical to capture the valuable bishop at the cost of spending a few tempos with the knight in doing so. } 8... Rxc8 9. Bc4 Kf8 10. O-O h6 { [%csl Gg5][%cal Yc1g5] } 11. Kh1 { [%cal Gf2f4,Rc5g1] } 11... Ne7 12. f4 $40 { White has the bishop pair and Black's king is in a somewhat uncomfortable situation. } { [%csl Rf8][%cal Gf1f7,Gc4f7] }) 8... Kf8 { [%clk 0:02:24] } 9. Bg5 { It's a very well-known idea to exchange the pieces controlling the d5-square. We transposed back into the line mentioned at the 5th move. At the expense of developing their dark-squared bishop outside of the pawn chain, Black lost their castling rights. } { [%csl Gd5] [%clk 0:04:43] } 9... h6 { [%clk 0:02:17] } (9... d6 { This is sort of the \"main\" line according to the Masters Database. One recent example from GM practice: } { [%csl Yf5,Be6][%cal Gc8e6] } 10. Ne3 { [%csl Bd5][%cal Be3d5,Bc3d5] } 10... h6 11. Bxf6 { White gave up their \"good bishop\" in order to increase control over the d5-square. } 11... Qxf6 12. Bc4 { [%csl Gd5,Gc4,Gc3,Ge3,Gd1][%cal Ye3d5,Yc3d5,Bc4d5,Bd1d5] } 12... Be6 (12... Bxe3? 13. fxe3 $40 { [%cal Gf1f7,Yh1f1,Bd1d6] }) 13. O-O Kg8 { [%cal Bg8h7] } 14. Ncd5 Qd8 (14... Bxd5 15. Nxd5 Qd8 16. c3 Kh7 $16 { White has the better structure, better minor pieces, and a safer king in this middlegame with opposite-colored bishops. }) 15. c3 $14 { Huschenbeth, N. - Rakotomaharo, Fy Antenaina, 1-0, FIDE World Cup 2023, https://lichess.org/b4xGV3WX } { [%csl Yd4] }) 10. Bxf6 { [%clk 0:04:37] } 10... Qxf6 { [%clk 0:02:15] } 11. Nd5 { [%clk 0:04:27] } 11... Qd8 { [%clk 0:02:10] } 12. c3 { White will try to put pressure on Black's d-pawn and control the d5-square. Meanwhile, Black will try to play around those squares on the queenside with the minority attack (a5, b5, b4) or go for kingside play by means of ...f5. The latter plan is harder to achieve compared to the usual Sicilian Svehnikov due to Black's king position. } { [%csl Gd4,Yc6][%cal Bb2b4,Gc3d4] [%clk 0:04:20] } 12... a5 { Preventing b2-b4 but weakening the b5-square in doing so, which might make it more difficult to arrange the minority attack (b7-b5) on the queenside. } { [%csl Gb4,Rb5] [%clk 0:02:06] } 13. Qd2! { The most aggresive idea and probably the best one! White would still be better in case of } { [%cal Ge1c1] [%clk 0:04:18] } (13. Bc4 d6 14. O-O g6 15. Nfe3 Kg7 $14 { but Black's task might be relatively easier preparing the usual Sveshnikov counterplay with ...f5. }) 13... d6 { [%clk 0:02:04] } 14. O-O-O { It was a very good decision by White to create an opposite-sides castling situation, which forces both players to play on the opposite wings. } { [%clk 0:03:57] } 14... g6 { [%cal Bg6f5,Gf8g7] [%clk 0:02:00] } (14... Bxf5 15. exf5 { Black logically tries to avoid this exchange because White would dominate the light squares and have a favorable attacking position due to the fact that \"opposite-colored bishops favor the attacker\". } { [%csl Rf8][%cal Gf5f6,Gf2f4,Gf1c4,Gg2g4] }) 15. Nfe3 { [%clk 0:03:56] } 15... Be6 { [%clk 0:01:58] } 16. Kb1 { [%clk 0:03:52] } 16... Kg7 { The game has developed very logically up to this point. It seems that Black should act on the queenside, while White can attack on the kingside. } { [%cal Gb7b5,Gb5b4,Gf2f4,Gh2h4] [%clk 0:01:54] } 17. f3 { A rather slow approach in a situation where every tempo is very important. This move makes the f4-push less attractive and this loss of time might give Black the upper hand with their queenside initiative. A more aggressive pawn thrust might have been } { [%clk 0:03:47] } (17. h4 { [%csl Gh5][%cal Gh4h5] } 17... Rb8 { [%csl Gb5][%cal Gb7b5] } (17... h5 18. g3 { [%csl Gf4][%cal Gf2f4] } 18... Rb8 { [%cal Gb7b5] } 19. f4 $40 { Showcasing that White might have needed the f4-push as in the game. } { [%cal Gf4f5] }) 18. h5 b5 (18... g5 19. Nf5+ $40) 19. f4 $13 { Anything is possible in this messy position. } { [%cal Gf4f5] }) 17... a4 { Even though Black should push the queenside pawns to open up lines against White's king, Black's last move makes it more difficult to achieve the b5-b4 push after White's next move. } { [%clk 0:01:51] } (17... Rb8! { would be more direct. } { [%cal Gb7b5] }) 18. a3 { Securing the b4-square and making it impossible for Black to play ...b5-b4, but weakening the light squares as well. } { [%csl Rb4][%cal Rb7b5,Rb5b4] [%clk 0:03:45] } 18... Qa5 { [%clk 0:01:47] } (18... Bxe3! { Stockfish suggests this move, intending counterplay on the light squares, and eventually preparing ...d6-d5. } 19. Nxe3 (19. Qxe3 Ra5 20. Bc4 Ne7 { [%csl Gd5][%cal Ra5d5,Ye7d5,Be6d5] }) 19... Bb3 20. Re1 { [%csl Gc4][%cal Gf1c4] } 20... Na5! $132 $40 { This is a very instructive concept! Black gains more control over the light squares, preventing Bc4 and preparing the ...d5-push. } { [%csl Yc4,Gd5][%cal Bd6d5,Ga8c8,Gc8c5,Gd8d7,Gd7e6,Gh8d8] }) 19. Bc4 { [%clk 0:03:44] } 19... b5 { [%clk 0:01:45] } 20. Ba2 { [%clk 0:03:42] } 20... Rhd8 { [%clk 0:01:43] } 21. h4! { It's time to attack as White has secured the central squares and stopped Black's offensive on the queenside. } { [%clk 0:03:37] } 21... Qa7 { [%clk 0:01:36] } 22. Rde1 { [%clk 0:03:18] } (22. h5 { would be more direct. } { [%cal Yd2h6,Bh1h6] }) 22... Ne7 { Black feels they have no choice but to remove the monster on d5. } { [%clk 0:01:32] } 23. f4 { In hindsight, Black might use this moment to give up their strategically bad bishop for one of White's knights. } { [%csl Gh5,Gf5][%cal Gf4f5,Gh4h5] [%clk 0:03:08] } (23. h5 g5 { Hence the text move as White is intending to play h5 on the next move without allowing ...g5. }) 23... exf4 { [%clk 0:01:25] } (23... Bxe3 { As on the 18th move, Black can get rid of the dark-squared bishop. } 24. Nxe3 Bxa2+ 25. Kxa2 exf4 26. Nc2 { [%cal Gd2f4] } (26. Nd5? Nxd5 27. Qxd5 Qc5 $17) 26... Qc5 $14 { With a series of exchanges, Black managed to get rid of White's active minor pieces, reduced White's attacking potential, and eventually centralized their queen. Black can put up a good fight in this complicated position. } { [%cal Gc5e5] }) 24. Nxf4 { [%clk 0:03:07] } 24... Bxa2+ { [%clk 0:01:24] } 25. Kxa2 { [%clk 0:03:06] } 25... Re8 { [%cal Ge8e4] [%clk 0:01:15] } (25... Bxe3! { It was the last call to make this exchange! } 26. Rxe3 $14 { Even though White is structurally better and has a safer king, Black has nice control over the central squares. } { [%csl Ge5,Gc5,Yd5] }) 26. Ned5! { The right knight! } { [%clk 0:02:52] } (26. Nfd5 Bxe3! { The text move avoids this exchange. }) 26... Nxd5 { [%clk 0:01:13] } 27. Nxd5 { Now this position looks like a nice illustration of a good knight vs. bad bishop scenario. Even though Black's bishop is not such a bad piece, it has little to do in terms of attacking or defensive purposes. Meanwhile, White's knight is a monster! } { [%csl Bc5,Gd5] [%clk 0:02:49] } 27... Re6 { Black targets the weak pawn on e4 by doubling the rooks and brings pieces closer to their own king. } { [%csl Ge4][%cal Ra8e8,Ge6e4] [%clk 0:01:09] } 28. Rhf1! { The move itself might not deserve an exclamation mark, but this is the first move of the killer rook! } { [%clk 0:02:46] } 28... Rae8 { [%clk 0:01:07] } 29. h5! { Always a useful strategy: softening up the squares around the opponent's king. White sacrifices the e4-pawn to open up lines. } { [%clk 0:02:37] } 29... g5 { Black decides to keep things closed. } { [%csl Rf5] [%clk 0:01:06] } (29... Rxe4 30. Rxe4 Rxe4 31. hxg6 fxg6 32. Qd3! { [%csl Gf6,Gf3][%cal Gf1f6,Rd3g6,Yd3f3] } 32... Re6 33. Qf3! $40 { White doesn't feel the pawn deficit as their attack is very strong. } { [%cal Gf3f8] }) 30. Rf5! { White immediately seizes the opportunity to exploit the weakness created by Black's previous move. White wants to double the rooks on the f-file and targets the g5-pawn. } { [%csl Gf1,Gg5][%cal Ge1f1] [%clk 0:02:19] } 30... Rxe4? { Black's position was critical and this is the losing move! } { [%clk 0:00:55] } (30... Re5! { Preventing White's next move would be better. }) 31. Rxg5+!! { A nice illustration of the queen and knight tandem as an attacking unit. } { [%clk 0:02:07] } 31... Kf8 $7 { [%clk 0:00:43] } (31... hxg5 32. Qxg5+ Kf8 (32... Kh7 33. Nf6+ Kh8 34. Qh6#) 33. Qh6+ Kg8 34. Nf6#) 32. Rg8+! { Not the only move but a classy one! } { [%clk 0:01:23] } 32... Kxg8 { [%clk 0:00:41] } 33. Qxh6 { Even though this move doesn't come with check, it's still devastating! } { [%csl Gf6][%cal Gd5f6] [%clk 0:01:22] } 33... f5 { The only move that doesn't loose material immediately. } { [%clk 0:00:33] } (33... R8e6 34. Qg5+ Kf8 (34... Kh7 35. Rxe4 $18 { [%cal Gd5f6] }) 35. Rxe4 Rxe4 36. Qh6+ Ke8 37. Nf6+ Ke7 38. Nxe4 $18) 34. Rxe4 { [%clk 0:01:09] } 34... Rxe4 { [%clk 0:00:31] } (34... fxe4 35. Nf6+ Kf7 36. Qg6+ Ke7 37. Nxe8 $18 e3 38. h6 { Even this is winning. Although Black queens the pawn first, White wins due to the exposed king. } 38... e2 39. h7 e1=Q 40. h8=Q $18) 35. Nf6+ { [%clk 0:01:02] } 35... Kf7 { [%clk 0:00:29] } 36. Qg6+ { [%clk 0:01:02] } 36... Ke7 { [%clk 0:00:26] } 37. h6 { [%clk 0:00:50] } (37. Nxe4 fxe4 38. h6 $18 { Stockfish claims White is winning as Black's bishop cannot control the h8-square in time. }) 37... Re6 { [%clk 0:00:22] } (37... Qa8 38. h7 Rh4 39. Qg7+ Ke6 40. Qg8+ $18) 38. Nd5+ { [%clk 0:00:42] } 38... Kd7 { [%clk 0:00:21] } 39. Qf7+ { [%clk 0:00:39] } 39... Kc6 { [%clk 0:00:21] } 40. Qxe6 $18 { And rest is a matter of technique as the famous saying goes... } { [%clk 0:00:38] } 40... Qh7 { [%clk 0:00:21] } 41. Ne7+ { [%clk 0:00:37] } 41... Kb6 { [%clk 0:00:20] } 42. Nxf5 { [%clk 0:00:36] } 42... Qh8 { [%clk 0:00:17] } 43. Nxd6 { [%clk 0:00:32] } 43... Bxd6 { The knight is eliminated by the bishop, but 25 moves too late. } { [%clk 0:00:16] } 44. Qxd6+ { Queen endgames are known to be very difficult, but this is easily winning for White! } { [%clk 0:00:32] } 44... Ka5 { [%clk 0:00:16] } 45. Qe6 { [%clk 0:00:32] } 45... Qf8 { [%clk 0:00:14] } (45... Qh7 46. g4 $18) 46. h7 { [%clk 0:00:30] } 46... Qg7 { [%clk 0:00:14] } 47. Qg8 { 1-0 Black resigns. } { [%clk 0:00:29] } 1-0\n";
|
|
5
5
|
/**
|
|
6
6
|
* https://lichess.org/study/dnp96Pfx/N9EOybPh
|
|
7
7
|
*/
|
|
8
8
|
export declare const PGN_small = "\n[Variant \"From Position\"]\n[FEN \"rnbqkbnr/pppppppp/8/8/3PP3/8/PP3PPP/RNBQKBNR w KQkq - 0 1\"]\n\n1. e5 d6\n";
|
|
9
|
-
export declare const PGN_bad = "[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[Round \"?\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"
|
|
10
|
-
export declare const PGN_bad_2 = "\n[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[Round \"?\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"
|
|
11
|
-
export declare const PGN_bad_3 = "[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[Round \"?\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"
|
|
9
|
+
export declare const PGN_bad = "[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[Round \"?\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"chess\"]\n[Termination \"Normal\"]\n[Annotator \"https://lichess.org/@/NaSil\"]\n[GameId \"oj3PlZ6Q\"]\n[ECO \"B33\"]\n[Opening \"Sicilian Defense: Lasker-Pelikan Variation\"]\n[StudyName \"Game Of The Month - April 2025\"]\n[ChapterName \"Ceburashka - littleplotkin\"]\n[ChapterURL \"https://lichess.org/study/dnp96Pfx/N9EOybPh\"]\n[Orientation \"white\"]\n\n{ A high level clash where IM littleplotkin playing Black berserked and played with half their time, thus seeking complications starting from the early opening stage of the game. The 2025 April Game Of The Month witnesses a very instructive attacking game played in the Sveshnikov Sicilian with many deep points!\n\nCongratulations to Ceburashka on combining nice strategic play with a cute attacking finish in one of the typical structures of the Sicilian Defense.\n\nEnjoy! } 1. e4 { [%clk 0:05:00] } c5 { [%clk 0:02:30] } 2. Nf3 { [%clk 0:04:59] } Nc6 { [%clk 0:02:29] } 3. d4 { [%clk 0:04:59] } cxd4 { [%clk 0:02:29] } 4. Nxd4 { [%clk 0:04:58] } Nf6 { [%clk 0:02:27] } 5. Nc3 { [%clk 0:04:53] } e5 { [%clk 0:02:27] } ( 5... e6 6. Ndb5 Bc5 { Please compare this line with the game continuation. } 7. Nd6+ Ke7 8. Bf4 e5 9. Nf5+ Kf8 10. Bg5 { with a transposition to our game. } ) 6. Nb3 ( 6. Nf3 Bb4 $1 $10 { After Black develops their so called \"bad bishop\", they can eventually play ...d6 or even the desired ...d5 if possible. The reason behind 6. Ndb5 is to keep Bf8 behind the pawn chain after 6...d6. } ) ( 6. Nde2 $5 { A rare approach which gained some popularity recently. White takes the sting out of 6...Bb4 with this move, but } 6... Bc5 $1 { Black is ready to play ...d6 next with an active \"bad bishop\". } { [%cal Gd8b6] } ( 6... Bb4 7. a3 $1 { the point of 5.Nde2 } { [%cal Ge2c3,Rb4c3] } ) 7. Ng3 { This rather fresh approach was seen in GM practice, but Black doesn't have a lot to worry about so far. } { [%csl Rd3,Rc4][%cal Gf1d3,Rd3c4] } ) ( 6. Nf5 $5 { White prevents Black's dark-squared bishop from developing and invites a forcing sequence. } 6... d5 $1 { [%cal Rc8f5] } 7. exd5 Bxf5 8. dxc6 bxc6 $1 $13 { with a messy theoretical battle where Black's position is OK according to theory. Piece activity fully compensates the pawn weaknesses in Black's camp. } ) 6... Bb4 $1 $10 { [%cal Gd6d5,Gd7d5,Rd7d6] } 1-0\n";
|
|
10
|
+
export declare const PGN_bad_2 = "\n[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[Round \"?\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"chess\"]\n[Termination \"Normal\"]\n[Annotator \"https://lichess.org/@/NaSil\"]\n[GameId \"oj3PlZ6Q\"]\n[ECO \"B33\"]\n[Opening \"Sicilian Defense: Lasker-Pelikan Variation\"]\n[StudyName \"Game Of The Month - April 2025\"]\n[ChapterName \"Ceburashka - littleplotkin\"]\n[ChapterURL \"https://lichess.org/study/dnp96Pfx/N9EOybPh\"]\n[Orientation \"white\"]\n\n{ A high level clash where IM littleplotkin playing Black berserked and played with half their time, thus seeking complications starting from the early opening stage of the game. The 2025 April Game Of The Month witnesses a very instructive attacking game played in the Sveshnikov Sicilian with many deep points!\n\nCongratulations to Ceburashka on combining nice strategic play with a cute attacking finish in one of the typical structures of the Sicilian Defense.\n\nEnjoy! } 1. e4 { [%clk 0:05:00] } c5 { [%clk 0:02:30] } 2. Nf3 { [%clk 0:04:59] } Nc6 { [%clk 0:02:29] } 3. d4 { [%clk 0:04:59] } cxd4 { [%clk 0:02:29] } 4. Nxd4 { [%clk 0:04:58] } Nf6 { [%clk 0:02:27] } 5. Nc3 { [%clk 0:04:53] } e5 { [%clk 0:02:27] } ( 5... e6 6. Ndb5 Bc5 { Please compare this line with the game continuation. } 7. Nd6+ Ke7 8. Bf4 e5 9. Nf5+ Kf8 10. Bg5 { with a transposition to our game. } ) 6. Nde2 $5 { A rare approach which gained some popularity recently. White takes the sting out of 6...Bb4 with this move, but } ( 6. Nf5 $5 { White prevents Black's dark-squared bishop from developing and invites a forcing sequence. } 6... d5 $1 { [%cal Rc8f5] } 7. exd5 Bxf5 8. dxc6 bxc6 $1 $13 { with a messy theoretical battle where Black's position is OK according to theory. Piece activity fully compensates the pawn weaknesses in Black's camp. } ) 6... Bc5 $1 { Black is ready to play ...d6 next with an active \"bad bishop\". } { [%cal Gd8b6] } ( 6... Bb4 7. a3 $1 { the point of 5.Nde2 } { [%cal Ge2c3,Rb4c3] } ) 7. Ng3 { This rather fresh approach was seen in GM practice, but Black doesn't have a lot to worry about so far. } { [%csl Rd3,Rc4][%cal Gf1d3,Rd3c4] } 1-0";
|
|
11
|
+
export declare const PGN_bad_3 = "[Event \"Rated blitz tournament https://lichess.org/tournament/spring25\"]\n[Site \"https://lichess.org/oj3PlZ6Q\"]\n[Date \"2025.04.26\"]\n[Round \"?\"]\n[White \"Ceburashka\"]\n[Black \"littleplotkin\"]\n[Result \"1-0\"]\n[WhiteElo \"2356\"]\n[BlackElo \"2767\"]\n[BlackTitle \"IM\"]\n[TimeControl \"300+0\"]\n[Variant \"chess\"]\n[Termination \"Normal\"]\n[Annotator \"https://lichess.org/@/NaSil\"]\n[GameId \"oj3PlZ6Q\"]\n[ECO \"B33\"]\n[Opening \"Sicilian Defense: Lasker-Pelikan Variation\"]\n[StudyName \"Game Of The Month - April 2025\"]\n[ChapterName \"Ceburashka - littleplotkin\"]\n[ChapterURL \"https://lichess.org/study/dnp96Pfx/N9EOybPh\"]\n[Orientation \"white\"]\n\n{ A high level clash where IM littleplotkin playing Black berserked and played with half their time, thus seeking complications starting from the early opening stage of the game. The 2025 April Game Of The Month witnesses a very instructive attacking game played in the Sveshnikov Sicilian with many deep points!\n\nCongratulations to Ceburashka on combining nice strategic play with a cute attacking finish in one of the typical structures of the Sicilian Defense.\n\nEnjoy! } 1. e4 { [%clk 0:05:00] } c5 { [%clk 0:02:30] } 2. Nf3 { [%clk 0:04:59] } Nc6 { [%clk 0:02:29] } 3. d4 { [%clk 0:04:59] } cxd4 { [%clk 0:02:29] } 4. Nxd4 { [%clk 0:04:58] } Nf6 { [%clk 0:02:27] } 5. Nc3 { [%clk 0:04:53] } e5 { [%clk 0:02:27] } ( 5... e6 6. Ndb5 Be7 ( 6... Bc5 { Please compare this line with the game continuation. } 7. Nd6+ Ke7 8. Bf4 e5 9. Nf5+ Kf8 10. Bg5 { with a transposition to our game. } ) ) 6. Nde2 $5 { A rare approach which gained some popularity recently. White takes the sting out of 6...Bb4 with this move, but } ( 6. Nf5 $5 { White prevents Black's dark-squared bishop from developing and invites a forcing sequence. } 6... d5 $1 { [%cal Rc8f5] } 7. exd5 Bxf5 8. dxc6 bxc6 $1 $13 { with a messy theoretical battle where Black's position is OK according to theory. Piece activity fully compensates the pawn weaknesses in Black's camp. } ) 6... Bc5 $1 { Black is ready to play ...d6 next with an active \"bad bishop\". } { [%cal Gd8b6] } ( 6... Bb4 7. a3 $1 { the point of 5.Nde2 } { [%cal Ge2c3,Rb4c3] } ) 7. Ng3 { This rather fresh approach was seen in GM practice, but Black doesn't have a lot to worry about so far. } { [%csl Rd3,Rc4][%cal Gf1d3,Rd3c4] } 1-0\n";
|
package/dist/(constants)/png.js
CHANGED
|
@@ -12,7 +12,7 @@ export const PGN = `
|
|
|
12
12
|
[BlackElo "2767"]
|
|
13
13
|
[BlackTitle "IM"]
|
|
14
14
|
[TimeControl "300+0"]
|
|
15
|
-
[Variant "
|
|
15
|
+
[Variant "chess"]
|
|
16
16
|
[Termination "Normal"]
|
|
17
17
|
[Annotator "https://lichess.org/@/NaSil"]
|
|
18
18
|
[GameId "oj3PlZ6Q"]
|
|
@@ -51,7 +51,7 @@ export const PGN_bad = `[Event "Rated blitz tournament https://lichess.org/tourn
|
|
|
51
51
|
[BlackElo "2767"]
|
|
52
52
|
[BlackTitle "IM"]
|
|
53
53
|
[TimeControl "300+0"]
|
|
54
|
-
[Variant "
|
|
54
|
+
[Variant "chess"]
|
|
55
55
|
[Termination "Normal"]
|
|
56
56
|
[Annotator "https://lichess.org/@/NaSil"]
|
|
57
57
|
[GameId "oj3PlZ6Q"]
|
|
@@ -80,7 +80,7 @@ export const PGN_bad_2 = `
|
|
|
80
80
|
[BlackElo "2767"]
|
|
81
81
|
[BlackTitle "IM"]
|
|
82
82
|
[TimeControl "300+0"]
|
|
83
|
-
[Variant "
|
|
83
|
+
[Variant "chess"]
|
|
84
84
|
[Termination "Normal"]
|
|
85
85
|
[Annotator "https://lichess.org/@/NaSil"]
|
|
86
86
|
[GameId "oj3PlZ6Q"]
|
|
@@ -107,7 +107,7 @@ export const PGN_bad_3 = `[Event "Rated blitz tournament https://lichess.org/tou
|
|
|
107
107
|
[BlackElo "2767"]
|
|
108
108
|
[BlackTitle "IM"]
|
|
109
109
|
[TimeControl "300+0"]
|
|
110
|
-
[Variant "
|
|
110
|
+
[Variant "chess"]
|
|
111
111
|
[Termination "Normal"]
|
|
112
112
|
[Annotator "https://lichess.org/@/NaSil"]
|
|
113
113
|
[GameId "oj3PlZ6Q"]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getTreeViewerVirtualScrollApi } from "./treeViewerVirtualScroll.js";
|
|
1
2
|
export const ACTIVE_MOVE_CLASS = "active-move-in-tree-viewer";
|
|
2
3
|
/**
|
|
3
4
|
* Представляет функцию для плавного скролла до активного хода в дереве ходов
|
|
@@ -5,6 +6,11 @@ export const ACTIVE_MOVE_CLASS = "active-move-in-tree-viewer";
|
|
|
5
6
|
* @param behavior - Поведение скролла ('smooth' | 'instant' | 'auto')
|
|
6
7
|
*/
|
|
7
8
|
function scrollToActiveMove(containerId = "tree-viewer-container", behavior = "instant") {
|
|
9
|
+
const virtual = getTreeViewerVirtualScrollApi();
|
|
10
|
+
if (virtual) {
|
|
11
|
+
virtual.scrollToCurrentNode(behavior);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
8
14
|
const container = document.getElementById(containerId);
|
|
9
15
|
if (!container) {
|
|
10
16
|
console.warn(`Контейнер с ID "${containerId}" не найден`);
|
|
@@ -66,6 +72,13 @@ function isActiveMoveVisible(containerId = "tree-viewer-container") {
|
|
|
66
72
|
* @param behavior - Поведение скролла ('smooth' | 'instant' | 'auto')
|
|
67
73
|
*/
|
|
68
74
|
export function scrollToActiveMoveIfNeeded(containerId = "tree-viewer-container", behavior = "instant") {
|
|
75
|
+
const virtual = getTreeViewerVirtualScrollApi();
|
|
76
|
+
if (virtual) {
|
|
77
|
+
if (!isActiveMoveVisible(containerId)) {
|
|
78
|
+
virtual.scrollToCurrentNode(behavior);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
69
82
|
if (!isActiveMoveVisible(containerId)) {
|
|
70
83
|
scrollToActiveMove(containerId, behavior);
|
|
71
84
|
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import { getTreeViewerVirtualScrollApi } from "./treeViewerVirtualScroll.js";
|
|
1
2
|
/**
|
|
2
3
|
* Представляет функцию для скролла к началу дерева ходов (первый видимый ход)
|
|
3
4
|
* @param containerId - ID контейнера с деревом ходов
|
|
4
5
|
* @param behavior - Поведение скролла ('smooth' | 'instant' | 'auto')
|
|
5
6
|
*/
|
|
6
7
|
export function scrollToStart(containerId = "tree-viewer-container", behavior = "instant") {
|
|
8
|
+
const virtual = getTreeViewerVirtualScrollApi();
|
|
9
|
+
if (virtual) {
|
|
10
|
+
virtual.scrollToStart(behavior);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
7
13
|
const container = document.getElementById(containerId);
|
|
8
14
|
if (!container) {
|
|
9
15
|
console.warn(`Контейнер с ID "${containerId}" не найден`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Представляет API скролла виртуализованного TreeViewer для соседних компонентов
|
|
3
|
+
* (панель навигации не является потомком TreeViewer в DOM).
|
|
4
|
+
*/
|
|
5
|
+
export type TreeViewerVirtualScrollApi = {
|
|
6
|
+
/** Возвращает void после прокрутки к строке с текущим узлом. */
|
|
7
|
+
scrollToCurrentNode: (behavior?: ScrollBehavior) => void;
|
|
8
|
+
/** Возвращает void после прокрутки к началу списка ходов. */
|
|
9
|
+
scrollToStart: (behavior?: ScrollBehavior) => void;
|
|
10
|
+
};
|
|
11
|
+
/** Представляет регистрацию API на время жизни TreeViewer. */
|
|
12
|
+
export declare function setTreeViewerVirtualScrollApi(next: TreeViewerVirtualScrollApi | null): void;
|
|
13
|
+
/** Представляет получение API скролла, если смонтирован TreeViewer. */
|
|
14
|
+
export declare function getTreeViewerVirtualScrollApi(): TreeViewerVirtualScrollApi | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
let api = null;
|
|
2
|
+
/** Представляет регистрацию API на время жизни TreeViewer. */
|
|
3
|
+
export function setTreeViewerVirtualScrollApi(next) {
|
|
4
|
+
api = next;
|
|
5
|
+
}
|
|
6
|
+
/** Представляет получение API скролла, если смонтирован TreeViewer. */
|
|
7
|
+
export function getTreeViewerVirtualScrollApi() {
|
|
8
|
+
return api;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Представляет цепочку главной линии: от корневого узла ходов по первым дочерним узлам.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildMainLineNodes(rootMoves: ChessTreeNode): ChessTreeNode[];
|
|
6
|
+
/**
|
|
7
|
+
* Представляет индекс строки виртуального списка для узла: главная линия или вариант,
|
|
8
|
+
* отображаемый внутри строки первого ребёнка родителя.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getVirtualRowIndexForNode(node: ChessTreeNode, mainLine: ChessTreeNode[], getParent: (n: ChessTreeNode) => ChessTreeNode | null): number;
|
|
11
|
+
/**
|
|
12
|
+
* Представляет значение needSpace для узла главной линии (как у рекурсивного Move).
|
|
13
|
+
*/
|
|
14
|
+
export declare function needSpaceForMainLineNode(node: ChessTreeNode, getParent: (n: ChessTreeNode) => ChessTreeNode | null): boolean;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Представляет цепочку главной линии: от корневого узла ходов по первым дочерним узлам.
|
|
3
|
+
*/
|
|
4
|
+
export function buildMainLineNodes(rootMoves) {
|
|
5
|
+
const out = [];
|
|
6
|
+
let cur = rootMoves;
|
|
7
|
+
while (cur) {
|
|
8
|
+
out.push(cur);
|
|
9
|
+
cur = cur.children[0];
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Представляет индекс строки виртуального списка для узла: главная линия или вариант,
|
|
15
|
+
* отображаемый внутри строки первого ребёнка родителя.
|
|
16
|
+
*/
|
|
17
|
+
export function getVirtualRowIndexForNode(node, mainLine, getParent) {
|
|
18
|
+
const mainLineIndex = new Map(mainLine.map((n, i) => [n.id, i]));
|
|
19
|
+
let cur = node;
|
|
20
|
+
while (cur) {
|
|
21
|
+
const idx = mainLineIndex.get(cur.id);
|
|
22
|
+
if (idx !== undefined) {
|
|
23
|
+
return idx;
|
|
24
|
+
}
|
|
25
|
+
const parent = getParent(cur);
|
|
26
|
+
if (!parent) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
if (parent.children[0]?.id !== cur.id) {
|
|
30
|
+
const anchor = parent.children[0];
|
|
31
|
+
return anchor ? (mainLineIndex.get(anchor.id) ?? 0) : 0;
|
|
32
|
+
}
|
|
33
|
+
cur = parent;
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Представляет значение needSpace для узла главной линии (как у рекурсивного Move).
|
|
39
|
+
*/
|
|
40
|
+
export function needSpaceForMainLineNode(node, getParent) {
|
|
41
|
+
const parent = getParent(node);
|
|
42
|
+
return parent ? parent.children.length > 1 : false;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@connectorvol/tree",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist",
|
|
6
6
|
"!dist/**/*.test.*",
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
"test:ui": "npx playwright test --ui"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@connectorvol/
|
|
42
|
+
"@connectorvol/chessops": "1.0.0",
|
|
43
|
+
"@connectorvol/shared": "2.0.0",
|
|
43
44
|
"clsx": "^2.1.1",
|
|
44
45
|
"tailwind-merge": "^3.3.1",
|
|
45
46
|
"tailwind-variants": "^3.1.1"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
|
-
"@connectorvol/chessboard": "
|
|
49
|
-
"@connectorvol/chessops": "0.16.0",
|
|
49
|
+
"@connectorvol/chessboard": "2.0.0",
|
|
50
50
|
"@ianvs/prettier-plugin-sort-imports": "4.5.1",
|
|
51
51
|
"@internationalized/date": "3.9.0",
|
|
52
52
|
"@lucide/svelte": "^0.548.0",
|