@connectorvol/tree 4.1.0 → 4.3.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 (38) hide show
  1. package/dist/(classes)/chessTree.svelte.d.ts +2 -0
  2. package/dist/(classes)/chessTree.svelte.js +7 -1
  3. package/dist/(components)/Move.svelte +346 -310
  4. package/dist/(components)/MoveWithIcon.svelte +79 -48
  5. package/dist/(components)/NagBadges.svelte +30 -30
  6. package/dist/(components)/TreeViewer.svelte +878 -842
  7. package/dist/(components)/TreeViewerPanelManager.svelte +99 -98
  8. package/dist/(components)/VariantDropdownNavigator.svelte +177 -173
  9. package/dist/(components)/VariantDropdownNavigator.svelte.d.ts +3 -0
  10. package/dist/(utils)/parseSanMove.d.ts +6 -0
  11. package/dist/(utils)/parseSanMove.js +40 -7
  12. package/dist/components/ui/checkbox/checkbox.svelte +39 -0
  13. package/dist/components/ui/checkbox/checkbox.svelte.d.ts +4 -0
  14. package/dist/components/ui/checkbox/index.d.ts +2 -0
  15. package/dist/components/ui/checkbox/index.js +4 -0
  16. package/dist/components/ui/popover/index.d.ts +9 -0
  17. package/dist/components/ui/popover/index.js +11 -0
  18. package/dist/components/ui/popover/popover-close.svelte +7 -0
  19. package/dist/components/ui/popover/popover-close.svelte.d.ts +4 -0
  20. package/dist/components/ui/popover/popover-content.svelte +31 -0
  21. package/dist/components/ui/popover/popover-content.svelte.d.ts +10 -0
  22. package/dist/components/ui/popover/popover-description.svelte +20 -0
  23. package/dist/components/ui/popover/popover-description.svelte.d.ts +5 -0
  24. package/dist/components/ui/popover/popover-header.svelte +20 -0
  25. package/dist/components/ui/popover/popover-header.svelte.d.ts +5 -0
  26. package/dist/components/ui/popover/popover-portal.svelte +7 -0
  27. package/dist/components/ui/popover/popover-portal.svelte.d.ts +3 -0
  28. package/dist/components/ui/popover/popover-title.svelte +20 -0
  29. package/dist/components/ui/popover/popover-title.svelte.d.ts +5 -0
  30. package/dist/components/ui/popover/popover-trigger.svelte +17 -0
  31. package/dist/components/ui/popover/popover-trigger.svelte.d.ts +4 -0
  32. package/dist/components/ui/popover/popover.svelte +7 -0
  33. package/dist/components/ui/popover/popover.svelte.d.ts +3 -0
  34. package/dist/components/ui/toggle/index.d.ts +3 -0
  35. package/dist/components/ui/toggle/index.js +5 -0
  36. package/dist/components/ui/toggle/toggle.svelte +51 -0
  37. package/dist/components/ui/toggle/toggle.svelte.d.ts +43 -0
  38. package/package.json +11 -9
@@ -1,109 +1,110 @@
1
1
  <script lang="ts">
2
- import ArrowLeft from "@lucide/svelte/icons/arrow-left";
3
- import ArrowRight from "@lucide/svelte/icons/arrow-right";
4
- import ListStart from "@lucide/svelte/icons/list-start";
5
- import ListEnd from "@lucide/svelte/icons/list-end";
6
- import PlayButtonIcon from "@lucide/svelte/icons/circle-play";
7
- import StopButtonIcon from "@lucide/svelte/icons/circle-stop";
8
- import * as Button from "../components/ui/button/index.js";
9
- import * as ButtonGroup from "../components/ui/button-group/index.js";
2
+ import ArrowLeft from "@lucide/svelte/icons/arrow-left";
3
+ import ArrowRight from "@lucide/svelte/icons/arrow-right";
4
+ import ListStart from "@lucide/svelte/icons/list-start";
5
+ import ListEnd from "@lucide/svelte/icons/list-end";
6
+ import PlayButtonIcon from "@lucide/svelte/icons/circle-play";
7
+ import StopButtonIcon from "@lucide/svelte/icons/circle-stop";
8
+ import * as Button from "../components/ui/button/index.js";
9
+ import * as ButtonGroup from "../components/ui/button-group/index.js";
10
10
 
