@flowscape-ui/core-sdk 1.0.1 → 1.0.3

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/index.d.ts CHANGED
@@ -1,39 +1,121 @@
1
1
  import Konva from 'konva';
2
+ export { default as Konva } from 'konva';
2
3
  import * as konva_lib_types from 'konva/lib/types';
3
4
 
4
- interface BaseNodeOptions {
5
- id?: string;
6
- x?: number;
7
- y?: number;
8
- width?: number;
9
- height?: number;
5
+ type Listener<TArgs extends unknown[]> = (...args: TArgs) => void;
6
+ declare class EventBus<TEvents extends {
7
+ [K in keyof TEvents]: unknown[];
8
+ } = Record<string, unknown[]>> {
9
+ private _listeners;
10
+ constructor();
11
+ get listeners(): Map<string, Listener<TEvents[keyof TEvents & string]>[]>;
12
+ on<K extends keyof TEvents & string>(event: K, callback: Listener<TEvents[K]>): void;
13
+ off<K extends keyof TEvents & string>(event: K, callback: Listener<TEvents[K]>): void;
14
+ once<K extends keyof TEvents & string>(event: K, callback: Listener<TEvents[K]>): void;
15
+ emit<K extends keyof TEvents & string>(event: K, ...args: TEvents[K]): void;
10
16
  }
11
- declare abstract class BaseNode<T extends Konva.Node = Konva.Node> {
12
- protected konvaNode: T;
17
+
18
+ /**
19
+ * Base class for a node addon.
20
+ *
21
+ * Usage:
22
+ * class MyAddon extends NodeAddon<TextNode> {
23
+ * protected onAttach(node: TextNode) { ... }
24
+ * protected onDetach(node: TextNode) { ... }
25
+ * }
26
+ */
27
+ declare abstract class NodeAddon<TNode extends BaseNode = BaseNode> {
28
+ protected abstract onAttach(node: TNode): void;
29
+ protected abstract onDetach(node: TNode): void;
30
+ /** Internal helper: called by the node's addon manager */
31
+ attach(node: TNode): void;
32
+ /** Internal helper: called by the node's addon manager */
33
+ detach(node: TNode): void;
34
+ }
35
+
36
+ /**
37
+ * Addon manager for a specific node.
38
+ * Allows adding/removing addons with a convenient API:
39
+ * node.addons.add(addon)
40
+ * node.addons.add([a, b])
41
+ * node.addons.remove(addon)
42
+ * node.addons.list()
43
+ */
44
+ declare class NodeAddons<TNode extends BaseNode = BaseNode> {
45
+ private readonly _node;
46
+ private readonly _addons;
47
+ constructor(node: TNode);
48
+ /** Attach one or more addons to the node */
49
+ add(addons: NodeAddon<TNode> | NodeAddon<TNode>[]): TNode;
50
+ /** Detach one or more addons from the node */
51
+ remove(addons: NodeAddon<TNode> | NodeAddon<TNode>[]): TNode;
52
+ /** All attached addons (array copy) */
53
+ list(): NodeAddon<TNode>[];
54
+ /** Check if a specific addon is attached */
55
+ has(addon: NodeAddon<TNode>): boolean;
56
+ /** Detach and clear all addons (used when removing the node) */
57
+ clear(): void;
58
+ }
59
+
60
+ type KonvaNode = Konva.Node;
61
+ type KonvaShape = Konva.Shape;
62
+ type KonvaGroup = Konva.Group;
63
+ type KonvaLayer = Konva.Layer;
64
+ type KonvaStage = Konva.Stage;
65
+ type KonvaText = Konva.Text;
66
+ type KonvaImage = Konva.Image;
67
+ type KonvaCircle = Konva.Circle;
68
+ type KonvaEllipse = Konva.Ellipse;
69
+ type KonvaArc = Konva.Arc;
70
+ type KonvaArrow = Konva.Arrow;
71
+ type KonvaStar = Konva.Star;
72
+ type KonvaRing = Konva.Ring;
73
+ type KonvaRegularPolygon = Konva.RegularPolygon;
74
+ type KonvaNodeConfig = Konva.NodeConfig;
75
+ type KonvaGroupConfig = Konva.GroupConfig;
76
+
77
+ /**
78
+ * Public interface for working with node addons.
79
+ * Hides the internal implementation of NodeAddons<BaseNode>.
80
+ */
81
+ interface NodeAddonsHandle {
82
+ add(addons: NodeAddon | NodeAddon[]): unknown;
83
+ remove(addons: NodeAddon | NodeAddon[]): unknown;
84
+ list(): NodeAddon[];
85
+ has(addon: NodeAddon): boolean;
86
+ clear(): void;
87
+ }
88
+ /**
89
+ * Base interface for public Node Handles.
90
+ * Concrete implementations will proxy BaseNode methods.
91
+ */
92
+ interface NodeHandle<TKonva extends KonvaNode = KonvaNode> {
13
93
  readonly id: string;
14
- constructor(node: T, options?: BaseNodeOptions);
15
- getNode(): T;
16
- setPosition({ x, y }: {
94
+ readonly addons: NodeAddonsHandle;
95
+ getPosition(): {
17
96
  x: number;
18
97
  y: number;
19
- }): void;
20
- getPosition(): konva_lib_types.Vector2d;
98
+ };
99
+ setPosition(position: {
100
+ x: number;
101
+ y: number;
102
+ }): this;
21
103
  remove(): void;
104
+ /**
105
+ * Safe access to the low-level Konva object.
106
+ * Returns the object itself without the need to import Konva from an external dependency.
107
+ */
108
+ getKonvaNode(): TKonva;
22
109
  }
23
-
24
- interface ShapeNodeOptions extends BaseNodeOptions {
25
- fill?: string;
26
- stroke?: string;
27
- strokeWidth?: number;
28
- cornerRadius?: number | number[];
29
- }
30
- declare class ShapeNode extends BaseNode<Konva.Rect> {
31
- constructor(options: ShapeNodeOptions);
110
+ /**
111
+ * Handle for ShapeNode (rectangle with rounded corners)
112
+ */
113
+ interface ShapeNodeHandle extends NodeHandle {
32
114
  setFill(color: string): this;
33
115
  setStroke(color: string): this;
34
116
  setStrokeWidth(width: number): this;
35
117
  setCornerRadius(radius: number | number[]): this;
36
- setSize({ width, height }: {
118
+ setSize(size: {
37
119
  width: number;
38
120
  height: number;
39
121
  }): this;
@@ -42,34 +124,183 @@ declare class ShapeNode extends BaseNode<Konva.Rect> {
42
124
  getStrokeWidth(): number;
43
125
  getCornerRadius(): number;
44
126
  }
127
+ /**
128
+ * Handle for TextNode
129
+ */
130
+ interface TextNodeHandle extends NodeHandle {
131
+ getText(): string;
132
+ setText(text: string): this;
133
+ setFontSize(size: number): this;
134
+ setFontFamily(family: string): this;
135
+ setFill(color: string): this;
136
+ setEditable(editable: boolean): this;
137
+ isEditable(): boolean;
138
+ onTextChange(callback: (event: {
139
+ oldText: string;
140
+ newText: string;
141
+ cancelled: boolean;
142
+ }) => void): this;
143
+ onEditStart(callback: () => void): this;
144
+ onEditEnd(callback: () => void): this;
145
+ }
146
+ /**
147
+ * Handle for ImageNode
148
+ */
149
+ interface ImageNodeHandle extends NodeHandle {
150
+ setSrc(url: string, crossOrigin?: '' | 'anonymous' | 'use-credentials'): Promise<this>;
151
+ setImage(image: HTMLImageElement): this;
152
+ setSize(size: {
153
+ width: number;
154
+ height: number;
155
+ }): this;
156
+ setCornerRadius(radius: number | number[]): this;
157
+ getSize(): {
158
+ width: number;
159
+ height: number;
160
+ };
161
+ getCornerRadius(): number;
162
+ }
163
+ /**
164
+ * Handle for CircleNode
165
+ */
166
+ interface CircleNodeHandle extends NodeHandle {
167
+ setRadius(radius: number): this;
168
+ setFill(color: string): this;
169
+ setStroke(color: string): this;
170
+ setStrokeWidth(width: number): this;
171
+ getRadius(): number;
172
+ getFill(): string | undefined;
173
+ getStroke(): string | undefined;
174
+ }
175
+ /**
176
+ * Handle for EllipseNode
177
+ */
178
+ interface EllipseNodeHandle extends NodeHandle {
179
+ setRadiusX(radiusX: number): this;
180
+ setRadiusY(radiusY: number): this;
181
+ setFill(color: string): this;
182
+ setStroke(color: string): this;
183
+ setStrokeWidth(width: number): this;
184
+ getRadiusX(): number;
185
+ getRadiusY(): number;
186
+ }
187
+ /**
188
+ * Handle for ArcNode
189
+ */
190
+ interface ArcNodeHandle extends NodeHandle {
191
+ setInnerRadius(radius: number): this;
192
+ setOuterRadius(radius: number): this;
193
+ setAngle(angle: number): this;
194
+ setFill(color: string): this;
195
+ setStroke(color: string): this;
196
+ setStrokeWidth(width: number): this;
197
+ }
198
+ /**
199
+ * Handle for ArrowNode
200
+ */
201
+ interface ArrowNodeHandle extends NodeHandle {
202
+ setPoints(points: number[]): this;
203
+ setPointerLength(length: number): this;
204
+ setPointerWidth(width: number): this;
205
+ setFill(color: string): this;
206
+ setStroke(color: string): this;
207
+ setStrokeWidth(width: number): this;
208
+ getPoints(): number[];
209
+ }
210
+ /**
211
+ * Handle for StarNode
212
+ */
213
+ interface StarNodeHandle extends NodeHandle {
214
+ setNumPoints(points: number): this;
215
+ setInnerRadius(radius: number): this;
216
+ setOuterRadius(radius: number): this;
217
+ setFill(color: string): this;
218
+ setStroke(color: string): this;
219
+ setStrokeWidth(width: number): this;
220
+ }
221
+ /**
222
+ * Handle for RingNode
223
+ */
224
+ interface RingNodeHandle extends NodeHandle {
225
+ setInnerRadius(radius: number): this;
226
+ setOuterRadius(radius: number): this;
227
+ setFill(color: string): this;
228
+ setStroke(color: string): this;
229
+ setStrokeWidth(width: number): this;
230
+ }
231
+ /**
232
+ * Handle for RegularPolygonNode
233
+ */
234
+ interface RegularPolygonNodeHandle extends NodeHandle {
235
+ setSides(sides: number): this;
236
+ setRadius(radius: number): this;
237
+ setFill(color: string): this;
238
+ setStroke(color: string): this;
239
+ setStrokeWidth(width: number): this;
240
+ getRadius(): number;
241
+ getSides(): number;
242
+ }
243
+ /**
244
+ * Handle for GroupNode
245
+ */
246
+ interface GroupNodeHandle extends NodeHandle {
247
+ addChild(child: KonvaNode | NodeHandle): this;
248
+ removeChild(child: KonvaNode | NodeHandle): this;
249
+ removeAllChildren(): this;
250
+ getChildren(): KonvaNode[];
251
+ findByName(name: string): KonvaNode[];
252
+ setDraggable(draggable: boolean): this;
253
+ isDraggable(): boolean;
254
+ setListening(listening: boolean): this;
255
+ isListening(): boolean;
256
+ setClip(rect: {
257
+ x: number;
258
+ y: number;
259
+ width: number;
260
+ height: number;
261
+ }): this;
262
+ }
45
263
 
46
- type Listener<TArgs extends unknown[]> = (...args: TArgs) => void;
47
- declare class EventBus<TEvents extends {
48
- [K in keyof TEvents]: unknown[];
49
- } = Record<string, unknown[]>> {
50
- private _listeners;
51
- constructor();
52
- get listeners(): Map<string, Listener<TEvents[keyof TEvents & string]>[]>;
53
- on<K extends keyof TEvents & string>(event: K, callback: Listener<TEvents[K]>): void;
54
- off<K extends keyof TEvents & string>(event: K, callback: Listener<TEvents[K]>): void;
55
- once<K extends keyof TEvents & string>(event: K, callback: Listener<TEvents[K]>): void;
56
- emit<K extends keyof TEvents & string>(event: K, ...args: TEvents[K]): void;
264
+ interface BaseNodeOptions {
265
+ id?: string;
266
+ x?: number;
267
+ y?: number;
268
+ width?: number;
269
+ height?: number;
270
+ }
271
+ declare abstract class BaseNode<T extends Konva.Node = Konva.Node> implements NodeHandle<T> {
272
+ protected konvaNode: T;
273
+ readonly id: string;
274
+ /** Local addons attached to this node */
275
+ readonly addons: NodeAddons<this>;
276
+ constructor(node: T, options?: BaseNodeOptions);
277
+ /**
278
+ * Public access to the low-level Konva object.
279
+ * Use this method instead of importing `konva` directly.
280
+ */
281
+ getKonvaNode(): T;
282
+ setPosition({ x, y }: {
283
+ x: number;
284
+ y: number;
285
+ }): this;
286
+ getPosition(): konva_lib_types.Vector2d;
287
+ remove(): void;
57
288
  }
58
289
 
59
290
  /**
60
- * Типизированные события CoreEngine
61
- * Все события строго типизированы для лучшего DX
291
+ * Typed CoreEngine events
292
+ * All events are strictly typed for better DX
62
293
  */
63
294
  interface CoreEvents {
64
- /** Нода была создана и добавлена в мир */
295
+ /** Node was created and added to the world */
65
296
  'node:created': [node: BaseNode];
66
- /** Нода была удалена из мира */
297
+ /** Node was removed from the world */
67
298
  'node:removed': [node: BaseNode];
68
- /** Нода была выделена */
299
+ /** Node was selected */
69
300
  'node:selected': [node: BaseNode];
70
- /** Выделение ноды было снято */
301
+ /** Node was deselected */
71
302
  'node:deselected': [node: BaseNode];
72
- /** Нода была изменена (position, size, rotation, etc.) */
303
+ /** Node was transformed (position, size, rotation, etc.) */
73
304
  'node:transformed': [
74
305
  node: BaseNode,
75
306
  changes: {
@@ -82,29 +313,29 @@ interface CoreEvents {
82
313
  scaleY?: number;
83
314
  }
84
315
  ];
85
- /** Z-index ноды был изменён */
316
+ /** Node z-index was changed */
86
317
  'node:zIndexChanged': [node: BaseNode, oldIndex: number, newIndex: number];
87
- /** Группа была создана */
318
+ /** Group was created */
88
319
  'group:created': [group: BaseNode, nodes: BaseNode[]];
89
- /** Группа была разгруппирована */
320
+ /** Group was ungrouped */
90
321
  'group:ungrouped': [group: BaseNode, nodes: BaseNode[]];
91
- /** Множественное выделение создано */
322
+ /** Multi-selection was created */
92
323
  'selection:multi:created': [nodes: BaseNode[]];
93
- /** Множественное выделение уничтожено */
324
+ /** Multi-selection was destroyed */
94
325
  'selection:multi:destroyed': [];
95
- /** Выделение полностью снято */
326
+ /** Selection was completely cleared */
96
327
  'selection:cleared': [];
97
- /** Ноды были скопированы в буфер обмена */
328
+ /** Nodes were copied to clipboard */
98
329
  'clipboard:copy': [nodes: BaseNode[]];
99
- /** Ноды были вырезаны в буфер обмена */
330
+ /** Nodes were cut to clipboard */
100
331
  'clipboard:cut': [nodes: BaseNode[]];
101
- /** Ноды были вставлены из буфера обмена */
332
+ /** Nodes were pasted from clipboard */
102
333
  'clipboard:paste': [nodes: BaseNode[]];
103
- /** Зум был изменён программно */
334
+ /** Zoom was changed programmatically */
104
335
  'camera:setZoom': [{
105
336
  scale: number;
106
337
  }];
107
- /** Зум был изменён пользователем (колесо мыши) */
338
+ /** Zoom was changed by user (mouse wheel) */
108
339
  'camera:zoom': [{
109
340
  scale: number;
110
341
  position: {
@@ -112,17 +343,17 @@ interface CoreEvents {
112
343
  y: number;
113
344
  };
114
345
  }];
115
- /** Камера была сброшена */
346
+ /** Camera was reset */
116
347
  'camera:reset': [];
117
- /** Шаг зума был изменён */
348
+ /** Zoom step was changed */
118
349
  'camera:zoomStep': [{
119
350
  zoomStep: number;
120
351
  }];
121
- /** Шаг панорамирования был изменён */
352
+ /** Pan step was changed */
122
353
  'camera:panStep': [{
123
354
  panStep: number;
124
355
  }];
125
- /** Камера была перемещена (панорамирование) */
356
+ /** Camera was moved (panning) */
126
357
  'camera:pan': [{
127
358
  dx: number;
128
359
  dy: number;
@@ -131,79 +362,74 @@ interface CoreEvents {
131
362
  y: number;
132
363
  };
133
364
  }];
134
- /** Плагин был добавлен */
365
+ /** Plugin was added */
135
366
  'plugin:added': [pluginName: string];
136
- /** Плагин был удалён */
367
+ /** Plugin was removed */
137
368
  'plugin:removed': [pluginName: string];
138
- /** Stage был изменён (resize) */
369
+ /** Stage was resized */
139
370
  'stage:resized': [{
140
371
  width: number;
141
372
  height: number;
142
373
  }];
143
374
  }
144
375
 
145
- interface TextNodeOptions extends BaseNodeOptions {
146
- text?: string;
147
- fontSize?: number;
148
- fontFamily?: string;
149
- fontStyle?: string;
150
- fill?: string;
151
- align?: 'left' | 'center' | 'right';
152
- padding?: number;
376
+ interface CameraManagerOptions {
377
+ stage: Konva.Stage;
378
+ eventBus: EventBus<CoreEvents>;
379
+ target?: Konva.Node;
380
+ initialScale?: number;
381
+ minScale?: number;
382
+ maxScale?: number;
383
+ draggable?: boolean;
384
+ zoomStep?: number;
385
+ panStep?: number;
153
386
  }
154
- declare class TextNode extends BaseNode<Konva.Text> {
155
- constructor(options?: TextNodeOptions);
156
- getText(): string;
157
- getFontSize(): number;
158
- getFontFamily(): string;
159
- getFontStyle(): string;
160
- getFill(): string | undefined;
161
- getAlign(): 'left' | 'center' | 'right';
162
- getPadding(): number;
163
- getSize(): {
164
- width: number;
165
- height: number;
166
- };
167
- setText(text: string): this;
168
- setFontSize(size: number): this;
169
- setFontFamily(family: string): this;
170
- setFontStyle(style: string): this;
171
- setFill(color: string): this;
172
- setAlign(align: 'left' | 'center' | 'right'): this;
173
- setPadding(padding: number): this;
174
- setSize({ width, height }: {
175
- width: number;
176
- height: number;
177
- }): this;
387
+ declare class CameraManager {
388
+ private _stage;
389
+ private _eventBus;
390
+ private _target;
391
+ private _scale;
392
+ private _minScale;
393
+ private _maxScale;
394
+ private _zoomStep;
395
+ private _panStep;
396
+ private _wheelScheduled;
397
+ private _pendingWheelEvent;
398
+ constructor(options: CameraManagerOptions);
399
+ private _initWheelZoom;
400
+ private _handleWheel;
401
+ get zoomStep(): number;
402
+ get panStep(): number;
403
+ setZoom(zoom: number): void;
404
+ zoomIn(step?: number): void;
405
+ zoomOut(step?: number): void;
406
+ reset(): void;
407
+ setDraggable(enabled: boolean): void;
408
+ setZoomStep(step: number): void;
409
+ setPanStep(step: number): void;
178
410
  }
179
411
 
180
- type ImageSource = HTMLImageElement;
181
- interface ImageNodeOptions extends BaseNodeOptions {
182
- image?: ImageSource;
183
- src?: string;
184
- width?: number;
185
- height?: number;
412
+ interface ArcNodeOptions extends BaseNodeOptions {
413
+ innerRadius?: number;
414
+ outerRadius?: number;
415
+ angle?: number;
416
+ rotation?: number;
417
+ clockwise?: boolean;
418
+ fill?: string;
419
+ stroke?: string;
420
+ strokeWidth?: number;
186
421
  }
187
- declare class ImageNode extends BaseNode<Konva.Image> {
188
- constructor(options?: ImageNodeOptions);
189
- getSize(): {
190
- width: number;
191
- height: number;
192
- };
193
- /**
194
- * Async loads image from URL and sets it to Konva.Image.
195
- * Returns this for chaining.
196
- */
197
- setSrc(url: string, crossOrigin?: '' | 'anonymous' | 'use-credentials' | undefined): Promise<this>;
198
- /**
199
- * Set already loaded image source (HTMLImageElement)
200
- */
201
- setImage(image: ImageSource): this;
202
- setSize({ width, height }: {
203
- width: number;
204
- height: number;
205
- }): this;
206
- private _loadHTMLImage;
422
+
423
+ interface ArrowNodeOptions extends BaseNodeOptions {
424
+ points?: number[];
425
+ tension?: number;
426
+ pointerLength?: number;
427
+ pointerWidth?: number;
428
+ pointerAtBeginning?: boolean;
429
+ pointerAtEnding?: boolean;
430
+ fill?: string;
431
+ stroke?: string;
432
+ strokeWidth?: number;
207
433
  }
208
434
 
209
435
  interface CircleNodeOptions extends BaseNodeOptions {
@@ -212,17 +438,6 @@ interface CircleNodeOptions extends BaseNodeOptions {
212
438
  stroke?: string;
213
439
  strokeWidth?: number;
214
440
  }
215
- declare class CircleNode extends BaseNode<Konva.Circle> {
216
- constructor(options?: CircleNodeOptions);
217
- getRadius(): number;
218
- getFill(): string | undefined;
219
- getStroke(): string | undefined;
220
- getStrokeWidth(): number;
221
- setRadius(radius: number): this;
222
- setFill(color: string): this;
223
- setStroke(color: string): this;
224
- setStrokeWidth(width: number): this;
225
- }
226
441
 
227
442
  interface EllipseNodeOptions extends BaseNodeOptions {
228
443
  radiusX?: number;
@@ -231,74 +446,47 @@ interface EllipseNodeOptions extends BaseNodeOptions {
231
446
  stroke?: string;
232
447
  strokeWidth?: number;
233
448
  }
234
- declare class EllipseNode extends BaseNode<Konva.Ellipse> {
235
- constructor(options?: EllipseNodeOptions);
236
- getRadiusX(): number;
237
- getRadiusY(): number;
238
- getFill(): string | undefined;
239
- getStroke(): string | undefined;
240
- getStrokeWidth(): number;
241
- setRadiusX(value: number): this;
242
- setRadiusY(value: number): this;
243
- setFill(color: string): this;
244
- setStroke(color: string): this;
245
- setStrokeWidth(width: number): this;
246
- }
247
449
 
248
- interface ArcNodeOptions extends BaseNodeOptions {
249
- innerRadius?: number;
250
- outerRadius?: number;
251
- angle?: number;
252
- rotation?: number;
253
- clockwise?: boolean;
254
- fill?: string;
255
- stroke?: string;
256
- strokeWidth?: number;
257
- }
258
- declare class ArcNode extends BaseNode<Konva.Arc> {
259
- constructor(options?: ArcNodeOptions);
260
- getInnerRadius(): number;
261
- getOuterRadius(): number;
262
- getAngle(): number;
263
- isClockwise(): boolean;
264
- setInnerRadius(v: number): this;
265
- setOuterRadius(v: number): this;
266
- setAngle(v: number): this;
267
- setRotationDeg(v: number): this;
268
- setClockwise(v: boolean): this;
269
- setFill(color: string): this;
270
- setStroke(color: string): this;
271
- setStrokeWidth(width: number): this;
450
+ interface GroupNodeOptions extends BaseNodeOptions {
451
+ draggable?: boolean;
452
+ listening?: boolean;
453
+ clip?: {
454
+ x: number;
455
+ y: number;
456
+ width: number;
457
+ height: number;
458
+ };
272
459
  }
273
460
 
274
- interface ArrowNodeOptions extends BaseNodeOptions {
275
- points?: number[];
276
- tension?: number;
277
- pointerLength?: number;
278
- pointerWidth?: number;
279
- pointerAtBeginning?: boolean;
280
- pointerAtEnding?: boolean;
281
- fill?: string;
282
- stroke?: string;
283
- strokeWidth?: number;
461
+ type ImageSource = HTMLImageElement;
462
+ interface ImageNodeOptions extends BaseNodeOptions {
463
+ image?: ImageSource;
464
+ src?: string;
465
+ width?: number;
466
+ height?: number;
284
467
  }
285
- declare class ArrowNode extends BaseNode<Konva.Arrow> {
286
- constructor(options?: ArrowNodeOptions);
287
- getPoints(): number[];
288
- getTension(): number;
289
- getPointerLength(): number;
290
- getPointerWidth(): number;
291
- getPointerAtBeginning(): boolean;
292
- getPointerAtEnding(): boolean;
293
- setPoints(v: number[]): this;
294
- setTension(v: number): this;
295
- setPointerLength(v: number): this;
296
- setPointerWidth(v: number): this;
297
- setPointerAtBeginning(v: boolean): this;
298
- setPointerAtEnding(v: boolean): this;
299
- setFill(color: string): this;
300
- setStroke(color: string): this;
301
- setStrokeWidth(width: number): this;
468
+ declare class ImageNode extends BaseNode<Konva.Image> {
469
+ constructor(options?: ImageNodeOptions);
470
+ getSize(): {
471
+ width: number;
472
+ height: number;
473
+ };
474
+ /**
475
+ * Async loads image from URL and sets it to Konva.Image.
476
+ * Returns this for chaining.
477
+ */
478
+ setSrc(url: string, crossOrigin?: '' | 'anonymous' | 'use-credentials' | undefined): Promise<this>;
479
+ /**
480
+ * Set already loaded image source (HTMLImageElement)
481
+ */
482
+ setImage(image: ImageSource): this;
483
+ setSize({ width, height }: {
484
+ width: number;
485
+ height: number;
486
+ }): this;
487
+ setCornerRadius(radius: number | number[]): this;
488
+ getCornerRadius(): number;
489
+ private _loadHTMLImage;
302
490
  }
303
491
 
304
492
  interface RegularPolygonNodeOptions extends BaseNodeOptions {
@@ -308,92 +496,125 @@ interface RegularPolygonNodeOptions extends BaseNodeOptions {
308
496
  stroke?: string;
309
497
  strokeWidth?: number;
310
498
  }
311
- declare class RegularPolygonNode extends BaseNode<Konva.RegularPolygon> {
312
- constructor(options?: RegularPolygonNodeOptions);
313
- getSides(): number;
314
- getRadius(): number;
315
- getFill(): string | undefined;
316
- getStroke(): string | undefined;
317
- getStrokeWidth(): number;
318
- setSides(v: number): this;
319
- setRadius(v: number): this;
320
- setFill(color: string): this;
321
- setStroke(color: string): this;
322
- setStrokeWidth(width: number): this;
323
- }
324
499
 
325
- interface StarNodeOptions extends BaseNodeOptions {
326
- numPoints?: number;
500
+ interface RingNodeOptions extends BaseNodeOptions {
327
501
  innerRadius?: number;
328
502
  outerRadius?: number;
329
503
  fill?: string;
330
504
  stroke?: string;
331
505
  strokeWidth?: number;
332
506
  }
333
- declare class StarNode extends BaseNode<Konva.Star> {
334
- constructor(options?: StarNodeOptions);
335
- getNumPoints(): number;
336
- getInnerRadius(): number;
337
- getOuterRadius(): number;
338
- getFill(): string | undefined;
339
- getStroke(): string | undefined;
340
- getStrokeWidth(): number;
341
- setNumPoints(v: number): this;
342
- setInnerRadius(v: number): this;
343
- setOuterRadius(v: number): this;
507
+
508
+ interface ShapeNodeOptions extends BaseNodeOptions {
509
+ fill?: string;
510
+ stroke?: string;
511
+ strokeWidth?: number;
512
+ cornerRadius?: number | number[];
513
+ }
514
+ declare class ShapeNode extends BaseNode<Konva.Rect> {
515
+ constructor(options: ShapeNodeOptions);
344
516
  setFill(color: string): this;
345
517
  setStroke(color: string): this;
346
518
  setStrokeWidth(width: number): this;
519
+ setCornerRadius(radius: number | number[]): this;
520
+ setSize({ width, height }: {
521
+ width: number;
522
+ height: number;
523
+ }): this;
524
+ getFill(): string | undefined;
525
+ getStroke(): string | undefined;
526
+ getStrokeWidth(): number;
527
+ getCornerRadius(): number;
347
528
  }
348
529
 
349
- interface RingNodeOptions extends BaseNodeOptions {
530
+ interface StarNodeOptions extends BaseNodeOptions {
531
+ numPoints?: number;
350
532
  innerRadius?: number;
351
533
  outerRadius?: number;
352
534
  fill?: string;
353
535
  stroke?: string;
354
536
  strokeWidth?: number;
355
537
  }
356
- declare class RingNode extends BaseNode<Konva.Ring> {
357
- constructor(options?: RingNodeOptions);
358
- getInnerRadius(): number;
359
- getOuterRadius(): number;
360
- getFill(): string | undefined;
361
- getStroke(): string | undefined;
362
- getStrokeWidth(): number;
363
- setInnerRadius(v: number): this;
364
- setOuterRadius(v: number): this;
365
- setFill(color: string): this;
366
- setStroke(color: string): this;
367
- setStrokeWidth(width: number): this;
368
- }
369
538
 
370
- interface GroupNodeOptions extends BaseNodeOptions {
371
- draggable?: boolean;
372
- listening?: boolean;
373
- clip?: {
374
- x: number;
375
- y: number;
376
- width: number;
377
- height: number;
378
- };
539
+ interface TextNodeOptions extends BaseNodeOptions {
540
+ text?: string;
541
+ fontSize?: number;
542
+ fontFamily?: string;
543
+ fontStyle?: string;
544
+ fill?: string;
545
+ align?: 'left' | 'center' | 'right';
546
+ verticalAlign?: 'top' | 'middle' | 'bottom';
547
+ padding?: number;
548
+ lineHeight?: number;
549
+ /** Включить редактирование по двойному клику (по умолчанию true) */
550
+ editable?: boolean;
379
551
  }
380
- declare class GroupNode extends BaseNode<Konva.Group> {
381
- constructor(options?: GroupNodeOptions);
382
- addChild(child: Konva.Node | BaseNode): this;
383
- removeChild(child: Konva.Node | BaseNode): this;
384
- removeAllChildren(): this;
385
- getChildren(): Konva.Node[];
386
- findByName(name: string): Konva.Node[];
387
- setDraggable(v: boolean): this;
388
- isDraggable(): boolean;
389
- setListening(v: boolean): this;
390
- isListening(): boolean;
391
- setClip(rect: {
392
- x: number;
393
- y: number;
552
+ /** Событие изменения текста */
553
+ interface TextChangeEvent {
554
+ /** Предыдущий текст */
555
+ oldText: string;
556
+ /** Новый текст */
557
+ newText: string;
558
+ /** Было ли изменение отменено (Escape) */
559
+ cancelled: boolean;
560
+ }
561
+ declare class TextNode extends BaseNode<Konva.Text> {
562
+ /** Включено ли редактирование по двойному клику */
563
+ private _editable;
564
+ /** Находится ли нода в режиме редактирования */
565
+ private _isEditing;
566
+ /** Текущий textarea элемент */
567
+ private _textarea;
568
+ /** Колбэки для событий редактирования */
569
+ private _onTextChangeCallbacks;
570
+ private _onEditStartCallbacks;
571
+ private _onEditEndCallbacks;
572
+ /** Флаг: ожидание повторного двойного клика для входа в редактирование внутри группы */
573
+ private _pendingGroupEditDblClick;
574
+ private _groupEditClickResetAttached;
575
+ constructor(options?: TextNodeOptions);
576
+ getText(): string;
577
+ setText(text: string): this;
578
+ setFontSize(size: number): this;
579
+ setFontFamily(family: string): this;
580
+ setFill(color: string): this;
581
+ setAlign(align: 'left' | 'center' | 'right'): this;
582
+ setPadding(padding: number): this;
583
+ setSize({ width, height }: {
394
584
  width: number;
395
585
  height: number;
396
586
  }): this;
587
+ setLineHeight(lineHeight: number): this;
588
+ setVerticalAlign(align: 'top' | 'middle' | 'bottom'): this;
589
+ isEditable(): boolean;
590
+ setEditable(editable: boolean): this;
591
+ isEditing(): boolean;
592
+ startEdit(): void;
593
+ finishEdit(): void;
594
+ cancelEdit(): void;
595
+ onTextChange(cb: (event: TextChangeEvent) => void): this;
596
+ offTextChange(cb: (event: TextChangeEvent) => void): this;
597
+ onEditStart(cb: () => void): this;
598
+ offEditStart(cb: () => void): this;
599
+ onEditEnd(cb: () => void): this;
600
+ offEditEnd(cb: () => void): this;
601
+ private _setupEditHandler;
602
+ /**
603
+ * При трансформации (resize) «запекаем» scaleX/scaleY в width/height,
604
+ * а затем сбрасываем scale обратно в 1.
605
+ *
606
+ * В итоге:
607
+ * - рамка может свободно менять ширину/высоту и по диагонали;
608
+ * - сам текст не растягивается, т.к. fontSize не меняется, а scale всегда 1.
609
+ */
610
+ private _setupTransformHandler;
611
+ private _oldText;
612
+ private _keyHandler;
613
+ private _clickHandler;
614
+ private _syncTextareaRafId;
615
+ private _syncTextareaPosition;
616
+ private _openTextarea;
617
+ private _saveAndClose;
397
618
  }
398
619
 
399
620
  declare class NodeManager {
@@ -410,17 +631,17 @@ declare class NodeManager {
410
631
  get world(): Konva.Group;
411
632
  get stage(): Konva.Stage;
412
633
  get eventBus(): EventBus<CoreEvents>;
413
- addShape(options: ShapeNodeOptions): ShapeNode;
414
- addText(options: TextNodeOptions): TextNode;
415
- addImage(options: ImageNodeOptions): ImageNode;
416
- addCircle(options: CircleNodeOptions): CircleNode;
417
- addEllipse(options: EllipseNodeOptions): EllipseNode;
418
- addArc(options: ArcNodeOptions): ArcNode;
419
- addStar(options: StarNodeOptions): StarNode;
420
- addArrow(options: ArrowNodeOptions): ArrowNode;
421
- addRing(options: RingNodeOptions): RingNode;
422
- addRegularPolygon(options: RegularPolygonNodeOptions): RegularPolygonNode;
423
- addGroup(options: GroupNodeOptions): GroupNode;
634
+ addShape(options: ShapeNodeOptions): ShapeNodeHandle;
635
+ addText(options: TextNodeOptions): TextNodeHandle;
636
+ addImage(options: ImageNodeOptions): ImageNodeHandle;
637
+ addCircle(options: CircleNodeOptions): CircleNodeHandle;
638
+ addEllipse(options: EllipseNodeOptions): EllipseNodeHandle;
639
+ addArc(options: ArcNodeOptions): ArcNodeHandle;
640
+ addStar(options: StarNodeOptions): StarNodeHandle;
641
+ addArrow(options: ArrowNodeOptions): ArrowNodeHandle;
642
+ addRing(options: RingNodeOptions): RingNodeHandle;
643
+ addRegularPolygon(options: RegularPolygonNodeOptions): RegularPolygonNodeHandle;
644
+ addGroup(options: GroupNodeOptions): GroupNodeHandle;
424
645
  remove(node: BaseNode): void;
425
646
  findById(id: string): BaseNode | undefined;
426
647
  list(): BaseNode[];
@@ -431,42 +652,6 @@ declare class NodeManager {
431
652
  private _scheduleBatchDraw;
432
653
  }
433
654
 
434
- interface CameraManagerOptions {
435
- stage: Konva.Stage;
436
- eventBus: EventBus<CoreEvents>;
437
- target?: Konva.Node;
438
- initialScale?: number;
439
- minScale?: number;
440
- maxScale?: number;
441
- draggable?: boolean;
442
- zoomStep?: number;
443
- panStep?: number;
444
- }
445
- declare class CameraManager {
446
- private _stage;
447
- private _eventBus;
448
- private _target;
449
- private _scale;
450
- private _minScale;
451
- private _maxScale;
452
- private _zoomStep;
453
- private _panStep;
454
- private _wheelScheduled;
455
- private _pendingWheelEvent;
456
- constructor(options: CameraManagerOptions);
457
- private _initWheelZoom;
458
- private _handleWheel;
459
- get zoomStep(): number;
460
- get panStep(): number;
461
- setZoom(zoom: number): void;
462
- zoomIn(step?: number): void;
463
- zoomOut(step?: number): void;
464
- reset(): void;
465
- setDraggable(enabled: boolean): void;
466
- setZoomStep(step: number): void;
467
- setPanStep(step: number): void;
468
- }
469
-
470
655
  interface LODLevel {
471
656
  minScale: number;
472
657
  maxScale: number;
@@ -670,7 +855,57 @@ declare class VirtualizationManager {
670
855
  destroy(): void;
671
856
  }
672
857
 
858
+ /**
859
+ * Base class for a plugin addon.
860
+ *
861
+ * Usage:
862
+ * class MyPluginAddon extends PluginAddon<MyPlugin> {
863
+ * protected onAttach(plugin: MyPlugin, core: CoreEngine) { ... }
864
+ * protected onDetach(plugin: MyPlugin, core: CoreEngine) { ... }
865
+ * }
866
+ */
867
+ declare abstract class PluginAddon<TPlugin extends Plugin = Plugin> {
868
+ protected abstract onAttach(plugin: TPlugin, core: CoreEngine): void;
869
+ protected abstract onDetach(plugin: TPlugin, core: CoreEngine): void;
870
+ /** Internal helper: called by the plugin's addon manager */
871
+ attach(plugin: TPlugin, core: CoreEngine): void;
872
+ /** Internal helper: called by the plugin's addon manager */
873
+ detach(plugin: TPlugin, core: CoreEngine): void;
874
+ }
875
+
876
+ /**
877
+ * Addon manager for a specific plugin.
878
+ * Allows adding/removing addons with a convenient API:
879
+ * plugin.addons.add(addon)
880
+ * plugin.addons.add([a, b])
881
+ * plugin.addons.remove(addon)
882
+ * plugin.addons.list()
883
+ */
884
+ declare class PluginAddons<TPlugin extends Plugin = Plugin> {
885
+ private readonly _plugin;
886
+ private readonly _addons;
887
+ private _core;
888
+ constructor(plugin: TPlugin);
889
+ /** Internal helper: called from Plugin.attach */
890
+ _attachAll(core: CoreEngine): void;
891
+ /** Internal helper: called from Plugin.detach */
892
+ _detachAll(core: CoreEngine): void;
893
+ /** Attach one or more addons to the plugin */
894
+ add(addons: PluginAddon<TPlugin> | PluginAddon<TPlugin>[]): TPlugin;
895
+ /** Detach one or more addons from the plugin */
896
+ remove(addons: PluginAddon<TPlugin> | PluginAddon<TPlugin>[]): TPlugin;
897
+ /** All attached addons (array copy) */
898
+ list(): PluginAddon<TPlugin>[];
899
+ /** Check if a specific addon is attached */
900
+ has(addon: PluginAddon<TPlugin>): boolean;
901
+ /** Detach and clear all addons (used when removing the plugin) */
902
+ clear(): void;
903
+ }
904
+
673
905
  declare abstract class Plugin {
906
+ /** Local addons attached to this plugin */
907
+ readonly addons: PluginAddons<this>;
908
+ constructor();
674
909
  protected abstract onAttach(core: CoreEngine): void;
675
910
  protected abstract onDetach(core: CoreEngine): void;
676
911
  attach(core: CoreEngine): void;
@@ -715,12 +950,25 @@ declare class CoreEngine {
715
950
  private _minScale;
716
951
  private _maxScale;
717
952
  private _gridLayer;
953
+ private _resizeObserver;
718
954
  readonly container: HTMLDivElement;
719
955
  readonly nodes: NodeManager;
720
956
  readonly camera: CameraManager;
721
957
  readonly virtualization: VirtualizationManager;
722
958
  readonly plugins: Plugins;
723
959
  constructor(options: CoreEngineOptions);
960
+ /**
961
+ * Setup automatic canvas resize when container size changes
962
+ */
963
+ private _setupAutoResize;
964
+ /**
965
+ * Handle container resize
966
+ */
967
+ private _handleResize;
968
+ /**
969
+ * Cleanup resources
970
+ */
971
+ destroy(): void;
724
972
  get eventBus(): EventBus<CoreEvents>;
725
973
  get stage(): Konva.Stage;
726
974
  get gridLayer(): Konva.Layer;
@@ -737,34 +985,146 @@ declare class CoreEngine {
737
985
  }): void;
738
986
  setBackgroundColor(color: string): void;
739
987
  setDraggable(draggable: boolean): void;
988
+ /**
989
+ * Enable or disable auto-resize
990
+ */
991
+ setAutoResize(enabled: boolean): void;
740
992
  }
741
993
 
742
- interface LogoOptions {
743
- src: string;
744
- width: number;
745
- height: number;
746
- opacity?: number;
994
+ /**
995
+ * HistoryAction — description of a single action in history
996
+ *
997
+ * Action types:
998
+ * - 'create' — node creation (before: null, after: SerializedNodeState)
999
+ * - 'remove' — node removal (before: SerializedNodeState, after: null)
1000
+ * - 'transform' — transformation (before/after: TransformState)
1001
+ * - 'zIndex' — z-index change (before/after: { zIndex: number })
1002
+ * - 'group' — grouping (before: { childIds, childStates }, after: SerializedNodeState)
1003
+ * - 'ungroup' — ungrouping (before: SerializedNodeState, after: { childStates })
1004
+ * - 'batch' — composite action (children: HistoryAction[])
1005
+ */
1006
+ interface HistoryAction {
1007
+ /** Action type: 'create', 'remove', 'transform', 'zIndex', 'group', 'ungroup', 'batch' */
1008
+ type: string;
1009
+ /** ID of the node this action relates to (empty string for batch) */
1010
+ nodeId: string;
1011
+ /** State BEFORE the action (null for create, not used for batch) */
1012
+ before: unknown;
1013
+ /** State AFTER the action (null for remove, not used for batch) */
1014
+ after: unknown;
1015
+ /** Action timestamp */
1016
+ timestamp: number;
1017
+ /** Child actions for batch */
1018
+ children?: HistoryAction[];
747
1019
  }
748
- declare class LogoPlugin extends Plugin {
749
- private _core?;
750
- private _layer?;
751
- private _image?;
752
- private _src;
753
- private _width;
754
- private _height;
755
- private _opacity;
756
- constructor(options: LogoOptions);
757
- protected onAttach(core: CoreEngine): void;
758
- protected onDetach(core: CoreEngine): void;
759
- setOpacity(opacity: number): void;
760
- setSize({ width, height }: {
761
- width: number;
762
- height: number;
763
- }): void;
764
- setSource(src: string): void;
765
- private _setImage;
766
- private _loadImageFromString;
767
- private _layout;
1020
+ /**
1021
+ * HistoryManager — action history manager for Undo/Redo
1022
+ *
1023
+ * Logic:
1024
+ * - push(action) adds an action to history
1025
+ * - If currentIndex < length-1, actions after currentIndex are removed first
1026
+ * - select(index) switches current index without removing history
1027
+ * - undo() returns current action and moves index backward
1028
+ * - redo() moves index forward and returns the action
1029
+ */
1030
+ declare class HistoryManager {
1031
+ private _actions;
1032
+ private _currentIndex;
1033
+ /**
1034
+ * Add an action to history.
1035
+ * If currentIndex < length-1, removes actions after currentIndex first.
1036
+ */
1037
+ push(action: HistoryAction): void;
1038
+ /**
1039
+ * Remove actions from fromIndex to the end of the array.
1040
+ */
1041
+ pop(fromIndex: number): void;
1042
+ /**
1043
+ * Switch current index (for history navigation).
1044
+ * Does not remove actions from history.
1045
+ */
1046
+ select(index: number): void;
1047
+ /**
1048
+ * Get current index in history.
1049
+ * -1 means we are "before" the first action (empty state).
1050
+ */
1051
+ getCurrentIndex(): number;
1052
+ /**
1053
+ * Number of actions in history.
1054
+ */
1055
+ get length(): number;
1056
+ /**
1057
+ * Get a copy of all actions array.
1058
+ */
1059
+ getActions(): HistoryAction[];
1060
+ /**
1061
+ * Can undo be performed (are there actions to roll back).
1062
+ */
1063
+ canUndo(): boolean;
1064
+ /**
1065
+ * Can redo be performed (are there actions to repeat).
1066
+ */
1067
+ canRedo(): boolean;
1068
+ /**
1069
+ * Perform undo: return current action and move index backward.
1070
+ * Returns the action to roll back (apply before).
1071
+ */
1072
+ undo(): HistoryAction | null;
1073
+ /**
1074
+ * Perform redo: move index forward and return the action.
1075
+ * Returns the action to repeat (apply after).
1076
+ */
1077
+ redo(): HistoryAction | null;
1078
+ /**
1079
+ * Clear all history.
1080
+ */
1081
+ clear(): void;
1082
+ /**
1083
+ * Get action by index (without changing currentIndex).
1084
+ */
1085
+ getAction(index: number): HistoryAction | null;
1086
+ /**
1087
+ * Debug output of history state.
1088
+ */
1089
+ private _debug;
1090
+ }
1091
+
1092
+ interface AreaSelectionPluginOptions {
1093
+ rectStroke?: string;
1094
+ rectFill?: string;
1095
+ rectStrokeWidth?: number;
1096
+ rectOpacity?: number;
1097
+ enableKeyboardShortcuts?: boolean;
1098
+ }
1099
+ /**
1100
+ * AreaSelectionPlugin
1101
+ * - Drag LKM over empty space draws selection rectangle (marquee) in screen coordinates
1102
+ * - All nodes whose client rectangles intersect the rectangle are temporarily grouped
1103
+ * - Click outside — temporary group is removed, nodes return to their original positions
1104
+ * - Ctrl+G — lock in permanent group (GroupNode through NodeManager)
1105
+ * - Ctrl+Shift+G — unlock selected permanent group
1106
+ */
1107
+ declare class AreaSelectionPlugin extends Plugin {
1108
+ private _core?;
1109
+ private _layer;
1110
+ private _rect;
1111
+ private _start;
1112
+ private _transformer;
1113
+ private _selecting;
1114
+ private _skipNextClick;
1115
+ private _lastPickedBaseNodes;
1116
+ private _options;
1117
+ constructor(options?: AreaSelectionPluginOptions);
1118
+ protected onAttach(core: CoreEngine): void;
1119
+ protected onDetach(core: CoreEngine): void;
1120
+ private _finalizeArea;
1121
+ private _clearSelection;
1122
+ private _getSelectionPlugin;
1123
+ private _rectsIntersect;
1124
+ private _findOwningGroupBaseNode;
1125
+ private _isAncestor;
1126
+ private _isPermanentGroupSelected;
1127
+ private _currentGroupNode;
768
1128
  }
769
1129
 
770
1130
  interface CameraHotkeysOptions {
@@ -802,155 +1162,6 @@ declare class CameraHotkeysPlugin extends Plugin {
802
1162
  private _pan;
803
1163
  }
804
1164
 
805
- interface MultiGroupControllerDeps {
806
- ensureTempMulti: (nodes: BaseNode[]) => void;
807
- destroyTempMulti: () => void;
808
- commitTempMultiToGroup: () => void;
809
- isActive: () => boolean;
810
- forceUpdate: () => void;
811
- onWorldChanged?: () => void;
812
- isInsideTempByTarget?: (target: Konva.Node) => boolean;
813
- }
814
- /**
815
- * MultiGroupController — thin controller encapsulating work with temporary multi-group.
816
- * Actual logic lives in passed dependencies (SelectionPlugin),
817
- * thanks to which we don't duplicate code for frames/overlays and behavior.
818
- */
819
- declare class MultiGroupController {
820
- private deps;
821
- constructor(deps: MultiGroupControllerDeps);
822
- ensure(nodes: BaseNode[]): void;
823
- destroy(): void;
824
- commitToPermanentGroup(): void;
825
- isActive(): boolean;
826
- forceUpdateOverlays(): void;
827
- onWorldChanged(): void;
828
- isInsideTempByTarget(target: Konva.Node): boolean;
829
- }
830
-
831
- interface SelectionPluginOptions {
832
- dragEnabled?: boolean;
833
- enableTransformer?: boolean;
834
- deselectOnEmptyClick?: boolean;
835
- selectablePredicate?: (node: Konva.Node) => boolean;
836
- autoPanEnabled?: boolean;
837
- autoPanEdgePx?: number;
838
- autoPanMaxSpeedPx?: number;
839
- }
840
- /**
841
- * Universal selection and dragging plugin for nodes compatible with BaseNode.
842
- *
843
- * Default behavior:
844
- * - Click on node in NodeManager layer selects the node
845
- * - Selected node becomes draggable (dragEnabled)
846
- * - Click on empty area deselects (deselectOnEmptyClick)
847
- * - Optionally enable Konva.Transformer (enableTransformer)
848
- */
849
- declare class SelectionPlugin extends Plugin {
850
- private _core?;
851
- private _options;
852
- private _selected;
853
- private _prevDraggable;
854
- private _transformer;
855
- private _transformerWasVisibleBeforeDrag;
856
- private _cornerHandlesWereVisibleBeforeDrag;
857
- private _sizeLabelWasVisibleBeforeDrag;
858
- private _rotateHandlesWereVisibleBeforeDrag;
859
- private _cornerHandlesGroup;
860
- private _cornerHandles;
861
- private _cornerHandlesSuppressed;
862
- private _transformOppositeCorner;
863
- private _sizeLabel;
864
- private _radiusLabel;
865
- private _rotateHandlesGroup;
866
- private _rotateHandles;
867
- private _rotateDragState;
868
- private _rotateCenterAbsStart;
869
- private _prevStageDraggableBeforeRotate;
870
- private _worldSyncRafId;
871
- private _onCameraZoomEvent;
872
- private _hoverTr;
873
- private _isPointerDown;
874
- private _autoPanRafId;
875
- private _autoPanActive;
876
- private _autoPanEdgePx;
877
- private _autoPanMaxSpeedPx;
878
- private _draggingNode;
879
- private _ratioKeyPressed;
880
- private _onGlobalKeyDown;
881
- private _onGlobalKeyUp;
882
- private _tempMultiSet;
883
- private _tempMultiGroup;
884
- private _tempMultiTr;
885
- private _tempOverlay;
886
- private _tempRotateHandlesGroup;
887
- private _tempRotateHandles;
888
- private _tempPlacement;
889
- getMultiGroupController(): MultiGroupController;
890
- private _tempMultiSizeLabel;
891
- private _tempMultiHitRect;
892
- private _multiCtrl;
893
- private _startAutoPanLoop;
894
- private _stopAutoPanLoop;
895
- /**
896
- * Deferred redraw (throttling)
897
- * Groups multiple batchDraw calls into one
898
- */
899
- private _scheduleBatchDraw;
900
- private _parentGroupDuringChildEdit;
901
- private _parentGroupPrevDraggable;
902
- private _dragMoveScheduled;
903
- private _batchDrawScheduled;
904
- private _hoverThrottle;
905
- private _uiUpdateDebounce;
906
- constructor(options?: SelectionPluginOptions);
907
- setOptions(patch: Partial<SelectionPluginOptions>): void;
908
- protected onAttach(core: CoreEngine): void;
909
- protected onDetach(core: CoreEngine): void;
910
- private _onMouseDown;
911
- private _select;
912
- private _clearSelection;
913
- private _ensureTempMulti;
914
- private _destroyTempMulti;
915
- private _updateTempRotateHandlesPosition;
916
- private _updateTempMultiSizeLabel;
917
- private _updateTempMultiHitRect;
918
- private _commitTempMultiToGroup;
919
- private _tryUngroupSelectedGroup;
920
- private _ensureHoverTr;
921
- private _destroyHoverTr;
922
- private _onHoverMoveThrottled;
923
- private _onHoverMove;
924
- private _onHoverDown;
925
- private _onHoverUp;
926
- private _onHoverLeave;
927
- private _refreshTransformer;
928
- private _restyleSideAnchors;
929
- private _setupRotateHandles;
930
- private _destroyRotateHandles;
931
- private _getNodeCenterAbs;
932
- private _updateRotateHandlesPosition;
933
- private _setupSizeLabel;
934
- private _scheduleUIUpdate;
935
- private _updateSizeLabel;
936
- private _destroySizeLabel;
937
- private _isCornerRadiusSupported;
938
- private _getCornerRadiusArray;
939
- private _setCornerRadiusArray;
940
- private _setupCornerRadiusHandles;
941
- private _destroyCornerRadiusHandles;
942
- private _bakeRectScale;
943
- private _updateCornerRadiusHandlesPosition;
944
- private _updateCornerRadiusHandlesVisibility;
945
- private _ensureRadiusLabel;
946
- private _updateRadiusLabelAt;
947
- private _showRadiusLabelForCorner;
948
- private _hideRadiusLabel;
949
- private _destroyRadiusLabel;
950
- private _findBaseNodeByTarget;
951
- private _onNodeRemoved;
952
- }
953
-
954
1165
  interface GridPluginOptions {
955
1166
  stepX?: number;
956
1167
  stepY?: number;
@@ -1001,96 +1212,209 @@ declare class GridPlugin extends Plugin {
1001
1212
  setSnap(enabled: boolean): void;
1002
1213
  }
1003
1214
 
1004
- interface RulerPluginOptions {
1005
- thicknessPx?: number;
1006
- fontFamily?: string;
1007
- fontSizePx?: number;
1008
- color?: string;
1009
- bgColor?: string;
1010
- borderColor?: string;
1011
- enabled?: boolean;
1215
+ interface HistoryPluginOptions {
1216
+ /** DOM target для прослушивания клавиш (по умолчанию globalThis) */
1217
+ target?: Window | Document | HTMLElement | EventTarget;
1218
+ /** Игнорировать хоткеи, если фокус на редактируемом элементе */
1219
+ ignoreEditableTargets?: boolean;
1220
+ /** Максимальное количество действий в истории (0 = без лимита) */
1221
+ maxHistoryLength?: number;
1012
1222
  }
1013
- declare class RulerPlugin extends Plugin {
1223
+ /**
1224
+ * HistoryPlugin — плагин для Undo/Redo функциональности
1225
+ *
1226
+ * Хоткеи:
1227
+ * - Ctrl+Z / Cmd+Z — Undo
1228
+ * - Ctrl+Shift+Z / Cmd+Shift+Z — Redo
1229
+ *
1230
+ * Автоматически записывает в историю:
1231
+ * - Создание нод (node:created)
1232
+ * - Удаление нод (node:removed)
1233
+ * - Трансформации (node:transformed)
1234
+ * - Изменение z-index (node:zIndexChanged)
1235
+ * - Создание групп (group:created)
1236
+ * - Разгруппировка (group:ungrouped)
1237
+ */
1238
+ declare class HistoryPlugin extends Plugin {
1014
1239
  private _core?;
1240
+ private _history;
1015
1241
  private _options;
1016
- private _layer;
1017
- private _hGroup?;
1018
- private _vGroup?;
1019
- private _hBg?;
1020
- private _vBg?;
1021
- private _hTicksShape?;
1022
- private _vTicksShape?;
1023
- private _hBorder?;
1024
- private _vBorder?;
1025
- private _redrawScheduled;
1026
- private _lastRedrawTime;
1027
- private _redrawThrottleMs;
1028
- private _panThrottleMs;
1029
- private _cachedActiveGuide;
1030
- private _cacheInvalidated;
1031
- private _stepsCache;
1032
- constructor(options?: RulerPluginOptions);
1242
+ /** Флаг для предотвращения записи в историю при undo/redo */
1243
+ private _isUndoRedoInProgress;
1244
+ /** Кэш состояний нод ДО начала drag/transform */
1245
+ private _dragStartStateCache;
1246
+ /** Флаг batch-режима для временной группы */
1247
+ private _isBatchMode;
1248
+ /** Буфер действий для batch */
1249
+ private _batchBuffer;
1250
+ /** Таймер для автоматического завершения batch */
1251
+ private _batchCommitTimer;
1252
+ constructor(options?: HistoryPluginOptions);
1033
1253
  /**
1034
- * Calculate optimal step for ruler ticks
1035
- * Uses nice numbers: 1, 2, 5, 10, 20, 50, 100 and so on
1254
+ * Получить менеджер истории для внешнего доступа
1036
1255
  */
1037
- private _calculateNiceStep;
1256
+ getHistoryManager(): HistoryManager;
1038
1257
  /**
1039
- * Format number for display on ruler
1040
- * Always returns an integer without decimal places
1258
+ * Начать batch-режим для группировки нескольких действий
1041
1259
  */
1042
- private _formatNumber;
1260
+ startBatch(): void;
1043
1261
  /**
1044
- * Calculate and cache parameters for ticks for current scale
1262
+ * Завершить batch-режим (вызывается извне после эмита всех событий)
1045
1263
  */
1046
- private _getStepsConfig;
1264
+ commitBatch(): void;
1265
+ /**
1266
+ * Проверить, активен ли batch-режим
1267
+ */
1268
+ isBatchMode(): boolean;
1047
1269
  protected onAttach(core: CoreEngine): void;
1048
1270
  protected onDetach(core: CoreEngine): void;
1271
+ private _onKeyDown;
1272
+ private _isEditableTarget;
1273
+ private _onDragStart;
1274
+ private _onDragEnd;
1049
1275
  /**
1050
- * Get active guide from RulerGuidesPlugin (with caching)
1276
+ * Завершить batch-режим и записать составное действие
1051
1277
  */
1052
- private _getActiveGuideInfo;
1278
+ private _finishBatch;
1279
+ private _onTransformStart;
1280
+ private _onTransformEnd;
1281
+ private _performUndo;
1282
+ private _performRedo;
1053
1283
  /**
1054
- * Invalidate active guide cache
1284
+ * Применить состояние из action (before или after)
1055
1285
  */
1056
- private _invalidateGuideCache;
1286
+ private _applyActionState;
1057
1287
  /**
1058
- * Universal ruler drawing (horizontal or vertical)
1059
- * @param ctx - canvas context
1060
- * @param axis - ruler axis ('h' for horizontal, 'v' for vertical)
1061
- * @param activeGuide - cached active guide info
1288
+ * Применить состояние трансформации к ноде
1062
1289
  */
1063
- private _drawRuler;
1290
+ private _applyTransformState;
1064
1291
  /**
1065
- * Draw horizontal ruler
1066
- * @param activeGuide - cached active guide info
1292
+ * Применить действие группировки
1067
1293
  */
1068
- private _drawHorizontalRuler;
1294
+ private _applyGroupAction;
1069
1295
  /**
1070
- * Draw vertical ruler
1071
- * @param activeGuide - cached active guide info
1296
+ * Применить действие разгруппировки
1072
1297
  */
1073
- private _drawVerticalRuler;
1298
+ private _applyUngroupAction;
1074
1299
  /**
1075
- * Full ruler redraw
1300
+ * Воссоздать ноду по сериализованному состоянию
1076
1301
  */
1077
- private _redraw;
1302
+ private _recreateNode;
1303
+ private _onNodeCreated;
1304
+ private _onNodeRemoved;
1305
+ private _onNodeTransformed;
1078
1306
  /**
1079
- * Deferred redraw with improved throttling
1080
- * Groups fast zoom/pan events for optimization
1081
- * @param isPanning - true for panning (more aggressive throttling)
1307
+ * Запланировать завершение batch с debounce
1082
1308
  */
1083
- private _scheduleRedraw;
1084
- show(): void;
1085
- hide(): void;
1309
+ private _scheduleBatchCommit;
1310
+ private _onZIndexChanged;
1311
+ private _onGroupCreated;
1312
+ private _onGroupUngrouped;
1086
1313
  /**
1087
- * Toggle ruler visibility
1314
+ * Полная сериализация ноды для истории
1088
1315
  */
1089
- toggle(): void;
1316
+ private _serializeNode;
1090
1317
  /**
1091
- * Check if ruler is visible
1318
+ * Захватить состояние трансформации (для drag/transform)
1319
+ * Использует абсолютные координаты для корректной работы с временными группами
1092
1320
  */
1093
- isVisible(): boolean;
1321
+ private _captureTransformState;
1322
+ /**
1323
+ * Построить after-состояние из changes события node:transformed
1324
+ * Использует координаты из changes (world-local для temp-multi-group)
1325
+ */
1326
+ private _buildAfterState;
1327
+ /**
1328
+ * Получить тип ноды из Konva className
1329
+ */
1330
+ private _getNodeType;
1331
+ /**
1332
+ * Найти BaseNode по Konva.Node (ищет также по родителям)
1333
+ */
1334
+ private _findBaseNodeByKonva;
1335
+ /**
1336
+ * Сравнить два состояния на равенство
1337
+ * Использует абсолютные координаты для сравнения (absX, absY, absRotation, etc.)
1338
+ */
1339
+ private _statesEqual;
1340
+ /**
1341
+ * Обрезать историю если превышен лимит
1342
+ */
1343
+ private _trimHistoryIfNeeded;
1344
+ private _debug;
1345
+ }
1346
+
1347
+ interface LogoOptions {
1348
+ src: string;
1349
+ width: number;
1350
+ height: number;
1351
+ opacity?: number;
1352
+ }
1353
+ declare class LogoPlugin extends Plugin {
1354
+ private _core?;
1355
+ private _layer?;
1356
+ private _image?;
1357
+ private _src;
1358
+ private _width;
1359
+ private _height;
1360
+ private _opacity;
1361
+ constructor(options: LogoOptions);
1362
+ protected onAttach(core: CoreEngine): void;
1363
+ protected onDetach(core: CoreEngine): void;
1364
+ setOpacity(opacity: number): void;
1365
+ setSize({ width, height }: {
1366
+ width: number;
1367
+ height: number;
1368
+ }): void;
1369
+ setSource(src: string): void;
1370
+ private _setImage;
1371
+ private _loadImageFromString;
1372
+ private _layout;
1373
+ }
1374
+
1375
+ interface NodeHotkeysOptions {
1376
+ target?: Window | Document | HTMLElement | EventTarget;
1377
+ ignoreEditableTargets?: boolean;
1378
+ }
1379
+ declare class NodeHotkeysPlugin extends Plugin {
1380
+ private _core?;
1381
+ private _options;
1382
+ private _clipboard;
1383
+ private _selectionPlugin?;
1384
+ constructor(options?: NodeHotkeysOptions);
1385
+ protected onAttach(core: CoreEngine): void;
1386
+ protected onDetach(_core: CoreEngine): void;
1387
+ private _onKeyDown;
1388
+ private _isEditableTarget;
1389
+ private _handleCopy;
1390
+ private _handleCut;
1391
+ private _handlePaste;
1392
+ private _handleDelete;
1393
+ private _getSelectedNodes;
1394
+ private _deleteNodes;
1395
+ private _serializeNode;
1396
+ private _serializeKonvaNode;
1397
+ private _getNodeTypeFromKonva;
1398
+ private _deserializeNode;
1399
+ private _getPastePosition;
1400
+ private _isPointerOnScreen;
1401
+ private _getScreenCenter;
1402
+ private _getClipboardCenter;
1403
+ private _computeSelectionWorldCenter;
1404
+ private _handleMoveUp;
1405
+ private _handleMoveDown;
1406
+ private _handleMoveToTop;
1407
+ private _handleMoveToBottom;
1408
+ /**
1409
+ * Checks if the node is inside a real group (not the group itself)
1410
+ */
1411
+ private _isNodeInsidePermanentGroup;
1412
+ /**
1413
+ * Checks if the node is inside a real group
1414
+ * - For group itself — do nothing (moveUp/moveDown already applied to the group)
1415
+ * - For node inside group — FORBIDDEN to change z-index
1416
+ */
1417
+ private _syncGroupZIndex;
1094
1418
  }
1095
1419
 
1096
1420
  interface RulerGuidesPluginOptions {
@@ -1266,126 +1590,379 @@ declare class RulerManagerPlugin extends Plugin {
1266
1590
  deleteActiveGuide(): boolean;
1267
1591
  }
1268
1592
 
1269
- interface AreaSelectionPluginOptions {
1270
- rectStroke?: string;
1271
- rectFill?: string;
1272
- rectStrokeWidth?: number;
1273
- rectOpacity?: number;
1274
- enableKeyboardShortcuts?: boolean;
1593
+ interface RulerPluginOptions {
1594
+ thicknessPx?: number;
1595
+ fontFamily?: string;
1596
+ fontSizePx?: number;
1597
+ color?: string;
1598
+ bgColor?: string;
1599
+ borderColor?: string;
1600
+ enabled?: boolean;
1275
1601
  }
1276
- /**
1277
- * AreaSelectionPlugin
1278
- * - Drag LKM over empty space draws selection rectangle (marquee) in screen coordinates
1279
- * - All nodes whose client rectangles intersect the rectangle are temporarily grouped
1280
- * - Click outside — temporary group is removed, nodes return to their original positions
1281
- * - Ctrl+G — lock in permanent group (GroupNode through NodeManager)
1282
- * - Ctrl+Shift+G — unlock selected permanent group
1283
- */
1284
- declare class AreaSelectionPlugin extends Plugin {
1602
+ declare class RulerPlugin extends Plugin {
1285
1603
  private _core?;
1286
- private _layer;
1287
- private _rect;
1288
- private _start;
1289
- private _transformer;
1290
- private _selecting;
1291
1604
  private _options;
1292
- constructor(options?: AreaSelectionPluginOptions);
1605
+ private _layer;
1606
+ private _hGroup?;
1607
+ private _vGroup?;
1608
+ private _hBg?;
1609
+ private _vBg?;
1610
+ private _hTicksShape?;
1611
+ private _vTicksShape?;
1612
+ private _hBorder?;
1613
+ private _vBorder?;
1614
+ private _redrawScheduled;
1615
+ private _lastRedrawTime;
1616
+ private _redrawThrottleMs;
1617
+ private _panThrottleMs;
1618
+ private _cachedActiveGuide;
1619
+ private _cacheInvalidated;
1620
+ private _stepsCache;
1621
+ constructor(options?: RulerPluginOptions);
1622
+ /**
1623
+ * Calculate optimal step for ruler ticks
1624
+ * Uses nice numbers: 1, 2, 5, 10, 20, 50, 100 and so on
1625
+ */
1626
+ private _calculateNiceStep;
1627
+ /**
1628
+ * Format number for display on ruler
1629
+ * Always returns an integer without decimal places
1630
+ */
1631
+ private _formatNumber;
1632
+ /**
1633
+ * Calculate and cache parameters for ticks for current scale
1634
+ */
1635
+ private _getStepsConfig;
1293
1636
  protected onAttach(core: CoreEngine): void;
1294
1637
  protected onDetach(core: CoreEngine): void;
1295
- private _finalizeArea;
1296
- private _clearSelection;
1297
- private _getSelectionPlugin;
1298
- private _rectsIntersect;
1299
- private _findOwningGroupBaseNode;
1300
- private _isAncestor;
1301
- private _isPermanentGroupSelected;
1302
- private _currentGroupNode;
1303
- private _pointerInsidePermanentGroupBBox;
1304
- }
1305
-
1306
- interface NodeHotkeysOptions {
1307
- target?: Window | Document | HTMLElement | EventTarget;
1308
- ignoreEditableTargets?: boolean;
1309
- }
1310
- declare class NodeHotkeysPlugin extends Plugin {
1311
- private _core?;
1312
- private _options;
1313
- private _clipboard;
1314
- private _selectionPlugin?;
1315
- constructor(options?: NodeHotkeysOptions);
1316
- protected onAttach(core: CoreEngine): void;
1317
- protected onDetach(_core: CoreEngine): void;
1318
- private _onKeyDown;
1319
- private _isEditableTarget;
1320
- private _handleCopy;
1321
- private _handleCut;
1322
- private _handlePaste;
1323
- private _handleDelete;
1324
- private _getSelectedNodes;
1325
- private _deleteNodes;
1326
- private _serializeNode;
1327
- private _serializeKonvaNode;
1328
- private _getNodeTypeFromKonva;
1329
- private _deserializeNode;
1330
- private _getPastePosition;
1331
- private _isPointerOnScreen;
1332
- private _getScreenCenter;
1333
- private _getClipboardCenter;
1334
- private _computeSelectionWorldCenter;
1335
- private _handleMoveUp;
1336
- private _handleMoveDown;
1337
1638
  /**
1338
- * Checks if the node is inside a real group (not the group itself)
1639
+ * Get active guide from RulerGuidesPlugin (with caching)
1339
1640
  */
1340
- private _isNodeInsidePermanentGroup;
1641
+ private _getActiveGuideInfo;
1341
1642
  /**
1342
- * Checks if the node is inside a real group
1343
- * - For group itself — do nothing (moveUp/moveDown already applied to the group)
1344
- * - For node inside group — FORBIDDEN to change z-index
1643
+ * Invalidate active guide cache
1345
1644
  */
1346
- private _syncGroupZIndex;
1645
+ private _invalidateGuideCache;
1646
+ /**
1647
+ * Universal ruler drawing (horizontal or vertical)
1648
+ * @param ctx - canvas context
1649
+ * @param axis - ruler axis ('h' for horizontal, 'v' for vertical)
1650
+ * @param activeGuide - cached active guide info
1651
+ */
1652
+ private _drawRuler;
1653
+ /**
1654
+ * Draw horizontal ruler
1655
+ * @param activeGuide - cached active guide info
1656
+ */
1657
+ private _drawHorizontalRuler;
1658
+ /**
1659
+ * Draw vertical ruler
1660
+ * @param activeGuide - cached active guide info
1661
+ */
1662
+ private _drawVerticalRuler;
1663
+ /**
1664
+ * Full ruler redraw
1665
+ */
1666
+ private _redraw;
1667
+ /**
1668
+ * Deferred redraw with improved throttling
1669
+ * Groups fast zoom/pan events for optimization
1670
+ * @param isPanning - true for panning (more aggressive throttling)
1671
+ */
1672
+ private _scheduleRedraw;
1673
+ show(): void;
1674
+ hide(): void;
1675
+ /**
1676
+ * Toggle ruler visibility
1677
+ */
1678
+ toggle(): void;
1679
+ /**
1680
+ * Check if ruler is visible
1681
+ */
1682
+ isVisible(): boolean;
1347
1683
  }
1348
1684
 
1685
+ interface MultiGroupControllerDeps {
1686
+ ensureTempMulti: (nodes: BaseNode[]) => void;
1687
+ destroyTempMulti: () => void;
1688
+ commitTempMultiToGroup: () => void;
1689
+ isActive: () => boolean;
1690
+ forceUpdate: () => void;
1691
+ onWorldChanged?: () => void;
1692
+ isInsideTempByTarget?: (target: Konva.Node) => boolean;
1693
+ }
1349
1694
  /**
1350
- * ThrottleHelper - utility for throttling (limiting the frequency of calls)
1351
- *
1352
- * Used to limit the frequency of operation execution to a certain number of times per second.
1353
- * For example, to limit UI updates to 60 FPS (16ms) or 30 FPS (32ms).
1354
- *
1355
- * @example
1356
- * ```typescript
1357
- * private _throttle = new ThrottleHelper(16); // 60 FPS
1695
+ * MultiGroupController thin controller encapsulating work with temporary multi-group.
1696
+ * Actual logic lives in passed dependencies (SelectionPlugin),
1697
+ * thanks to which we don't duplicate code for frames/overlays and behavior.
1698
+ */
1699
+ declare class MultiGroupController {
1700
+ private deps;
1701
+ constructor(deps: MultiGroupControllerDeps);
1702
+ ensure(nodes: BaseNode[]): void;
1703
+ destroy(): void;
1704
+ commitToPermanentGroup(): void;
1705
+ isActive(): boolean;
1706
+ forceUpdateOverlays(): void;
1707
+ onWorldChanged(): void;
1708
+ isInsideTempByTarget(target: Konva.Node): boolean;
1709
+ }
1710
+
1711
+ interface SelectionPluginOptions {
1712
+ dragEnabled?: boolean;
1713
+ enableTransformer?: boolean;
1714
+ deselectOnEmptyClick?: boolean;
1715
+ selectablePredicate?: (node: Konva.Node) => boolean;
1716
+ autoPanEnabled?: boolean;
1717
+ autoPanEdgePx?: number;
1718
+ autoPanMaxSpeedPx?: number;
1719
+ }
1720
+ /**
1721
+ * Universal selection and dragging plugin for nodes compatible with BaseNode.
1358
1722
  *
1359
- * onMouseMove() {
1360
- * if (!this._throttle.shouldExecute()) return;
1361
- * // Execute expensive operation
1362
- * }
1363
- * ```
1723
+ * Default behavior:
1724
+ * - Click on node in NodeManager layer selects the node
1725
+ * - Selected node becomes draggable (dragEnabled)
1726
+ * - Click on empty area deselects (deselectOnEmptyClick)
1727
+ * - Optionally enable Konva.Transformer (enableTransformer)
1364
1728
  */
1365
- declare class ThrottleHelper {
1366
- private _lastTime;
1367
- private _throttle;
1368
- /**
1369
- * @param throttleMs - minimum interval between calls in milliseconds
1370
- */
1371
- constructor(throttleMs?: number);
1729
+ declare class SelectionPlugin extends Plugin {
1730
+ private _core?;
1731
+ private _options;
1732
+ private _selected;
1733
+ private _prevDraggable;
1734
+ private _transformer;
1735
+ private _transformerWasVisibleBeforeDrag;
1736
+ private _cornerHandlesWereVisibleBeforeDrag;
1737
+ private _sizeLabelWasVisibleBeforeDrag;
1738
+ private _rotateHandlesWereVisibleBeforeDrag;
1739
+ private _cornerHandlesGroup;
1740
+ private _cornerHandles;
1741
+ private _cornerHandlesSuppressed;
1742
+ private _transformOppositeCorner;
1743
+ private _sizeLabel;
1744
+ private _radiusLabel;
1745
+ private _rotateHandlesGroup;
1746
+ private _rotateHandles;
1747
+ private _rotateDragState;
1748
+ private _rotateCenterAbsStart;
1749
+ private _prevStageDraggableBeforeRotate;
1750
+ private _worldSyncRafId;
1751
+ private _onCameraZoomEvent;
1752
+ private _hoverTr;
1753
+ private _isPointerDown;
1754
+ private _autoPanRafId;
1755
+ private _autoPanActive;
1756
+ private _autoPanEdgePx;
1757
+ private _autoPanMaxSpeedPx;
1758
+ private _draggingNode;
1759
+ private _ratioKeyPressed;
1760
+ private _onGlobalKeyDown;
1761
+ private _onGlobalKeyUp;
1762
+ private _tempMultiSet;
1763
+ private _tempMultiNodes;
1764
+ private _tempMultiInitialTransforms;
1765
+ private _tempMultiGroup;
1766
+ private _tempOverlay;
1767
+ getMultiGroupController(): MultiGroupController;
1768
+ private _multiCtrl;
1769
+ private _startAutoPanLoop;
1770
+ private _stopAutoPanLoop;
1372
1771
  /**
1373
- * Checks if the operation can be executed
1374
- * @returns true if enough time has passed since the last call
1772
+ * Deferred redraw (throttling)
1773
+ * Groups multiple batchDraw calls into one
1375
1774
  */
1376
- shouldExecute(): boolean;
1775
+ private _scheduleBatchDraw;
1776
+ private _parentGroupDuringChildEdit;
1777
+ private _parentGroupPrevDraggable;
1778
+ private _dragMoveScheduled;
1779
+ private _batchDrawScheduled;
1780
+ private _hoverThrottle;
1781
+ private _uiUpdateDebounce;
1782
+ constructor(options?: SelectionPluginOptions);
1783
+ setOptions(patch: Partial<SelectionPluginOptions>): void;
1784
+ protected onAttach(core: CoreEngine): void;
1785
+ protected onDetach(core: CoreEngine): void;
1786
+ private _onMouseDown;
1787
+ private _select;
1788
+ private _clearSelection;
1377
1789
  /**
1378
- * Resets the timer (the next call will be executed immediately)
1790
+ * Apply transformation from overlay group to actual nodes using matrix math.
1791
+ * Each node's transform is updated to match the overlay group's transformation,
1792
+ * while staying in its original parent.
1379
1793
  */
1380
- reset(): void;
1794
+ private _applyOverlayTransformToNodes;
1381
1795
  /**
1382
- * Changes the throttling interval
1796
+ * Update overlay group bbox to match current positions of selected nodes.
1797
+ * Must be called after drag/transform when nodes change position.
1383
1798
  */
1384
- setThrottle(throttleMs: number): void;
1799
+ private _updateTempMultiOverlayBBox;
1385
1800
  /**
1386
- * Returns the current throttling interval
1801
+ * Compute union bounding box for multiple nodes in world-local coordinates.
1802
+ * Used for overlay-only temporary multi-group.
1803
+ * Returns bbox that doesn't change when world transform (zoom/pan) changes.
1387
1804
  */
1388
- getThrottle(): number;
1805
+ private _computeUnionBBox;
1806
+ private _ensureTempMulti;
1807
+ private _destroyTempMulti;
1808
+ private _commitTempMultiToGroup;
1809
+ private _tryUngroupSelectedGroup;
1810
+ private _ensureHoverTr;
1811
+ private _destroyHoverTr;
1812
+ private _onHoverMoveThrottled;
1813
+ private _onHoverMove;
1814
+ private _onHoverDown;
1815
+ private _onHoverUp;
1816
+ private _onHoverLeave;
1817
+ private _refreshTransformer;
1818
+ private _restyleSideAnchors;
1819
+ private _setupRotateHandles;
1820
+ private _destroyRotateHandles;
1821
+ private _getNodeCenterAbs;
1822
+ private _updateRotateHandlesPosition;
1823
+ private _setupSizeLabel;
1824
+ private _scheduleUIUpdate;
1825
+ private _updateSizeLabel;
1826
+ private _destroySizeLabel;
1827
+ private _isCornerRadiusSupported;
1828
+ private _getCornerRadiusArray;
1829
+ private _setCornerRadiusArray;
1830
+ private _setupCornerRadiusHandles;
1831
+ private _destroyCornerRadiusHandles;
1832
+ private _bakeRectScale;
1833
+ private _updateCornerRadiusHandlesPosition;
1834
+ private _updateCornerRadiusHandlesVisibility;
1835
+ private _ensureRadiusLabel;
1836
+ private _updateRadiusLabelAt;
1837
+ private _showRadiusLabelForCorner;
1838
+ private _hideRadiusLabel;
1839
+ private _destroyRadiusLabel;
1840
+ private _findBaseNodeByTarget;
1841
+ private _onNodeRemoved;
1842
+ private _disableGroupChildrenDragging;
1843
+ }
1844
+
1845
+ interface VisualGuidesPluginOptions {
1846
+ guidelineColor?: string;
1847
+ guidelineWidth?: number;
1848
+ guidelineDash?: number[];
1849
+ thresholdPx?: number;
1850
+ }
1851
+ /**
1852
+ * VisualGuidesPlugin — snapping of nodes and groups relative to other nodes/groups + stage borders.
1853
+ *
1854
+ * Implemented directly on top of Konva, based on the official guidelines example:
1855
+ * https://konvajs.org/docs/sandbox/Guides.html
1856
+ */
1857
+ declare class VisualGuidesPlugin extends Plugin {
1858
+ private _core?;
1859
+ private _options;
1860
+ private _layer;
1861
+ private _dragMoveHandler;
1862
+ private _dragEndHandler;
1863
+ private _nodesAddHandler;
1864
+ private _nodesRemoveHandler;
1865
+ constructor(options?: VisualGuidesPluginOptions);
1866
+ private _applyGapSnappingForDrag;
1867
+ protected onAttach(core: CoreEngine): void;
1868
+ protected onDetach(core: CoreEngine): void;
1869
+ private _onDragMove;
1870
+ private _clearGuides;
1871
+ private _drawDragGuides;
1872
+ private _pointOnRay;
1873
+ private _drawRayMarkerWithLabel;
1874
+ private _buildDragRays;
1875
+ private _collectOtherNodeBoxes;
1876
+ private _intersectRayWithBox;
1877
+ private _getLineGuideStops;
1878
+ private _getObjectSnappingEdges;
1879
+ private _getGuides;
1880
+ private _attachExistingTransformers;
1881
+ private _walkAttachTransformers;
1882
+ private _attachTransformer;
1883
+ private _walkDetachTransformers;
1884
+ private _handleTransformerTransform;
1885
+ private _drawResizeGuides;
1886
+ private _buildResizeRays;
1887
+ private _isVisualLinesShouldCleared;
1888
+ }
1889
+
1890
+ type ImageHoverFilterMode = 'sepia' | 'warm' | 'cool';
1891
+ interface ImageHoverFilterAddonOptions {
1892
+ mode?: ImageHoverFilterMode;
1893
+ /** 0..1 — effect strength for warm/cool */
1894
+ intensity?: number;
1895
+ /** pixelRatio for cache during hover. Default = devicePixelRatio or 1 */
1896
+ pixelRatio?: number;
1897
+ }
1898
+ declare class ImageHoverFilterAddon extends NodeAddon<ImageNode> {
1899
+ private readonly mode;
1900
+ private readonly intensity;
1901
+ private readonly pixelRatio;
1902
+ private readonly nodes;
1903
+ constructor(options?: ImageHoverFilterAddonOptions);
1904
+ protected onAttach(node: ImageNode): void;
1905
+ protected onDetach(node: ImageNode): void;
1906
+ }
1907
+
1908
+ /**
1909
+ * Addon for RulerPlugin, set up by RulerGuidesPlugin.
1910
+ */
1911
+ declare class RulerGuidesAddon extends PluginAddon<RulerPlugin> {
1912
+ private readonly _options;
1913
+ private _instance;
1914
+ private _owned;
1915
+ constructor(options?: RulerGuidesPluginOptions);
1916
+ protected onAttach(_plugin: RulerPlugin, core: CoreEngine): void;
1917
+ protected onDetach(_plugin: RulerPlugin, core: CoreEngine): void;
1918
+ }
1919
+
1920
+ /**
1921
+ * Addon for RulerPlugin, set up by RulerHighlightPlugin.
1922
+ */
1923
+ declare class RulerHighlightAddon extends PluginAddon<RulerPlugin> {
1924
+ private readonly _options;
1925
+ private _instance;
1926
+ private _owned;
1927
+ constructor(options?: RulerHighlightPluginOptions);
1928
+ protected onAttach(_plugin: RulerPlugin, core: CoreEngine): void;
1929
+ protected onDetach(_plugin: RulerPlugin, core: CoreEngine): void;
1930
+ }
1931
+
1932
+ /**
1933
+ * Addon for RulerPlugin, set up by RulerManagerPlugin.
1934
+ */
1935
+ declare class RulerManagerAddon extends PluginAddon<RulerPlugin> {
1936
+ private readonly _options;
1937
+ private _instance;
1938
+ private _owned;
1939
+ constructor(options?: RulerManagerPluginOptions);
1940
+ protected onAttach(_plugin: RulerPlugin, core: CoreEngine): void;
1941
+ protected onDetach(_plugin: RulerPlugin, core: CoreEngine): void;
1942
+ }
1943
+
1944
+ interface ShapeHoverHighlightAddonOptions {
1945
+ stroke?: string;
1946
+ strokeWidth?: number;
1947
+ fill?: string;
1948
+ mode?: 'stroke' | 'fill' | 'both';
1949
+ }
1950
+ declare class ShapeHoverHighlightAddon extends NodeAddon<ShapeNode> {
1951
+ private readonly stroke;
1952
+ private readonly strokeWidth;
1953
+ private readonly fill;
1954
+ private readonly mode;
1955
+ private readonly nodes;
1956
+ constructor(options?: ShapeHoverHighlightAddonOptions);
1957
+ protected onAttach(node: ShapeNode): void;
1958
+ protected onDetach(node: ShapeNode): void;
1959
+ }
1960
+
1961
+ declare class TextAutoTrimAddon extends NodeAddon<TextNode> {
1962
+ private readonly nodes;
1963
+ protected onAttach(node: TextNode): void;
1964
+ protected onDetach(node: TextNode): void;
1965
+ private normalize;
1389
1966
  }
1390
1967
 
1391
1968
  /**
@@ -1427,4 +2004,46 @@ declare class DebounceHelper {
1427
2004
  cancel(): void;
1428
2005
  }
1429
2006
 
1430
- export { ArcNode, AreaSelectionPlugin, ArrowNode, CameraHotkeysPlugin, CameraManager, CircleNode, CoreEngine, DebounceHelper, EllipseNode, EventBus, GridPlugin, GroupNode, ImageNode, LogoPlugin, NodeHotkeysPlugin, NodeManager, Plugins, RegularPolygonNode, RingNode, RulerGuidesPlugin, RulerHighlightPlugin, RulerManagerPlugin, RulerPlugin, SelectionPlugin, ShapeNode, StarNode, TextNode, ThrottleHelper };
2007
+ /**
2008
+ * ThrottleHelper - utility for throttling (limiting the frequency of calls)
2009
+ *
2010
+ * Used to limit the frequency of operation execution to a certain number of times per second.
2011
+ * For example, to limit UI updates to 60 FPS (16ms) or 30 FPS (32ms).
2012
+ *
2013
+ * @example
2014
+ * ```typescript
2015
+ * private _throttle = new ThrottleHelper(16); // 60 FPS
2016
+ *
2017
+ * onMouseMove() {
2018
+ * if (!this._throttle.shouldExecute()) return;
2019
+ * // Execute expensive operation
2020
+ * }
2021
+ * ```
2022
+ */
2023
+ declare class ThrottleHelper {
2024
+ private _lastTime;
2025
+ private _throttle;
2026
+ /**
2027
+ * @param throttleMs - minimum interval between calls in milliseconds
2028
+ */
2029
+ constructor(throttleMs?: number);
2030
+ /**
2031
+ * Checks if the operation can be executed
2032
+ * @returns true if enough time has passed since the last call
2033
+ */
2034
+ shouldExecute(): boolean;
2035
+ /**
2036
+ * Resets the timer (the next call will be executed immediately)
2037
+ */
2038
+ reset(): void;
2039
+ /**
2040
+ * Changes the throttling interval
2041
+ */
2042
+ setThrottle(throttleMs: number): void;
2043
+ /**
2044
+ * Returns the current throttling interval
2045
+ */
2046
+ getThrottle(): number;
2047
+ }
2048
+
2049
+ export { type ArcNodeHandle, type ArcNodeOptions, AreaSelectionPlugin, type AreaSelectionPluginOptions, type ArrowNodeHandle, type ArrowNodeOptions, type BaseNodeOptions, type CameraHotkeysOptions, CameraHotkeysPlugin, CameraManager, type CircleNodeHandle, type CircleNodeOptions, CoreEngine, type CoreEngineOptions, type CoreEvents, DebounceHelper, type EllipseNodeHandle, type EllipseNodeOptions, EventBus, GridPlugin, type GridPluginOptions, type GroupNodeHandle, type GroupNodeOptions, type HistoryAction, HistoryManager, HistoryPlugin, type HistoryPluginOptions, ImageHoverFilterAddon, type ImageNodeHandle, type ImageNodeOptions, type ImageSource, type KonvaArc, type KonvaArrow, type KonvaCircle, type KonvaEllipse, type KonvaGroup, type KonvaGroupConfig, type KonvaImage, type KonvaLayer, type KonvaNode, type KonvaNodeConfig, type KonvaRegularPolygon, type KonvaRing, type KonvaShape, type KonvaStage, type KonvaStar, type KonvaText, type LogoOptions, LogoPlugin, NodeAddon, NodeAddons, type NodeAddonsHandle, type NodeHandle, type NodeHotkeysOptions, NodeHotkeysPlugin, NodeManager, PluginAddon, Plugins, type RegularPolygonNodeHandle, type RegularPolygonNodeOptions, type RingNodeHandle, type RingNodeOptions, RulerGuidesAddon, RulerGuidesPlugin, type RulerGuidesPluginOptions, RulerHighlightAddon, RulerHighlightPlugin, type RulerHighlightPluginOptions, RulerManagerAddon, RulerManagerPlugin, type RulerManagerPluginOptions, RulerPlugin, type RulerPluginOptions, SelectionPlugin, type SelectionPluginOptions, ShapeHoverHighlightAddon, type ShapeNodeHandle, type ShapeNodeOptions, type StarNodeHandle, type StarNodeOptions, TextAutoTrimAddon, type TextNodeHandle, type TextNodeOptions, ThrottleHelper, VisualGuidesPlugin, type VisualGuidesPluginOptions };