@umbraci/jsmind 0.10.15 → 0.10.17

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.
@@ -78,10 +78,6 @@ declare class jsMind {
78
78
  version: string;
79
79
  initialized: boolean;
80
80
  mind: Mind;
81
- /** @type {'single'|'multi'|null} */
82
- _selection_mode: "single" | "multi" | null;
83
- /** @type {import('./jsmind.node.js').Node|null} */
84
- _last_selected_node: import("./jsmind.node.js").Node | null;
85
81
  /** @type {Array<(type: number, data: EventData) => void>} */
86
82
  event_handles: Array<(type: number, data: EventData) => void>;
87
83
  /** Initialize sub-systems and plugins. */
@@ -336,24 +332,8 @@ declare class jsMind {
336
332
  * @returns {import('./jsmind.node.js').Node|null} Node instance or null
337
333
  */
338
334
  get_selected_node(): import("./jsmind.node.js").Node | null;
339
- /**
340
- * Get all currently selected nodes.
341
- * @returns {import('./jsmind.node.js').Node[]}
342
- */
343
- get_selected_nodes(): import("./jsmind.node.js").Node[];
344
335
  /** clear selection */
345
336
  select_clear(): void;
346
- /**
347
- * Toggle multi-selection for a node (and optionally descendants).
348
- * @param {string | import('./jsmind.node.js').Node} node
349
- */
350
- toggle_subtree_selection(node: string | import("./jsmind.node.js").Node): void;
351
- /**
352
- * Determine whether a node is currently selected.
353
- * @param {string | import('./jsmind.node.js').Node} node
354
- * @returns {boolean}
355
- */
356
- is_node_selected(node: string | import("./jsmind.node.js").Node): boolean;
357
337
  /** @param {string | import('./jsmind.node.js').Node} node */
358
338
  is_node_visible(node: string | import("./jsmind.node.js").Node): boolean;
359
339
  /**
@@ -361,180 +341,6 @@ declare class jsMind {
361
341
  * @param {string | import('./jsmind.node.js').Node} node
362
342
  */
363
343
  scroll_node_to_center(node: string | import("./jsmind.node.js").Node): void;
364
- /**
365
- * Add nodes into the current selection set without clearing existing ones.
366
- * @param {import('./jsmind.node.js').Node[]} nodes
367
- * @returns {import('./jsmind.node.js').Node[]}
368
- * @private
369
- */
370
- /**
371
- * @param {import('./jsmind.node.js').Node[]} nodes
372
- * @param {{focusNode?: import('./jsmind.node.js').Node}=} options
373
- * @private
374
- */
375
- private _append_selection;
376
- /**
377
- * Remove nodes from the current selection set.
378
- * @param {import('./jsmind.node.js').Node[]} nodes
379
- * @returns {import('./jsmind.node.js').Node[]}
380
- * @private
381
- */
382
- private _remove_selection;
383
- /**
384
- * Deselect a node and all its descendants from the current selection.
385
- * @param {string | import('./jsmind.node.js').Node} node
386
- * @private
387
- */
388
- private _deselect_subtree;
389
- /**
390
- * Clear all current selections and return the nodes that were cleared.
391
- * @returns {import('./jsmind.node.js').Node[]}
392
- * @private
393
- */
394
- private _clear_selection_state;
395
- /**
396
- * Collect a node and optionally its descendants respecting filters.
397
- * @param {import('./jsmind.node.js').Node} node
398
- * @param {{includeChildren?:boolean, respectFilter?:boolean, skipRootFilter?:boolean}=} config
399
- * @returns {import('./jsmind.node.js').Node[]}
400
- * @private
401
- */
402
- private _collect_subtree_nodes;
403
- /**
404
- * Ensure ancestors of provided nodes are also selected (up to first selected ancestor).
405
- * @param {import('./jsmind.node.js').Node[]} nodes
406
- * @param {import('./jsmind.node.js').Node=} focusNode
407
- * @returns {import('./jsmind.node.js').Node[]}
408
- * @private
409
- */
410
- /**
411
- * @param {import('./jsmind.node.js').Node[]} nodes
412
- * @param {import('./jsmind.node.js').Node=} focusNode
413
- * @param {{requireAncestorChainSelected?: boolean}=} options
414
- * @returns {import('./jsmind.node.js').Node[]}
415
- * @private
416
- */
417
- private _ensure_ancestor_selection;
418
- /**
419
- * Get the configured selection filter callback, if any.
420
- * @returns {((node: import('./jsmind.node.js').Node)=>boolean)|null}
421
- * @private
422
- */
423
- private _get_selection_filter;
424
- /**
425
- * Determine the multi-select mode based on event modifiers.
426
- * Returns: null (single select), 'ctrl' (add/remove), 'shift' (range select)
427
- * @param {MouseEvent} e
428
- * @returns {null|'ctrl'|'shift'}
429
- * @private
430
- */
431
- private _get_multi_select_mode;
432
- /**
433
- * Toggle selection of a single node (add if not selected, remove if selected).
434
- * Used for Ctrl+Click behavior.
435
- * @param {string | import('./jsmind.node.js').Node} node_id
436
- * @private
437
- */
438
- private _toggle_node_selection;
439
- /**
440
- * Range select nodes for Shift+Click behavior.
441
- * Logic:
442
- * - If no nodes are currently selected: select all nodes under the clicked node
443
- * - If nodes are already selected: select nodes in the range from first selected to clicked node
444
- * @param {string | import('./jsmind.node.js').Node} node_id
445
- * @private
446
- */
447
- private _range_select_nodes;
448
- /**
449
- * Find all nodes between two nodes (for range selection).
450
- * This includes both nodes and all nodes in between them in tree order.
451
- * @param {import('./jsmind.node.js').Node} node1
452
- * @param {import('./jsmind.node.js').Node} node2
453
- * @returns {import('./jsmind.node.js').Node[]}
454
- * @private
455
- */
456
- private _find_nodes_between;
457
- /**
458
- * Check whether ancestor is an ancestor of node.
459
- * @param {import('./jsmind.node.js').Node} ancestor
460
- * @param {import('./jsmind.node.js').Node} node
461
- * @returns {boolean}
462
- * @private
463
- */
464
- private _is_ancestor_of;
465
- /**
466
- * Return nodes along the ancestor->descendant chain, inclusive. If 'ancestor' is not actually
467
- * an ancestor of 'descendant', returns empty array.
468
- * @param {import('./jsmind.node.js').Node} ancestor
469
- * @param {import('./jsmind.node.js').Node} descendant
470
- * @returns {import('./jsmind.node.js').Node[]}
471
- * @private
472
- */
473
- private _get_path_nodes;
474
- /**
475
- * Find nearest selected ancestor of the given node from current selection set.
476
- * @param {import('./jsmind.node.js').Node} node
477
- * @returns {import('./jsmind.node.js').Node|null}
478
- * @private
479
- */
480
- private _find_nearest_selected_ancestor;
481
- /**
482
- * Find lowest common ancestor of two nodes.
483
- * @param {import('./jsmind.node.js').Node} a
484
- * @param {import('./jsmind.node.js').Node} b
485
- * @returns {import('./jsmind.node.js').Node|null}
486
- * @private
487
- */
488
- private _find_lca;
489
- /**
490
- * Given a lowest common ancestor 'lca' and a descendant 'node',
491
- * return the direct child of lca that lies on the path to node.
492
- * Returns null if node is not a descendant of lca or node === lca.
493
- * @param {import('./jsmind.node.js').Node} lca
494
- * @param {import('./jsmind.node.js').Node} node
495
- * @returns {import('./jsmind.node.js').Node|null}
496
- * @private
497
- */
498
- private _child_on_path;
499
- /**
500
- * From a list of nodes, remove those that are ancestors of any other node in the same list.
501
- * Keeps only the deepest nodes so that we don't auto-select parents implicitly.
502
- * @param {import('./jsmind.node.js').Node[]} nodes
503
- * @returns {import('./jsmind.node.js').Node[]}
504
- * @private
505
- */
506
- private _remove_ancestor_nodes;
507
- /**
508
- * From a list of nodes, remove those that are descendants of any other node in the same list.
509
- * Keeps only top-most nodes so that expanding subtrees covers full branches across siblings.
510
- * @param {import('./jsmind.node.js').Node[]} nodes
511
- * @returns {import('./jsmind.node.js').Node[]}
512
- * @private
513
- */
514
- private _remove_descendant_nodes;
515
- /**
516
- * Expand a set of base nodes with all their descendants (and themselves).
517
- * @param {import('./jsmind.node.js').Node[]} nodes
518
- * @param {{respectFilter?: boolean}=} opts
519
- * @returns {Set<import('./jsmind.node.js').Node>}
520
- * @private
521
- */
522
- private _expand_with_descendants;
523
- /**
524
- * Promote parents into selection ONLY when all their direct children are selected
525
- * AND at least one ancestor of that parent is already selected (in previous selection set).
526
- * This avoids auto-selecting parents when only siblings are selected.
527
- * @param {Set<import('./jsmind.node.js').Node>} set
528
- * @returns {Set<import('./jsmind.node.js').Node>}
529
- * @private
530
- */
531
- private _promote_parents_when_children_selected;
532
- /**
533
- * Determine selection mode based on current selection size.
534
- * @returns {'single'|'multi'|null}
535
- * @private
536
- */
537
- private _derive_selection_mode;
538
344
  /**
539
345
  * Find the previous sibling node of the given node.
540
346
  *
@@ -9,8 +9,6 @@ export class Mind {
9
9
  root: Node | null;
10
10
  /** @type {Node | null} */
11
11
  selected: Node | null;
12
- /** @type {Set<Node>} */
13
- selected_nodes: Set<Node>;
14
12
  /** @type {Record<string, Node>} */
15
13
  nodes: Record<string, Node>;
16
14
  /**
@@ -108,12 +106,6 @@ export class Mind {
108
106
  * @returns {boolean}
109
107
  */
110
108
  remove_node(node: Node): boolean;
111
- /**
112
- * Remove a node and its subtree from the cached selection set.
113
- * @param {Node} node
114
- * @private
115
- */
116
- private _purge_selection;
117
109
  /**
118
110
  * Put node into the map if id is not taken.
119
111
  * @param {Node} node
@@ -60,11 +60,6 @@ export type JsMindRuntimeOptions = {
60
60
  mapping?: Record<string, number | number[]>;
61
61
  id_generator?: () => string;
62
62
  };
63
- selection?: {
64
- enable_multi_select?: boolean;
65
- include_descendants?: boolean;
66
- filter?: (node: import("./jsmind.node.js").Node) => boolean;
67
- };
68
63
  fieldNames?: {
69
64
  id?: string;
70
65
  topic?: string;
@@ -71,8 +71,6 @@ export class ViewProvider {
71
71
  h: number;
72
72
  };
73
73
  selected_node: import("./jsmind.node.js").Node;
74
- /** @type {Map<string, import('./jsmind.node.js').Node>} */
75
- multi_selected_nodes: Map<string, import("./jsmind.node.js").Node>;
76
74
  editing_node: import("./jsmind.node.js").Node;
77
75
  graph: {
78
76
  view: ViewProvider;
@@ -255,19 +253,6 @@ export class ViewProvider {
255
253
  select_node(node: import("./jsmind.node.js").Node | null): void;
256
254
  /** Clear node selection. */
257
255
  select_clear(): void;
258
- /**
259
- * Append nodes to the current selection without clearing existing ones.
260
- * @param {import('./jsmind.node.js').Node[]} nodes
261
- * @param {import('./jsmind.node.js').Node=} focus_node
262
- */
263
- append_selected_nodes(nodes: import("./jsmind.node.js").Node[], focus_node?: import("./jsmind.node.js").Node | undefined): void;
264
- /**
265
- * Remove the provided nodes from selection state.
266
- * @param {import('./jsmind.node.js').Node[]} nodes
267
- */
268
- remove_selected_nodes(nodes: import("./jsmind.node.js").Node[]): void;
269
- /** Clear all selections at once. */
270
- clear_all_selected_nodes(): void;
271
256
  /**
272
257
  * Get currently editing node.
273
258
  * @returns {import('./jsmind.node.js').Node|null} Currently editing node
@@ -344,18 +329,6 @@ export class ViewProvider {
344
329
  restore_selected_node_custom_style(node: import("./jsmind.node.js").Node): void;
345
330
  /** @param {import('./jsmind.node.js').Node} node */
346
331
  clear_selected_node_custom_style(node: import("./jsmind.node.js").Node): void;
347
- /**
348
- * Mark the DOM/state for a selected node.
349
- * @param {import('./jsmind.node.js').Node} node
350
- * @private
351
- */
352
- private _mark_node_selected;
353
- /**
354
- * Remove DOM/state selection for a node.
355
- * @param {import('./jsmind.node.js').Node} node
356
- * @private
357
- */
358
- private _unmark_node_selected;
359
332
  clear_lines(): void;
360
333
  show_lines(): void;
361
334
  /**
@@ -23,6 +23,7 @@
23
23
  export function flatten(tree: NodeTreeFormat | NodeTreeData, opts?: FlattenOptions): Map<string, FlatNode>;
24
24
  /**
25
25
  * Compute diff between two snapshots.
26
+ * Always categorizes updates into moved/modified/movedAndModified using LIS algorithm for precise move detection.
26
27
  *
27
28
  * Note: When using custom fieldNames, make sure to pass the correct field names in opts.fields.
28
29
  * For example, if you configured fieldNames: { topic: 'name' }, you should pass fields: ['name', 'data', 'id'].
@@ -31,22 +32,26 @@ export function flatten(tree: NodeTreeFormat | NodeTreeData, opts?: FlattenOptio
31
32
  * @param {NodeTreeFormat|NodeTreeData} a - First snapshot (before)
32
33
  * @param {NodeTreeFormat|NodeTreeData} b - Second snapshot (after)
33
34
  * @param {DiffOptions} [opts] - Diff options
34
- * @returns {DiffResult} Diff result with created, updated, deleted nodes, and optionally categorized updates
35
+ * @returns {DiffResult} Diff result with created, deleted, moved, modified, and movedAndModified nodes
35
36
  *
36
37
  * @example
37
38
  * // Basic usage with default fieldNames
38
39
  * const result = diff(snapshot1, snapshot2);
39
40
  * console.log(result.created); // Newly created nodes
40
- * console.log(result.updated); // Updated nodes with changes
41
41
  * console.log(result.deleted); // Deleted nodes
42
- *
43
- * @example
44
- * // With categorization
45
- * const result = diff(snapshot1, snapshot2, { categorize: true });
46
- * console.log(result.moved); // Nodes that were only moved
42
+ * console.log(result.moved); // Nodes that were only moved (cross-parent or reordered)
47
43
  * console.log(result.modified); // Nodes that were only modified
48
44
  * console.log(result.movedAndModified); // Nodes that were both moved and modified
49
45
  *
46
+ * // Check move type
47
+ * result.moved.forEach(node => {
48
+ * if (node.moveInfo.moveType === 'cross-parent') {
49
+ * console.log(`${node.id} moved from ${node.moveInfo.fromParent} to ${node.moveInfo.toParent}`);
50
+ * } else if (node.moveInfo.moveType === 'reorder') {
51
+ * console.log(`${node.id} reordered from index ${node.moveInfo.fromOrder} to ${node.moveInfo.toOrder}`);
52
+ * }
53
+ * });
54
+ *
50
55
  * @example
51
56
  * // With custom fieldNames: { topic: 'name' }
52
57
  * const result = diff(snapshot1, snapshot2, { fields: ['name', 'data', 'id'] });
@@ -58,12 +63,7 @@ export function flatten(tree: NodeTreeFormat | NodeTreeData, opts?: FlattenOptio
58
63
  * // ... make changes ...
59
64
  * const after = jm.get_data('node_tree');
60
65
  * const result = jm.history.diff(before, after);
61
- * // fieldNames are automatically applied, no need to specify fields manually
62
- *
63
- * @example
64
- * // Ignore index changes caused by preceding node deletions
65
- * const result = jm.history.diff(before, after, { ignoreDeletionShift: true });
66
- * // Now nodes that only change index due to deletion of previous siblings won't be marked as moved
66
+ * // fieldNames are automatically applied, LIS algorithm is used for precise move detection
67
67
  */
68
68
  export function diff(a: NodeTreeFormat | NodeTreeData, b: NodeTreeFormat | NodeTreeData, opts?: DiffOptions): DiffResult;
69
69
  export type NodeTreeFormat = {
@@ -135,6 +135,10 @@ export type UpdatedNode = {
135
135
  changes: ChangeDetail[];
136
136
  };
137
137
  export type MoveInfo = {
138
+ /**
139
+ * - Type of move: 'cross-parent' for different parent, 'reorder' for same parent
140
+ */
141
+ moveType: "cross-parent" | "reorder";
138
142
  /**
139
143
  * - Whether parent changed
140
144
  */
@@ -223,10 +227,6 @@ export type DiffResult = {
223
227
  * - Newly created nodes
224
228
  */
225
229
  created: FlatNode[];
226
- /**
227
- * - Updated nodes (all changes)
228
- */
229
- updated: UpdatedNode[];
230
230
  /**
231
231
  * - Deleted nodes
232
232
  */
@@ -236,17 +236,17 @@ export type DiffResult = {
236
236
  */
237
237
  truncated: boolean;
238
238
  /**
239
- * - Nodes that were only moved (when categorize=true)
239
+ * - Nodes that were only moved
240
240
  */
241
- moved?: MovedNode[];
241
+ moved: MovedNode[];
242
242
  /**
243
- * - Nodes that were only modified (when categorize=true)
243
+ * - Nodes that were only modified
244
244
  */
245
- modified?: ModifiedNode[];
245
+ modified: ModifiedNode[];
246
246
  /**
247
- * - Nodes that were both moved and modified (when categorize=true)
247
+ * - Nodes that were both moved and modified
248
248
  */
249
- movedAndModified?: MovedAndModifiedNode[];
249
+ movedAndModified: MovedAndModifiedNode[];
250
250
  };
251
251
  export type FlattenOptions = {
252
252
  /**
@@ -273,21 +273,16 @@ export type FlattenOptions = {
273
273
  export type DiffOptions = {
274
274
  /**
275
275
  * - Array of field names to compare. Defaults to ['topic', 'data', 'id'].
276
- * When using custom fieldNames (e.g., { id: 'key', topic: 'name' }), this should be
277
- * ['name', 'data', 'key'] to match the actual field names in the data.
278
- * Note: When using jm.history.diff(), this is automatically handled based
279
- * on the configured fieldNames, so you don't need to specify it manually.
276
+ * Note: When using jm.history.diff(), this is automatically handled.
280
277
  */
281
278
  fields?: string[];
282
279
  /**
283
280
  * - The field name to use as the node ID. Defaults to 'id'.
284
- * When using custom fieldNames (e.g., { id: 'key' }), this should be 'key'.
285
281
  * Note: When using jm.history.diff(), this is automatically handled.
286
282
  */
287
283
  idKey?: string;
288
284
  /**
289
285
  * - The field name to use for children array. Defaults to 'children'.
290
- * When using custom fieldNames (e.g., { children: 'items' }), this should be 'items'.
291
286
  * Note: When using jm.history.diff(), this is automatically handled.
292
287
  */
293
288
  childrenKey?: string;
@@ -299,12 +294,4 @@ export type DiffOptions = {
299
294
  * - Maximum number of diff results. Defaults to 5000
300
295
  */
301
296
  maxSize?: number;
302
- /**
303
- * - Whether to categorize updates into moved/modified/movedAndModified. Defaults to false
304
- */
305
- categorize?: boolean;
306
- /**
307
- * - Whether to ignore index changes caused by preceding node deletions. When true, nodes that only change index due to deletion of previous siblings won't be marked as moved. Defaults to false
308
- */
309
- ignoreDeletionShift?: boolean;
310
297
  };
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Multi-Select Plugin - Enhanced plugin for jsMind
3
+ */
4
+ export class MultiSelectPlugin extends EnhancedPlugin {
5
+ /**
6
+ * @param {{ jm: import('../jsmind.js').default, pluginOpt: object }} params
7
+ */
8
+ constructor({ jm, pluginOpt }: {
9
+ jm: import("../jsmind.js").default;
10
+ pluginOpt: object;
11
+ });
12
+ options: any;
13
+ _mounted: boolean;
14
+ _core: MultiSelectCore;
15
+ _listener: (type: any, data: any) => void;
16
+ _enabled: boolean;
17
+ /**
18
+ * Initialize core and mount API
19
+ */
20
+ _initCore(): void;
21
+ _original_select_node: any;
22
+ _original_select_clear: any;
23
+ _domClickHandler: (e: any) => void;
24
+ /**
25
+ * Mount API to jsMind instance
26
+ */
27
+ _mountAPI(): void;
28
+ setEnabled(flag: any): void;
29
+ setOptions(partial: any): void;
30
+ }
31
+ export default MultiSelectPlugin;
32
+ /**
33
+ * Default options for multi-select plugin.
34
+ */
35
+ export type MultiSelectOptions = {
36
+ /**
37
+ * - Enable multi-select feature
38
+ */
39
+ enable_multi_select?: boolean;
40
+ /**
41
+ * - Include descendants in subtree selection
42
+ */
43
+ include_descendants?: boolean;
44
+ /**
45
+ * - Shift mode: false=simple subtree, true=advanced range
46
+ */
47
+ shift_simple_mode?: boolean;
48
+ /**
49
+ * - Node filter function
50
+ */
51
+ filter?: ((node: import("../jsmind.node.js").Node) => boolean) | null;
52
+ };
53
+ import { EnhancedPlugin } from '../jsmind.enhanced-plugin.js';
54
+ /**
55
+ * Multi-Select Core - Handles all multi-select logic
56
+ */
57
+ export class MultiSelectCore {
58
+ /**
59
+ * @param {import('../jsmind.js').default} jm - jsMind instance
60
+ * @param {MultiSelectOptions} options - Plugin options
61
+ */
62
+ constructor(jm: import("../jsmind.js").default, options: MultiSelectOptions);
63
+ jm: import("../jsmind.js").default;
64
+ options: MultiSelectOptions;
65
+ _selection_mode: string;
66
+ _last_selected_node: import("../jsmind.node.js").Node;
67
+ /**
68
+ * Ensure selection state is initialized
69
+ * @private
70
+ */
71
+ private _ensure_selection_state;
72
+ /**
73
+ * Get all selected nodes
74
+ * @returns {string[]} Array of selected node IDs
75
+ */
76
+ get_selected_nodes(): string[];
77
+ /**
78
+ * Check if a node is selected
79
+ * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance
80
+ * @returns {boolean}
81
+ */
82
+ is_node_selected(node: string | import("../jsmind.node.js").Node): boolean;
83
+ /**
84
+ * Select a single node (clears other selections)
85
+ * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance
86
+ */
87
+ select_node(node: string | import("../jsmind.node.js").Node): void;
88
+ /**
89
+ * Clear all selections
90
+ */
91
+ select_clear(): void;
92
+ /**
93
+ * Toggle node selection (equivalent to Ctrl/Cmd+Click)
94
+ * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance
95
+ */
96
+ toggle_node_selection(node: string | import("../jsmind.node.js").Node): void;
97
+ /**
98
+ * Toggle subtree selection
99
+ * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance
100
+ */
101
+ toggle_subtree_selection(node: string | import("../jsmind.node.js").Node, opts: any): void;
102
+ /**
103
+ * Get current selection mode
104
+ * @returns {'single'|'multi'|null}
105
+ */
106
+ get_selection_mode(): "single" | "multi" | null;
107
+ /**
108
+ * Handle node click event
109
+ * @param {Object} payload - Click event payload
110
+ * @param {MouseEvent} payload.e - Mouse event
111
+ * @param {string} payload.node - Node ID
112
+ * @param {HTMLElement} payload.element - Node element
113
+ * @param {string} payload.evt - Event type
114
+ */
115
+ _handle_node_click(payload: {
116
+ e: MouseEvent;
117
+ node: string;
118
+ element: HTMLElement;
119
+ evt: string;
120
+ }): void;
121
+ /**
122
+ * Handle node removed event
123
+ * @param {import('../jsmind.node.js').Node} node - Removed node
124
+ */
125
+ _handle_node_removed(node: import("../jsmind.node.js").Node): void;
126
+ /**
127
+ * Get multi-select mode from event
128
+ * @param {MouseEvent} e - Mouse event
129
+ * @returns {'ctrl'|'shift'|null}
130
+ */
131
+ _get_multi_select_mode(e: MouseEvent): "ctrl" | "shift" | null;
132
+ /**
133
+ * Append nodes to selection
134
+ * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to select
135
+ * @param {Object} [options] - Options
136
+ * @param {import('../jsmind.node.js').Node} [options.focusNode] - Focus node
137
+ * @returns {import('../jsmind.node.js').Node[]} Actually added nodes
138
+ */
139
+ _append_selection(nodes: import("../jsmind.node.js").Node[], options?: {
140
+ focusNode?: import("../jsmind.node.js").Node;
141
+ }): import("../jsmind.node.js").Node[];
142
+ /**
143
+ * Remove nodes from selection
144
+ * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to deselect
145
+ * @returns {import('../jsmind.node.js').Node[]} Actually removed nodes
146
+ */
147
+ _remove_selection(nodes: import("../jsmind.node.js").Node[]): import("../jsmind.node.js").Node[];
148
+ /**
149
+ * Deselect subtree
150
+ * @param {import('../jsmind.node.js').Node} node - Root node of subtree
151
+ */
152
+ _deselect_subtree(node: import("../jsmind.node.js").Node): void;
153
+ /**
154
+ * Clear selection state
155
+ * @returns {import('../jsmind.node.js').Node[]} Removed nodes
156
+ */
157
+ _clear_selection_state(): import("../jsmind.node.js").Node[];
158
+ /**
159
+ * Collect subtree nodes
160
+ * @param {import('../jsmind.node.js').Node} node - Root node
161
+ * @param {Object} options - Options
162
+ * @param {boolean} [options.includeChildren=true] - Include children
163
+ * @param {boolean} [options.respectFilter=false] - Respect filter
164
+ * @param {boolean} [options.skipRootFilter=false] - Skip root filter
165
+ * @returns {import('../jsmind.node.js').Node[]}
166
+ */
167
+ _collect_subtree_nodes(node: import("../jsmind.node.js").Node, options: {
168
+ includeChildren?: boolean;
169
+ respectFilter?: boolean;
170
+ skipRootFilter?: boolean;
171
+ }): import("../jsmind.node.js").Node[];
172
+ /**
173
+ * Ensure ancestor selection
174
+ * @param {import('../jsmind.node.js').Node[]} nodes - Nodes
175
+ * @param {import('../jsmind.node.js').Node} focusNode - Focus node
176
+ * @param {Object} options - Options
177
+ * @returns {import('../jsmind.node.js').Node[]}
178
+ */
179
+ _ensure_ancestor_selection(nodes: import("../jsmind.node.js").Node[], focusNode: import("../jsmind.node.js").Node, options: any): import("../jsmind.node.js").Node[];
180
+ /**
181
+ * Get selection filter
182
+ * @returns {Function|null}
183
+ */
184
+ _get_selection_filter(): Function | null;
185
+ /**
186
+ * Range select nodes (advanced mode)
187
+ * @param {string} nodeId - Target node ID
188
+ */
189
+ _range_select_nodes(nodeId: string): void;
190
+ /**
191
+ * Find nodes between two nodes
192
+ * @param {import('../jsmind.node.js').Node} from - Start node
193
+ * @param {import('../jsmind.node.js').Node} to - End node
194
+ * @returns {import('../jsmind.node.js').Node[]}
195
+ */
196
+ _find_nodes_between(from: import("../jsmind.node.js").Node, to: import("../jsmind.node.js").Node): import("../jsmind.node.js").Node[];
197
+ /**
198
+ * Derive selection mode from current state
199
+ * @returns {'single'|'multi'|null}
200
+ */
201
+ _derive_selection_mode(): "single" | "multi" | null;
202
+ /**
203
+ * Mark nodes as selected in view
204
+ * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to mark
205
+ * @param {import('../jsmind.node.js').Node} focusNode - Focus node
206
+ */
207
+ _mark_nodes_selected(nodes: import("../jsmind.node.js").Node[], focusNode: import("../jsmind.node.js").Node): void;
208
+ /**
209
+ * Mark a node as selected in view
210
+ * @param {import('../jsmind.node.js').Node} node - Node to mark
211
+ */
212
+ _mark_node_selected(node: import("../jsmind.node.js").Node): void;
213
+ /**
214
+ * Unmark nodes as selected in view
215
+ * @param {import('../jsmind.node.js').Node[]} nodes - Nodes to unmark
216
+ */
217
+ _unmark_nodes_selected(nodes: import("../jsmind.node.js").Node[]): void;
218
+ /**
219
+ * Unmark a node as selected in view
220
+ * @param {import('../jsmind.node.js').Node} node - Node to unmark
221
+ */
222
+ _unmark_node_selected(node: import("../jsmind.node.js").Node): void;
223
+ /**
224
+ * Clear all selected nodes view
225
+ */
226
+ _clear_all_selected_nodes_view(): void;
227
+ /**
228
+ * Invoke select event
229
+ * @param {Object} data - Event data
230
+ */
231
+ _invoke_select_event(data: any): void;
232
+ /**
233
+ * Resolve node from ID or Node instance
234
+ * @param {string|import('../jsmind.node.js').Node} node - Node ID or Node instance
235
+ * @returns {import('../jsmind.node.js').Node|null}
236
+ */
237
+ _resolve_node(node: string | import("../jsmind.node.js").Node): import("../jsmind.node.js").Node | null;
238
+ }