11
- import type { Snippet } from "svelte";
12
- import type { ChessTree } from "../(classes)/chessTree.svelte.js";
13
- import type { ClassValue } from "clsx";
14
- import { getOrCreateTreeViewerPanelNavigation } from "../(utils)/treeViewerPanelNavigation.svelte.js";
11
+ import type { Snippet } from "svelte";
12
+ import type { ChessTree } from "../(classes)/chessTree.svelte.js";
13
+ import type { ClassValue } from "clsx";
14
+ import { getOrCreateTreeViewerPanelNavigation } from "../(utils)/treeViewerPanelNavigation.svelte.js";
15
15
 
16
- type Props = {
17
- chessTree: ChessTree;
18
- className?: ClassValue;
19
- setChessFen: (fen: string) => void;
20
- setChessboardFen: (animationTime?: number) => void;
21
- extraActions?: Snippet;
22
- };
16
+ type Props = {
17
+ chessTree: ChessTree;
18
+ className?: ClassValue;
19
+ setChessFen: (fen: string) => void;
20
+ setChessboardFen: (animationTime?: number) => void;
21
+ extraActions?: Snippet;
22
+ };
23
23
 
24
- const {
25
- extraActions,
26
- chessTree,
27
- setChessFen,
28
- setChessboardFen,
29
- className,
30
- }: Props = $props();
24
+ const {
25
+ extraActions,
26
+ chessTree,
27
+ setChessFen,
28
+ setChessboardFen,
29
+ className,
30
+ }: Props = $props();
31
31
 
32
- const panelNav = $derived.by(() =>
33
- getOrCreateTreeViewerPanelNavigation(chessTree),
34
- );
35
-
36
- $effect(() => {
37
- panelNav.bindCallbacks(setChessFen, setChessboardFen);
38
- });
32
+ const panelNav = $derived.by(() => {
33
+ const nav = getOrCreateTreeViewerPanelNavigation(chessTree);
34
+ nav.bindCallbacks(setChessFen, setChessboardFen);
35
+ return nav;
36
+ });
39
37
  </script>
40
38
 
41
39
  <div class={[className]}>
42
- <ButtonGroup.Root>
43
- <ButtonGroup.Root class="hidden sm:flex"></ButtonGroup.Root>
44
40
  <ButtonGroup.Root>
45
- <Button.Root
46
- variant="outline"
47
- size="icon"
48
- aria-label="Previous Move"
49
- onclick={() => panelNav.selectPreviousNode()}
50
- class={{
51
- "bg-primary text-white": panelNav.highlightedButtons.previous,
52
- "cursor-pointer": true,
53
- }}
54
- >
55
- <ArrowLeft />
56
- </Button.Root>
57
- <Button.Root
58
- variant="outline"
59
- size="icon"
60
- aria-label="Go to Start"
61
- onclick={() => panelNav.stepToStart()}
62
- class={{
63
- "bg-primary text-white": panelNav.highlightedButtons.start,
64
- }}
65
- >
66
- <ListStart />
67
- </Button.Root>
68
- <Button.Root
69
- variant={panelNav.isPlaying ? "default" : "outline"}
70
- size="icon"
71
- aria-label={panelNav.isPlaying ? "Stop Auto Play" : "Start Auto Play"}
72
- onclick={() => panelNav.toggleAutoPlay()}
73
- class={{
74
- "bg-primary text-white": panelNav.isPlaying,
75
- }}
76
- >
77
- {#if panelNav.isPlaying}
78
- <StopButtonIcon />
79
- {:else}
80
- <PlayButtonIcon />
81
- {/if}
82
- </Button.Root>
83
- <Button.Root
84
- variant="outline"
85
- size="icon"
86
- aria-label="Go to End"
87
- onclick={() => panelNav.stepToEnd()}
88
- class={{
89
- "bg-primary text-white": panelNav.highlightedButtons.end,
90
- }}
91
- >
92
- <ListEnd />
93
- </Button.Root>
94
- <Button.Root
95
- variant="outline"
96
- size="icon"
97
- aria-label="Next Move"
98
- onclick={() => panelNav.selectNextNode()}
99
- class={{
100
- "bg-primary text-white": panelNav.highlightedButtons.next,
101
- }}
102
- >
103
- <ArrowRight />
104
- </Button.Root>
41
+ <ButtonGroup.Root class="hidden sm:flex"></ButtonGroup.Root>
42
+ <ButtonGroup.Root>
43
+ <Button.Root
44
+ variant="outline"
45
+ size="icon"
46
+ aria-label="Previous Move"
47
+ onclick={() => panelNav.selectPreviousNode()}
48
+ class={{
49
+ "bg-primary text-white":
50
+ panelNav.highlightedButtons.previous,
51
+ "cursor-pointer": true,
52
+ }}
53
+ >
54
+ <ArrowLeft />
55
+ </Button.Root>
56
+ <Button.Root
57
+ variant="outline"
58
+ size="icon"
59
+ aria-label="Go to Start"
60
+ onclick={() => panelNav.stepToStart()}
61
+ class={{
62
+ "bg-primary text-white": panelNav.highlightedButtons.start,
63
+ }}
64
+ >
65
+ <ListStart />
66
+ </Button.Root>
67
+ <Button.Root
68
+ variant={panelNav.isPlaying ? "default" : "outline"}
69
+ size="icon"
70
+ aria-label={panelNav.isPlaying
71
+ ? "Stop Auto Play"
72
+ : "Start Auto Play"}
73
+ onclick={() => panelNav.toggleAutoPlay()}
74
+ class={{
75
+ "bg-primary text-white": panelNav.isPlaying,
76
+ }}
77
+ >
78
+ {#if panelNav.isPlaying}
79
+ <StopButtonIcon />
80
+ {:else}
81
+ <PlayButtonIcon />
82
+ {/if}
83
+ </Button.Root>
84
+ <Button.Root
85
+ variant="outline"
86
+ size="icon"
87
+ aria-label="Go to End"
88
+ onclick={() => panelNav.stepToEnd()}
89
+ class={{
90
+ "bg-primary text-white": panelNav.highlightedButtons.end,
91
+ }}
92
+ >
93
+ <ListEnd />
94
+ </Button.Root>
95
+ <Button.Root
96
+ variant="outline"
97
+ size="icon"
98
+ aria-label="Next Move"
99
+ onclick={() => panelNav.selectNextNode()}
100
+ class={{
101
+ "bg-primary text-white": panelNav.highlightedButtons.next,
102
+ }}
103
+ >
104
+ <ArrowRight />
105
+ </Button.Root>
106
+ </ButtonGroup.Root>
107
+ <ButtonGroup.Root class="hidden sm:flex"></ButtonGroup.Root>
105
108
  </ButtonGroup.Root>
106
- <ButtonGroup.Root class="hidden sm:flex"></ButtonGroup.Root>
107
- </ButtonGroup.Root>
108
- {@render extraActions?.()}
109
+ {@render extraActions?.()}
109
110
  </div>
@@ -1,173 +1,177 @@
1
- <script lang="ts">
2
- import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
3
- import NagBadges from "./NagBadges.svelte";
4
- type Props = {
5
- variants: ChessTreeNode[];
6
- onActivateVariant: (node: ChessTreeNode) => void;
7
- /**
8
- * Представляет CSS-селектор элемента, относительно которого нужно позиционировать dropdown.
9
- */
10
- anchorSelector?: string;
11
- };
12
-
13
- const { variants, onActivateVariant, anchorSelector = "" }: Props = $props();
14
-
15
- let requestedOpen = $state(false);
16
- let activeIndex = $state(0);
17
- let anchorRect = $state<DOMRect | null>(null);
18
-
19
- const isOpen = $derived(requestedOpen && variants.length > 1);
20
-
21
- const safeActiveIndex = $derived(
22
- variants.length === 0
23
- ? 0
24
- : Math.min(Math.max(activeIndex, 0), variants.length - 1),
25
- );
26
-
27
- function readAnchorRect(): void {
28
- if (!anchorSelector) {
29
- anchorRect = null;
30
- return;
31
- }
32
- const el = document.querySelector(anchorSelector);
33
- anchorRect = el instanceof HTMLElement ? el.getBoundingClientRect() : null;
34
- }
35
-
36
- function close(): void {
37
- requestedOpen = false;
38
- }
39
-
40
- export function forceClose(): void {
41
- close();
42
- }
43
-
44
- export function open(): void {
45
- if (variants.length <= 1) return;
46
- requestedOpen = true;
47
- activeIndex = 0;
48
- readAnchorRect();
49
- }
50
-
51
- export function handleKeyDown(e: KeyboardEvent): boolean {
52
- if (!isOpen) return false;
53
-
54
- if (e.key === "Escape") {
55
- close();
56
- return true;
57
- }
58
-
59
- if (e.key === "ArrowDown") {
60
- if (variants.length <= 1) return true;
61
- activeIndex = Math.min(safeActiveIndex + 1, variants.length - 1);
62
- return true;
63
- }
64
-
65
- if (e.key === "ArrowUp") {
66
- if (variants.length <= 1) return true;
67
- activeIndex = Math.max(safeActiveIndex - 1, 0);
68
- return true;
69
- }
70
-
71
- if (e.key === "ArrowRight") {
72
- const node = variants[safeActiveIndex];
73
- if (!node) return true;
74
- onActivateVariant(node);
75
- close();
76
- return true;
77
- }
78
-
79
- return false;
80
- }
81
-
82
- function onWindowMouseDown(e: MouseEvent): void {
83
- if (!isOpen) return;
84
- const path = e.composedPath?.() ?? [];
85
- const clickedInside = path.some(
86
- (t) => t instanceof HTMLElement && t.dataset?.["variantDropdown"] === "1",
87
- );
88
- if (!clickedInside) close();
89
- }
90
-
91
- $effect(() => {
92
- if (!isOpen) return;
93
-
94
- readAnchorRect();
95
-
96
- const onScrollOrResize = () => readAnchorRect();
97
- window.addEventListener("scroll", onScrollOrResize, true);
98
- window.addEventListener("resize", onScrollOrResize, { passive: true });
99
- return () => {
100
- window.removeEventListener("scroll", onScrollOrResize, true);
101
- window.removeEventListener("resize", onScrollOrResize);
102
- };
103
- });
104
-
105
- const position = $derived(
106
- !isOpen || !anchorRect
107
- ? null
108
- : (() => {
109
- const gap = 8;
110
- const minW = 224; // ~14rem
111
- const viewportW = window.innerWidth;
112
- const viewportH = window.innerHeight;
113
-
114
- // Prefer to the right of anchor; fallback to left if overflowing.
115
- let left = anchorRect.right + gap;
116
- if (left + minW > viewportW - gap) {
117
- left = Math.max(gap, anchorRect.left - gap - minW);
118
- }
119
-
120
- // Align tops, but keep in viewport.
121
- let top = anchorRect.top;
122
- top = Math.min(Math.max(gap, top), viewportH - gap);
123
-
124
- return { left, top };
125
- })(),
126
- );
127
-
128
- function getMovePrefix(node: ChessTreeNode): string {
129
- const fm = node.data.fullMoves;
130
- if (!fm) return "";
131
- return node.data.ply % 2 === 1 ? `${fm}.` : `${fm - 1}...`;
132
- }
133
- </script>
134
-
135
- <svelte:window onmousedown={onWindowMouseDown} />
136
-
137
- {#if isOpen}
138
- <div
139
- data-variant-dropdown="1"
140
- class="z-50 min-w-[14rem] rounded-md border bg-white p-1 shadow-md outline-none"
141
- role="menu"
142
- aria-label="Варианты следующего хода"
143
- style={position
144
- ? `position: fixed; left: ${position.left}px; top: ${position.top}px;`
145
- : "position: fixed;"}
146
- >
147
- {#each variants as variant, index (variant.id)}
148
- <button
149
- type="button"
150
- data-variant-dropdown="1"
151
- class={{
152
- "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none": true,
153
- "bg-accent text-accent-foreground": index === safeActiveIndex,
154
- "hover:bg-accent hover:text-accent-foreground": true,
155
- }}
156
- onclick={(e) => {
157
- e.preventDefault();
158
- e.stopPropagation();
159
- onActivateVariant(variant);
160
- close();
161
- }}
162
- >
163
- <span class="flex min-w-0 flex-wrap items-center gap-x-1 gap-y-0.5">
164
- {#if getMovePrefix(variant)}
165
- <span>{getMovePrefix(variant)}</span>
166
- {/if}
167
- <span>{variant.data.san}</span>
168
- <NagBadges nags={variant.data.nags} class="!ml-0" />
169
- </span>
170
- </button>
171
- {/each}
172
- </div>
173
- {/if}
1
+ <script lang="ts">
2
+ import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
3
+ import type { PieceSet } from "@connectorvol/shared";
4
+ import NagBadges from "./NagBadges.svelte";
5
+ import MoveWithIcon from "./MoveWithIcon.svelte";
6
+ type Props = {
7
+ variants: ChessTreeNode[];
8
+ onActivateVariant: (node: ChessTreeNode) => void;
9
+ /**
10
+ * Представляет CSS-селектор элемента, относительно которого нужно позиционировать dropdown.
11
+ */
12
+ anchorSelector?: string;
13
+ /** Возвращает набор фигур для иконок SAN. */
14
+ pieceSet: PieceSet;
15
+ };
16
+
17
+ const { variants, onActivateVariant, anchorSelector = "", pieceSet }: Props = $props();
18
+
19
+ let requestedOpen = $state(false);
20
+ let activeIndex = $state(0);
21
+ let anchorRect = $state<DOMRect | null>(null);
22
+
23
+ const isOpen = $derived(requestedOpen && variants.length > 1);
24
+
25
+ const safeActiveIndex = $derived(
26
+ variants.length === 0
27
+ ? 0
28
+ : Math.min(Math.max(activeIndex, 0), variants.length - 1),
29
+ );
30
+
31
+ function readAnchorRect(): void {
32
+ if (!anchorSelector) {
33
+ anchorRect = null;
34
+ return;
35
+ }
36
+ const el = document.querySelector(anchorSelector);
37
+ anchorRect = el instanceof HTMLElement ? el.getBoundingClientRect() : null;
38
+ }
39
+
40
+ function close(): void {
41
+ requestedOpen = false;
42
+ }
43
+
44
+ export function forceClose(): void {
45
+ close();
46
+ }
47
+
48
+ export function open(): void {
49
+ if (variants.length <= 1) return;
50
+ requestedOpen = true;
51
+ activeIndex = 0;
52
+ readAnchorRect();
53
+ }
54
+
55
+ export function handleKeyDown(e: KeyboardEvent): boolean {
56
+ if (!isOpen) return false;
57
+
58
+ if (e.key === "Escape") {
59
+ close();
60
+ return true;
61
+ }
62
+
63
+ if (e.key === "ArrowDown") {
64
+ if (variants.length <= 1) return true;
65
+ activeIndex = Math.min(safeActiveIndex + 1, variants.length - 1);
66
+ return true;
67
+ }
68
+
69
+ if (e.key === "ArrowUp") {
70
+ if (variants.length <= 1) return true;
71
+ activeIndex = Math.max(safeActiveIndex - 1, 0);
72
+ return true;
73
+ }
74
+
75
+ if (e.key === "ArrowRight") {
76
+ const node = variants[safeActiveIndex];
77
+ if (!node) return true;
78
+ onActivateVariant(node);
79
+ close();
80
+ return true;
81
+ }
82
+
83
+ return false;
84
+ }
85
+
86
+ function onWindowMouseDown(e: MouseEvent): void {
87
+ if (!isOpen) return;
88
+ const path = e.composedPath?.() ?? [];
89
+ const clickedInside = path.some(
90
+ (t) => t instanceof HTMLElement && t.dataset?.["variantDropdown"] === "1",
91
+ );
92
+ if (!clickedInside) close();
93
+ }
94
+
95
+ $effect(() => {
96
+ if (!isOpen) return;
97
+
98
+ readAnchorRect();
99
+
100
+ const onScrollOrResize = () => readAnchorRect();
101
+ window.addEventListener("scroll", onScrollOrResize, true);
102
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
103
+ return () => {
104
+ window.removeEventListener("scroll", onScrollOrResize, true);
105
+ window.removeEventListener("resize", onScrollOrResize);
106
+ };
107
+ });
108
+
109
+ const position = $derived(
110
+ !isOpen || !anchorRect
111
+ ? null
112
+ : (() => {
113
+ const gap = 8;
114
+ const minW = 224; // ~14rem
115
+ const viewportW = window.innerWidth;
116
+ const viewportH = window.innerHeight;
117
+
118
+ // Prefer to the right of anchor; fallback to left if overflowing.
119
+ let left = anchorRect.right + gap;
120
+ if (left + minW > viewportW - gap) {
121
+ left = Math.max(gap, anchorRect.left - gap - minW);
122
+ }
123
+
124
+ // Align tops, but keep in viewport.
125
+ let top = anchorRect.top;
126
+ top = Math.min(Math.max(gap, top), viewportH - gap);
127
+
128
+ return { left, top };
129
+ })(),
130
+ );
131
+
132
+ function getMovePrefix(node: ChessTreeNode): string {
133
+ const fm = node.data.fullMoves;
134
+ if (!fm) return "";
135
+ return node.data.ply % 2 === 1 ? `${fm}.` : `${fm - 1}...`;
136
+ }
137
+ </script>
138
+
139
+ <svelte:window onmousedown={onWindowMouseDown} />
140
+
141
+ {#if isOpen}
142
+ <div
143
+ data-variant-dropdown="1"
144
+ class="z-50 min-w-[14rem] rounded-md border bg-white p-1 shadow-md outline-none"
145
+ role="menu"
146
+ aria-label="Варианты следующего хода"
147
+ style={position
148
+ ? `position: fixed; left: ${position.left}px; top: ${position.top}px;`
149
+ : "position: fixed;"}
150
+ >
151
+ {#each variants as variant, index (variant.id)}
152
+ <button
153
+ type="button"
154
+ data-variant-dropdown="1"
155
+ class={{
156
+ "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none": true,
157
+ "bg-accent text-accent-foreground": index === safeActiveIndex,
158
+ "hover:bg-accent hover:text-accent-foreground": true,
159
+ }}
160
+ onclick={(e) => {
161
+ e.preventDefault();
162
+ e.stopPropagation();
163
+ onActivateVariant(variant);
164
+ close();
165
+ }}
166
+ >
167
+ <span class="flex min-w-0 flex-wrap items-center gap-x-1 gap-y-0.5">
168
+ {#if getMovePrefix(variant)}
169
+ <span>{getMovePrefix(variant)}</span>
170
+ {/if}
171
+ <MoveWithIcon san={variant.data.san} ply={variant.data.ply} {pieceSet} />
172
+ <NagBadges nags={variant.data.nags} class="!ml-0" />
173
+ </span>
174
+ </button>
175
+ {/each}
176
+ </div>
177
+ {/if}
@@ -1,4 +1,5 @@
1
1
  import type { ChessTreeNode } from "../(models)/chessTreeNode.js";
2
+ import type { PieceSet } from "@connectorvol/shared";
2
3
  type Props = {
3
4
  variants: ChessTreeNode[];
4
5
  onActivateVariant: (node: ChessTreeNode) => void;
@@ -6,6 +7,8 @@ type Props = {
6
7
  * Представляет CSS-селектор элемента, относительно которого нужно позиционировать dropdown.
7
8
  */
8
9
  anchorSelector?: string;
10
+ /** Возвращает набор фигур для иконок SAN. */
11
+ pieceSet: PieceSet;
9
12
  };
10
13
  declare const VariantDropdownNavigator: import("svelte").Component<Props, {
11
14
  forceClose: () => void;
@@ -17,6 +17,12 @@ export interface ParsedSanMove {
17
17
  isCheckmate: boolean;
18
18
  /** Возвращает координаты назначения (например, "f3", "e4") */
19
19
  destination?: string;
20
+ /** Возвращает уточнение (файл или ранг) при неоднозначности хода (например, "d" для Ndb5, "4" для N4b6) */
21
+ disambiguation?: string;
22
+ /** Возвращает тип фигуры превращения */
23
+ promotionPieceType?: PieceType;
24
+ /** Возвращает файл пешки при взятии (например, "e" для exd5) */
25
+ pawnFile?: string;
20
26
  }
21
27
  /**
22
28
  * Представляет функцию для парсинга SAN нотации в информацию о ходе