@flowscape-ui/core-sdk 1.0.2 → 1.0.4

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,262 @@ 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
+ }
263
+ /**
264
+ * Handle for SvgNode
265
+ */
266
+ interface SvgNodeHandle extends NodeHandle {
267
+ setSrc(url: string, onLoad?: (node: unknown) => void, onError?: (error: Error) => void): Promise<unknown>;
268
+ setSize(size: {
269
+ width: number;
270
+ height: number;
271
+ }): this;
272
+ setCornerRadius(radius: number | number[]): this;
273
+ setOpacity(opacity: number): this;
274
+ getSize(): {
275
+ width: number;
276
+ height: number;
277
+ };
278
+ getCornerRadius(): number;
279
+ getOpacity(): number;
280
+ isLoading(): boolean;
281
+ isLoaded(): boolean;
282
+ }
283
+ /**
284
+ * Handle for VideoNode
285
+ */
286
+ interface VideoNodeHandle extends NodeHandle {
287
+ setSrc(url: string, options?: Record<string, unknown>): Promise<unknown>;
288
+ play(): Promise<unknown>;
289
+ pause(): this;
290
+ stop(): this;
291
+ setCurrentTime(time: number): this;
292
+ getCurrentTime(): number;
293
+ getDuration(): number;
294
+ setVolume(volume: number): this;
295
+ getVolume(): number;
296
+ setMuted(muted: boolean): this;
297
+ isMuted(): boolean;
298
+ setLoop(loop: boolean): this;
299
+ isLoop(): boolean;
300
+ setPlaybackRate(rate: number): this;
301
+ getPlaybackRate(): number;
302
+ isPlaying(): boolean;
303
+ isLoaded(): boolean;
304
+ getVideoElement(): HTMLVideoElement | null;
305
+ setSize(size: {
306
+ width: number;
307
+ height: number;
308
+ }): this;
309
+ setCornerRadius(radius: number | number[]): this;
310
+ setOpacity(opacity: number): this;
311
+ getSize(): {
312
+ width: number;
313
+ height: number;
314
+ };
315
+ getCornerRadius(): number;
316
+ getOpacity(): number;
317
+ }
318
+ /**
319
+ * Handle for GifNode
320
+ */
321
+ interface GifNodeHandle extends NodeHandle {
322
+ setSrc(url: string, options?: Record<string, unknown>): Promise<unknown>;
323
+ play(): this;
324
+ pause(): this;
325
+ isPlaying(): boolean;
326
+ isLoaded(): boolean;
327
+ getFrameIndex(): number;
328
+ getCanvas(): HTMLCanvasElement | null;
329
+ setSize(size: {
330
+ width: number;
331
+ height: number;
332
+ }): this;
333
+ setCornerRadius(radius: number | number[]): this;
334
+ setOpacity(opacity: number): this;
335
+ getSize(): {
336
+ width: number;
337
+ height: number;
338
+ };
339
+ getCornerRadius(): number;
340
+ getOpacity(): number;
341
+ }
45
342
 
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;
343
+ interface BaseNodeOptions {
344
+ id?: string;
345
+ x?: number;
346
+ y?: number;
347
+ width?: number;
348
+ height?: number;
349
+ }
350
+ declare abstract class BaseNode<T extends Konva.Node = Konva.Node> implements NodeHandle<T> {
351
+ protected konvaNode: T;
352
+ readonly id: string;
353
+ /** Local addons attached to this node */
354
+ readonly addons: NodeAddons<this>;
355
+ constructor(node: T, options?: BaseNodeOptions);
356
+ /**
357
+ * Public access to the low-level Konva object.
358
+ * Use this method instead of importing `konva` directly.
359
+ */
360
+ getKonvaNode(): T;
361
+ setPosition({ x, y }: {
362
+ x: number;
363
+ y: number;
364
+ }): this;
365
+ getPosition(): konva_lib_types.Vector2d;
366
+ remove(): void;
57
367
  }
58
368
 
59
369
  /**
60
- * Типизированные события CoreEngine
61
- * Все события строго типизированы для лучшего DX
370
+ * Typed CoreEngine events
371
+ * All events are strictly typed for better DX
62
372
  */
63
373
  interface CoreEvents {
64
- /** Нода была создана и добавлена в мир */
374
+ /** Node was created and added to the world */
65
375
  'node:created': [node: BaseNode];
66
- /** Нода была удалена из мира */
376
+ /** Node was removed from the world */
67
377
  'node:removed': [node: BaseNode];
68
- /** Нода была выделена */
378
+ /** Node was selected */
69
379
  'node:selected': [node: BaseNode];
70
- /** Выделение ноды было снято */
380
+ /** Node was deselected */
71
381
  'node:deselected': [node: BaseNode];
72
- /** Нода была изменена (position, size, rotation, etc.) */
382
+ /** Node was transformed (position, size, rotation, etc.) */
73
383
  'node:transformed': [
74
384
  node: BaseNode,
75
385
  changes: {
@@ -82,29 +392,29 @@ interface CoreEvents {
82
392
  scaleY?: number;
83
393
  }
84
394
  ];
85
- /** Z-index ноды был изменён */
395
+ /** Node z-index was changed */
86
396
  'node:zIndexChanged': [node: BaseNode, oldIndex: number, newIndex: number];
87
- /** Группа была создана */
397
+ /** Group was created */
88
398
  'group:created': [group: BaseNode, nodes: BaseNode[]];
89
- /** Группа была разгруппирована */
399
+ /** Group was ungrouped */
90
400
  'group:ungrouped': [group: BaseNode, nodes: BaseNode[]];
91
- /** Множественное выделение создано */
401
+ /** Multi-selection was created */
92
402
  'selection:multi:created': [nodes: BaseNode[]];
93
- /** Множественное выделение уничтожено */
403
+ /** Multi-selection was destroyed */
94
404
  'selection:multi:destroyed': [];
95
- /** Выделение полностью снято */
405
+ /** Selection was completely cleared */
96
406
  'selection:cleared': [];
97
- /** Ноды были скопированы в буфер обмена */
407
+ /** Nodes were copied to clipboard */
98
408
  'clipboard:copy': [nodes: BaseNode[]];
99
- /** Ноды были вырезаны в буфер обмена */
409
+ /** Nodes were cut to clipboard */
100
410
  'clipboard:cut': [nodes: BaseNode[]];
101
- /** Ноды были вставлены из буфера обмена */
411
+ /** Nodes were pasted from clipboard */
102
412
  'clipboard:paste': [nodes: BaseNode[]];
103
- /** Зум был изменён программно */
413
+ /** Zoom was changed programmatically */
104
414
  'camera:setZoom': [{
105
415
  scale: number;
106
416
  }];
107
- /** Зум был изменён пользователем (колесо мыши) */
417
+ /** Zoom was changed by user (mouse wheel) */
108
418
  'camera:zoom': [{
109
419
  scale: number;
110
420
  position: {
@@ -112,17 +422,17 @@ interface CoreEvents {
112
422
  y: number;
113
423
  };
114
424
  }];
115
- /** Камера была сброшена */
425
+ /** Camera was reset */
116
426
  'camera:reset': [];
117
- /** Шаг зума был изменён */
427
+ /** Zoom step was changed */
118
428
  'camera:zoomStep': [{
119
429
  zoomStep: number;
120
430
  }];
121
- /** Шаг панорамирования был изменён */
431
+ /** Pan step was changed */
122
432
  'camera:panStep': [{
123
433
  panStep: number;
124
434
  }];
125
- /** Камера была перемещена (панорамирование) */
435
+ /** Camera was moved (panning) */
126
436
  'camera:pan': [{
127
437
  dx: number;
128
438
  dy: number;
@@ -131,50 +441,191 @@ interface CoreEvents {
131
441
  y: number;
132
442
  };
133
443
  }];
134
- /** Плагин был добавлен */
444
+ /** Plugin was added */
135
445
  'plugin:added': [pluginName: string];
136
- /** Плагин был удалён */
446
+ /** Plugin was removed */
137
447
  'plugin:removed': [pluginName: string];
138
- /** Stage был изменён (resize) */
448
+ /** Stage was resized */
139
449
  'stage:resized': [{
140
450
  width: number;
141
451
  height: number;
142
452
  }];
143
453
  }
144
454
 
145
- interface TextNodeOptions extends BaseNodeOptions {
146
- text?: string;
147
- fontSize?: number;
148
- fontFamily?: string;
149
- fontStyle?: string;
455
+ interface CameraManagerOptions {
456
+ stage: Konva.Stage;
457
+ eventBus: EventBus<CoreEvents>;
458
+ target?: Konva.Node;
459
+ initialScale?: number;
460
+ minScale?: number;
461
+ maxScale?: number;
462
+ draggable?: boolean;
463
+ zoomStep?: number;
464
+ panStep?: number;
465
+ }
466
+ declare class CameraManager {
467
+ private _stage;
468
+ private _eventBus;
469
+ private _target;
470
+ private _scale;
471
+ private _minScale;
472
+ private _maxScale;
473
+ private _zoomStep;
474
+ private _panStep;
475
+ private _wheelScheduled;
476
+ private _pendingWheelEvent;
477
+ constructor(options: CameraManagerOptions);
478
+ private _initWheelZoom;
479
+ private _handleWheel;
480
+ get zoomStep(): number;
481
+ get panStep(): number;
482
+ setZoom(zoom: number): void;
483
+ zoomIn(step?: number): void;
484
+ zoomOut(step?: number): void;
485
+ reset(): void;
486
+ setDraggable(enabled: boolean): void;
487
+ setZoomStep(step: number): void;
488
+ setPanStep(step: number): void;
489
+ }
490
+
491
+ interface ArcNodeOptions extends BaseNodeOptions {
492
+ innerRadius?: number;
493
+ outerRadius?: number;
494
+ angle?: number;
495
+ rotation?: number;
496
+ clockwise?: boolean;
150
497
  fill?: string;
151
- align?: 'left' | 'center' | 'right';
152
- padding?: number;
498
+ stroke?: string;
499
+ strokeWidth?: number;
153
500
  }
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;
501
+
502
+ interface ArrowNodeOptions extends BaseNodeOptions {
503
+ points?: number[];
504
+ tension?: number;
505
+ pointerLength?: number;
506
+ pointerWidth?: number;
507
+ pointerAtBeginning?: boolean;
508
+ pointerAtEnding?: boolean;
509
+ fill?: string;
510
+ stroke?: string;
511
+ strokeWidth?: number;
512
+ }
513
+
514
+ interface CircleNodeOptions extends BaseNodeOptions {
515
+ radius?: number;
516
+ fill?: string;
517
+ stroke?: string;
518
+ strokeWidth?: number;
519
+ }
520
+
521
+ interface EllipseNodeOptions extends BaseNodeOptions {
522
+ radiusX?: number;
523
+ radiusY?: number;
524
+ fill?: string;
525
+ stroke?: string;
526
+ strokeWidth?: number;
527
+ }
528
+
529
+ interface MediaPlaceholderOptions {
530
+ text: string;
531
+ textColor: string;
532
+ font: string;
533
+ backgroundColor: string;
534
+ borderColor: string;
535
+ baseSpinnerColor: string;
536
+ accentSpinnerColor: string;
537
+ lineWidth: number;
538
+ fallbackWidth: number;
539
+ fallbackHeight: number;
540
+ }
541
+ declare class MediaPlaceholder {
542
+ private readonly _node;
543
+ private _options;
544
+ private _rafId;
545
+ private _startTime;
546
+ private _lastDrawTime;
547
+ private _canvas;
548
+ private _ctx;
549
+ private _logicalWidth;
550
+ private _logicalHeight;
551
+ private _pixelRatio;
552
+ private readonly _maxPixelRatio;
553
+ private readonly _maxCanvasPixels;
554
+ private readonly _targetFps;
555
+ constructor(node: Konva.Image, options?: Partial<MediaPlaceholderOptions>);
556
+ setOptions(options: Partial<MediaPlaceholderOptions>): void;
557
+ start(): void;
558
+ stop(): void;
559
+ private _tick;
560
+ private _getPixelRatio;
561
+ private _draw;
562
+ }
563
+
564
+ declare global {
565
+ interface Window {
566
+ gifler?: (url: string) => GiflerInstance;
567
+ }
568
+ interface GiflerInstance {
569
+ frames: (canvas: HTMLCanvasElement, onDrawFrame: (ctx: CanvasRenderingContext2D, frame: GiflerFrame) => void) => void;
570
+ }
571
+ interface GiflerFrame {
572
+ width: number;
573
+ height: number;
574
+ buffer: HTMLCanvasElement;
575
+ }
576
+ }
577
+ interface GifNodeOptions extends BaseNodeOptions {
578
+ src?: string;
579
+ width?: number;
580
+ height?: number;
581
+ cornerRadius?: number | number[];
582
+ autoplay?: boolean;
583
+ placeholder?: Partial<MediaPlaceholderOptions>;
584
+ onLoad?: (node: GifNode) => void;
585
+ onError?: (error: Error) => void;
586
+ onFrame?: (node: GifNode, frameIndex: number) => void;
587
+ }
588
+ declare class GifNode extends BaseNode<Konva.Image> {
589
+ private _canvas;
590
+ private _placeholder;
591
+ private _isLoaded;
592
+ private _isPlaying;
593
+ private _giflerLoaded;
594
+ private _frameIndex;
595
+ constructor(options?: GifNodeOptions);
596
+ setSrc(url: string, options?: Omit<GifNodeOptions, 'src' | 'x' | 'y' | 'width' | 'height'>): Promise<this>;
597
+ play(): this;
598
+ pause(): this;
599
+ isPlaying(): boolean;
600
+ isLoaded(): boolean;
601
+ getFrameIndex(): number;
602
+ getCanvas(): HTMLCanvasElement | null;
163
603
  getSize(): {
164
604
  width: number;
165
605
  height: number;
166
606
  };
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
607
  setSize({ width, height }: {
175
608
  width: number;
176
609
  height: number;
177
610
  }): this;
611
+ setCornerRadius(radius: number | number[]): this;
612
+ getCornerRadius(): number;
613
+ setOpacity(opacity: number): this;
614
+ getOpacity(): number;
615
+ private _ensureGiflerLibrary;
616
+ private _cleanup;
617
+ remove(): void;
618
+ }
619
+
620
+ interface GroupNodeOptions extends BaseNodeOptions {
621
+ draggable?: boolean;
622
+ listening?: boolean;
623
+ clip?: {
624
+ x: number;
625
+ y: number;
626
+ width: number;
627
+ height: number;
628
+ };
178
629
  }
179
630
 
180
631
  type ImageSource = HTMLImageElement;
@@ -183,8 +634,11 @@ interface ImageNodeOptions extends BaseNodeOptions {
183
634
  src?: string;
184
635
  width?: number;
185
636
  height?: number;
637
+ cornerRadius?: number | number[];
638
+ placeholder?: Partial<MediaPlaceholderOptions>;
186
639
  }
187
640
  declare class ImageNode extends BaseNode<Konva.Image> {
641
+ private _placeholder;
188
642
  constructor(options?: ImageNodeOptions);
189
643
  getSize(): {
190
644
  width: number;
@@ -203,123 +657,48 @@ declare class ImageNode extends BaseNode<Konva.Image> {
203
657
  width: number;
204
658
  height: number;
205
659
  }): this;
660
+ setCornerRadius(radius: number | number[]): this;
661
+ getCornerRadius(): number;
206
662
  private _loadHTMLImage;
663
+ remove(): void;
207
664
  }
208
665
 
209
- interface CircleNodeOptions extends BaseNodeOptions {
666
+ interface RegularPolygonNodeOptions extends BaseNodeOptions {
667
+ sides?: number;
210
668
  radius?: number;
211
669
  fill?: string;
212
670
  stroke?: string;
213
671
  strokeWidth?: number;
214
672
  }
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
-
227
- interface EllipseNodeOptions extends BaseNodeOptions {
228
- radiusX?: number;
229
- radiusY?: number;
230
- fill?: string;
231
- stroke?: string;
232
- strokeWidth?: number;
233
- }
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
673
 
248
- interface ArcNodeOptions extends BaseNodeOptions {
674
+ interface RingNodeOptions extends BaseNodeOptions {
249
675
  innerRadius?: number;
250
676
  outerRadius?: number;
251
- angle?: number;
252
- rotation?: number;
253
- clockwise?: boolean;
254
677
  fill?: string;
255
678
  stroke?: string;
256
679
  strokeWidth?: number;
257
680
  }
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;
272
- }
273
681
 
274
- interface ArrowNodeOptions extends BaseNodeOptions {
275
- points?: number[];
276
- tension?: number;
277
- pointerLength?: number;
278
- pointerWidth?: number;
279
- pointerAtBeginning?: boolean;
280
- pointerAtEnding?: boolean;
682
+ interface ShapeNodeOptions extends BaseNodeOptions {
281
683
  fill?: string;
282
684
  stroke?: string;
283
685
  strokeWidth?: number;
686
+ cornerRadius?: number | number[];
284
687
  }
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;
688
+ declare class ShapeNode extends BaseNode<Konva.Rect> {
689
+ constructor(options: ShapeNodeOptions);
299
690
  setFill(color: string): this;
300
691
  setStroke(color: string): this;
301
692
  setStrokeWidth(width: number): this;
302
- }
303
-
304
- interface RegularPolygonNodeOptions extends BaseNodeOptions {
305
- sides?: number;
306
- radius?: number;
307
- fill?: string;
308
- stroke?: string;
309
- strokeWidth?: number;
310
- }
311
- declare class RegularPolygonNode extends BaseNode<Konva.RegularPolygon> {
312
- constructor(options?: RegularPolygonNodeOptions);
313
- getSides(): number;
314
- getRadius(): number;
693
+ setCornerRadius(radius: number | number[]): this;
694
+ setSize({ width, height }: {
695
+ width: number;
696
+ height: number;
697
+ }): this;
315
698
  getFill(): string | undefined;
316
699
  getStroke(): string | undefined;
317
700
  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;
701
+ getCornerRadius(): number;
323
702
  }
324
703
 
325
704
  interface StarNodeOptions extends BaseNodeOptions {
@@ -330,70 +709,184 @@ interface StarNodeOptions extends BaseNodeOptions {
330
709
  stroke?: string;
331
710
  strokeWidth?: number;
332
711
  }
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;
344
- setFill(color: string): this;
345
- setStroke(color: string): this;
346
- setStrokeWidth(width: number): this;
712
+
713
+ interface SvgNodeOptions extends BaseNodeOptions {
714
+ src?: string;
715
+ width?: number;
716
+ height?: number;
717
+ cornerRadius?: number | number[];
718
+ placeholder?: Partial<MediaPlaceholderOptions>;
719
+ onLoad?: (node: SvgNode) => void;
720
+ onError?: (error: Error) => void;
721
+ }
722
+ declare class SvgNode extends BaseNode<Konva.Image> {
723
+ private _isLoading;
724
+ private _isLoaded;
725
+ private _placeholder;
726
+ constructor(options?: SvgNodeOptions);
727
+ isLoading(): boolean;
728
+ isLoaded(): boolean;
729
+ getSize(): {
730
+ width: number;
731
+ height: number;
732
+ };
733
+ setSrc(url: string, onLoad?: (node: SvgNode) => void, onError?: (error: Error) => void): Promise<this>;
734
+ setSize({ width, height }: {
735
+ width: number;
736
+ height: number;
737
+ }): this;
738
+ setCornerRadius(radius: number | number[]): this;
739
+ getCornerRadius(): number;
740
+ setOpacity(opacity: number): this;
741
+ getOpacity(): number;
742
+ remove(): void;
347
743
  }
348
744
 
349
- interface RingNodeOptions extends BaseNodeOptions {
350
- innerRadius?: number;
351
- outerRadius?: number;
745
+ interface TextNodeOptions extends BaseNodeOptions {
746
+ text?: string;
747
+ fontSize?: number;
748
+ fontFamily?: string;
749
+ fontStyle?: string;
352
750
  fill?: string;
353
- stroke?: string;
354
- strokeWidth?: number;
751
+ align?: 'left' | 'center' | 'right';
752
+ verticalAlign?: 'top' | 'middle' | 'bottom';
753
+ padding?: number;
754
+ lineHeight?: number;
755
+ /** Включить редактирование по двойному клику (по умолчанию true) */
756
+ editable?: boolean;
355
757
  }
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;
758
+ /** Событие изменения текста */
759
+ interface TextChangeEvent {
760
+ /** Предыдущий текст */
761
+ oldText: string;
762
+ /** Новый текст */
763
+ newText: string;
764
+ /** Было ли изменение отменено (Escape) */
765
+ cancelled: boolean;
766
+ }
767
+ declare class TextNode extends BaseNode<Konva.Text> {
768
+ /** Включено ли редактирование по двойному клику */
769
+ private _editable;
770
+ /** Находится ли нода в режиме редактирования */
771
+ private _isEditing;
772
+ /** Текущий textarea элемент */
773
+ private _textarea;
774
+ /** Колбэки для событий редактирования */
775
+ private _onTextChangeCallbacks;
776
+ private _onEditStartCallbacks;
777
+ private _onEditEndCallbacks;
778
+ /** Флаг: ожидание повторного двойного клика для входа в редактирование внутри группы */
779
+ private _pendingGroupEditDblClick;
780
+ private _groupEditClickResetAttached;
781
+ constructor(options?: TextNodeOptions);
782
+ getText(): string;
783
+ setText(text: string): this;
784
+ setFontSize(size: number): this;
785
+ setFontFamily(family: string): this;
365
786
  setFill(color: string): this;
366
- setStroke(color: string): this;
367
- setStrokeWidth(width: number): this;
787
+ setAlign(align: 'left' | 'center' | 'right'): this;
788
+ setPadding(padding: number): this;
789
+ setSize({ width, height }: {
790
+ width: number;
791
+ height: number;
792
+ }): this;
793
+ setLineHeight(lineHeight: number): this;
794
+ setVerticalAlign(align: 'top' | 'middle' | 'bottom'): this;
795
+ isEditable(): boolean;
796
+ setEditable(editable: boolean): this;
797
+ isEditing(): boolean;
798
+ startEdit(): void;
799
+ finishEdit(): void;
800
+ cancelEdit(): void;
801
+ onTextChange(cb: (event: TextChangeEvent) => void): this;
802
+ offTextChange(cb: (event: TextChangeEvent) => void): this;
803
+ onEditStart(cb: () => void): this;
804
+ offEditStart(cb: () => void): this;
805
+ onEditEnd(cb: () => void): this;
806
+ offEditEnd(cb: () => void): this;
807
+ private _setupEditHandler;
808
+ /**
809
+ * При трансформации (resize) «запекаем» scaleX/scaleY в width/height,
810
+ * а затем сбрасываем scale обратно в 1.
811
+ *
812
+ * В итоге:
813
+ * - рамка может свободно менять ширину/высоту и по диагонали;
814
+ * - сам текст не растягивается, т.к. fontSize не меняется, а scale всегда 1.
815
+ */
816
+ private _setupTransformHandler;
817
+ private _oldText;
818
+ private _keyHandler;
819
+ private _clickHandler;
820
+ private _syncTextareaRafId;
821
+ private _inputHandler;
822
+ private _prevWrap;
823
+ private _getWrappedLineCount;
824
+ private _ensureHeightFitsWrappedText;
825
+ private _ensureWidthFitsText;
826
+ private _syncNodeSizeFromTextarea;
827
+ private _syncTextareaPosition;
828
+ private _openTextarea;
829
+ private _saveAndClose;
368
830
  }
369
831
 
370
- interface GroupNodeOptions extends BaseNodeOptions {
371
- draggable?: boolean;
372
- listening?: boolean;
373
- clip?: {
374
- x: number;
375
- y: number;
832
+ interface VideoNodeOptions extends BaseNodeOptions {
833
+ src?: string;
834
+ width?: number;
835
+ height?: number;
836
+ cornerRadius?: number | number[];
837
+ autoplay?: boolean;
838
+ loop?: boolean;
839
+ muted?: boolean;
840
+ currentTime?: number;
841
+ volume?: number;
842
+ playbackRate?: number;
843
+ placeholder?: Partial<MediaPlaceholderOptions>;
844
+ onLoadedMetadata?: (node: VideoNode, video: HTMLVideoElement) => void;
845
+ onError?: (error: Error) => void;
846
+ onPlay?: (node: VideoNode) => void;
847
+ onPause?: (node: VideoNode) => void;
848
+ onEnded?: (node: VideoNode) => void;
849
+ }
850
+ declare class VideoNode extends BaseNode<Konva.Image> {
851
+ private _videoElement;
852
+ private _animation;
853
+ private _placeholder;
854
+ private _isPlaying;
855
+ private _isLoaded;
856
+ constructor(options?: VideoNodeOptions);
857
+ setSrc(url: string, options?: Omit<VideoNodeOptions, 'src' | 'x' | 'y' | 'width' | 'height'>): Promise<this>;
858
+ play(): Promise<this>;
859
+ pause(): this;
860
+ stop(): this;
861
+ setCurrentTime(time: number): this;
862
+ getCurrentTime(): number;
863
+ getDuration(): number;
864
+ setVolume(volume: number): this;
865
+ getVolume(): number;
866
+ setMuted(muted: boolean): this;
867
+ isMuted(): boolean;
868
+ setLoop(loop: boolean): this;
869
+ isLoop(): boolean;
870
+ setPlaybackRate(rate: number): this;
871
+ getPlaybackRate(): number;
872
+ isPlaying(): boolean;
873
+ isLoaded(): boolean;
874
+ getVideoElement(): HTMLVideoElement | null;
875
+ getSize(): {
376
876
  width: number;
377
877
  height: number;
378
878
  };
379
- }
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;
879
+ setSize({ width, height }: {
394
880
  width: number;
395
881
  height: number;
396
882
  }): this;
883
+ setCornerRadius(radius: number | number[]): this;
884
+ getCornerRadius(): number;
885
+ setOpacity(opacity: number): this;
886
+ getOpacity(): number;
887
+ private _ensureAnimation;
888
+ private _cleanup;
889
+ remove(): void;
397
890
  }
398
891
 
399
892
  declare class NodeManager {
@@ -410,17 +903,20 @@ declare class NodeManager {
410
903
  get world(): Konva.Group;
411
904
  get stage(): Konva.Stage;
412
905
  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;
906
+ addShape(options: ShapeNodeOptions): ShapeNodeHandle;
907
+ addText(options: TextNodeOptions): TextNodeHandle;
908
+ addImage(options: ImageNodeOptions): ImageNodeHandle;
909
+ addCircle(options: CircleNodeOptions): CircleNodeHandle;
910
+ addEllipse(options: EllipseNodeOptions): EllipseNodeHandle;
911
+ addArc(options: ArcNodeOptions): ArcNodeHandle;
912
+ addStar(options: StarNodeOptions): StarNodeHandle;
913
+ addArrow(options: ArrowNodeOptions): ArrowNodeHandle;
914
+ addRing(options: RingNodeOptions): RingNodeHandle;
915
+ addRegularPolygon(options: RegularPolygonNodeOptions): RegularPolygonNodeHandle;
916
+ addGroup(options: GroupNodeOptions): GroupNodeHandle;
917
+ addSvg(options: SvgNodeOptions): SvgNodeHandle;
918
+ addVideo(options: VideoNodeOptions): VideoNodeHandle;
919
+ addGif(options: GifNodeOptions): GifNodeHandle;
424
920
  remove(node: BaseNode): void;
425
921
  findById(id: string): BaseNode | undefined;
426
922
  list(): BaseNode[];
@@ -431,42 +927,6 @@ declare class NodeManager {
431
927
  private _scheduleBatchDraw;
432
928
  }
433
929
 
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
930
  interface LODLevel {
471
931
  minScale: number;
472
932
  maxScale: number;
@@ -670,11 +1130,61 @@ declare class VirtualizationManager {
670
1130
  destroy(): void;
671
1131
  }
672
1132
 
673
- declare abstract class Plugin {
674
- protected abstract onAttach(core: CoreEngine): void;
675
- protected abstract onDetach(core: CoreEngine): void;
676
- attach(core: CoreEngine): void;
677
- detach(core: CoreEngine): void;
1133
+ /**
1134
+ * Base class for a plugin addon.
1135
+ *
1136
+ * Usage:
1137
+ * class MyPluginAddon extends PluginAddon<MyPlugin> {
1138
+ * protected onAttach(plugin: MyPlugin, core: CoreEngine) { ... }
1139
+ * protected onDetach(plugin: MyPlugin, core: CoreEngine) { ... }
1140
+ * }
1141
+ */
1142
+ declare abstract class PluginAddon<TPlugin extends Plugin = Plugin> {
1143
+ protected abstract onAttach(plugin: TPlugin, core: CoreEngine): void;
1144
+ protected abstract onDetach(plugin: TPlugin, core: CoreEngine): void;
1145
+ /** Internal helper: called by the plugin's addon manager */
1146
+ attach(plugin: TPlugin, core: CoreEngine): void;
1147
+ /** Internal helper: called by the plugin's addon manager */
1148
+ detach(plugin: TPlugin, core: CoreEngine): void;
1149
+ }
1150
+
1151
+ /**
1152
+ * Addon manager for a specific plugin.
1153
+ * Allows adding/removing addons with a convenient API:
1154
+ * plugin.addons.add(addon)
1155
+ * plugin.addons.add([a, b])
1156
+ * plugin.addons.remove(addon)
1157
+ * plugin.addons.list()
1158
+ */
1159
+ declare class PluginAddons<TPlugin extends Plugin = Plugin> {
1160
+ private readonly _plugin;
1161
+ private readonly _addons;
1162
+ private _core;
1163
+ constructor(plugin: TPlugin);
1164
+ /** Internal helper: called from Plugin.attach */
1165
+ _attachAll(core: CoreEngine): void;
1166
+ /** Internal helper: called from Plugin.detach */
1167
+ _detachAll(core: CoreEngine): void;
1168
+ /** Attach one or more addons to the plugin */
1169
+ add(addons: PluginAddon<TPlugin> | PluginAddon<TPlugin>[]): TPlugin;
1170
+ /** Detach one or more addons from the plugin */
1171
+ remove(addons: PluginAddon<TPlugin> | PluginAddon<TPlugin>[]): TPlugin;
1172
+ /** All attached addons (array copy) */
1173
+ list(): PluginAddon<TPlugin>[];
1174
+ /** Check if a specific addon is attached */
1175
+ has(addon: PluginAddon<TPlugin>): boolean;
1176
+ /** Detach and clear all addons (used when removing the plugin) */
1177
+ clear(): void;
1178
+ }
1179
+
1180
+ declare abstract class Plugin {
1181
+ /** Local addons attached to this plugin */
1182
+ readonly addons: PluginAddons<this>;
1183
+ constructor();
1184
+ protected abstract onAttach(core: CoreEngine): void;
1185
+ protected abstract onDetach(core: CoreEngine): void;
1186
+ attach(core: CoreEngine): void;
1187
+ detach(core: CoreEngine): void;
678
1188
  }
679
1189
 
680
1190
  declare class Plugins {
@@ -715,12 +1225,25 @@ declare class CoreEngine {
715
1225
  private _minScale;
716
1226
  private _maxScale;
717
1227
  private _gridLayer;
1228
+ private _resizeObserver;
718
1229
  readonly container: HTMLDivElement;
719
1230
  readonly nodes: NodeManager;
720
1231
  readonly camera: CameraManager;
721
1232
  readonly virtualization: VirtualizationManager;
722
1233
  readonly plugins: Plugins;
723
1234
  constructor(options: CoreEngineOptions);
1235
+ /**
1236
+ * Setup automatic canvas resize when container size changes
1237
+ */
1238
+ private _setupAutoResize;
1239
+ /**
1240
+ * Handle container resize
1241
+ */
1242
+ private _handleResize;
1243
+ /**
1244
+ * Cleanup resources
1245
+ */
1246
+ destroy(): void;
724
1247
  get eventBus(): EventBus<CoreEvents>;
725
1248
  get stage(): Konva.Stage;
726
1249
  get gridLayer(): Konva.Layer;
@@ -737,34 +1260,152 @@ declare class CoreEngine {
737
1260
  }): void;
738
1261
  setBackgroundColor(color: string): void;
739
1262
  setDraggable(draggable: boolean): void;
1263
+ /**
1264
+ * Enable or disable auto-resize
1265
+ */
1266
+ setAutoResize(enabled: boolean): void;
740
1267
  }
741
1268
 
742
- interface LogoOptions {
743
- src: string;
744
- width: number;
745
- height: number;
746
- opacity?: number;
1269
+ /**
1270
+ * HistoryAction — description of a single action in history
1271
+ *
1272
+ * Action types:
1273
+ * - 'create' — node creation (before: null, after: SerializedNodeState)
1274
+ * - 'remove' — node removal (before: SerializedNodeState, after: null)
1275
+ * - 'transform' — transformation (before/after: TransformState)
1276
+ * - 'zIndex' — z-index change (before/after: { zIndex: number })
1277
+ * - 'group' — grouping (before: { childIds, childStates }, after: SerializedNodeState)
1278
+ * - 'ungroup' — ungrouping (before: SerializedNodeState, after: { childStates })
1279
+ * - 'batch' — composite action (children: HistoryAction[])
1280
+ */
1281
+ interface HistoryAction {
1282
+ /** Action type: 'create', 'remove', 'transform', 'zIndex', 'group', 'ungroup', 'batch' */
1283
+ type: string;
1284
+ /** ID of the node this action relates to (empty string for batch) */
1285
+ nodeId: string;
1286
+ /** State BEFORE the action (null for create, not used for batch) */
1287
+ before: unknown;
1288
+ /** State AFTER the action (null for remove, not used for batch) */
1289
+ after: unknown;
1290
+ /** Action timestamp */
1291
+ timestamp: number;
1292
+ /** Child actions for batch */
1293
+ children?: HistoryAction[];
747
1294
  }
748
- declare class LogoPlugin extends Plugin {
1295
+ /**
1296
+ * HistoryManager — action history manager for Undo/Redo
1297
+ *
1298
+ * Logic:
1299
+ * - push(action) adds an action to history
1300
+ * - If currentIndex < length-1, actions after currentIndex are removed first
1301
+ * - select(index) switches current index without removing history
1302
+ * - undo() returns current action and moves index backward
1303
+ * - redo() moves index forward and returns the action
1304
+ */
1305
+ declare class HistoryManager {
1306
+ private _actions;
1307
+ private _currentIndex;
1308
+ /**
1309
+ * Add an action to history.
1310
+ * If currentIndex < length-1, removes actions after currentIndex first.
1311
+ */
1312
+ push(action: HistoryAction): void;
1313
+ /**
1314
+ * Remove actions from fromIndex to the end of the array.
1315
+ */
1316
+ pop(fromIndex: number): void;
1317
+ /**
1318
+ * Switch current index (for history navigation).
1319
+ * Does not remove actions from history.
1320
+ */
1321
+ select(index: number): void;
1322
+ /**
1323
+ * Get current index in history.
1324
+ * -1 means we are "before" the first action (empty state).
1325
+ */
1326
+ getCurrentIndex(): number;
1327
+ /**
1328
+ * Number of actions in history.
1329
+ */
1330
+ get length(): number;
1331
+ /**
1332
+ * Get a copy of all actions array.
1333
+ */
1334
+ getActions(): HistoryAction[];
1335
+ /**
1336
+ * Can undo be performed (are there actions to roll back).
1337
+ */
1338
+ canUndo(): boolean;
1339
+ /**
1340
+ * Can redo be performed (are there actions to repeat).
1341
+ */
1342
+ canRedo(): boolean;
1343
+ /**
1344
+ * Perform undo: return current action and move index backward.
1345
+ * Returns the action to roll back (apply before).
1346
+ */
1347
+ undo(): HistoryAction | null;
1348
+ /**
1349
+ * Perform redo: move index forward and return the action.
1350
+ * Returns the action to repeat (apply after).
1351
+ */
1352
+ redo(): HistoryAction | null;
1353
+ /**
1354
+ * Clear all history.
1355
+ */
1356
+ clear(): void;
1357
+ /**
1358
+ * Get action by index (without changing currentIndex).
1359
+ */
1360
+ getAction(index: number): HistoryAction | null;
1361
+ /**
1362
+ * Debug output of history state.
1363
+ */
1364
+ private _debug;
1365
+ }
1366
+
1367
+ interface AreaSelectionPluginOptions {
1368
+ rectStroke?: string;
1369
+ rectFill?: string;
1370
+ rectStrokeWidth?: number;
1371
+ rectOpacity?: number;
1372
+ enableKeyboardShortcuts?: boolean;
1373
+ }
1374
+ /**
1375
+ * AreaSelectionPlugin
1376
+ * - Drag LKM over empty space draws selection rectangle (marquee) in screen coordinates
1377
+ * - All nodes whose client rectangles intersect the rectangle are temporarily grouped
1378
+ * - Click outside — temporary group is removed, nodes return to their original positions
1379
+ * - Ctrl+G — lock in permanent group (GroupNode through NodeManager)
1380
+ * - Ctrl+Shift+G — unlock selected permanent group
1381
+ */
1382
+ declare class AreaSelectionPlugin extends Plugin {
749
1383
  private _core?;
750
- private _layer?;
751
- private _image?;
752
- private _src;
753
- private _width;
754
- private _height;
755
- private _opacity;
756
- constructor(options: LogoOptions);
1384
+ private _layer;
1385
+ private _rect;
1386
+ private _start;
1387
+ private _transformer;
1388
+ private _selecting;
1389
+ private _skipNextClick;
1390
+ private _lastPickedBaseNodes;
1391
+ private _autoPanRafId;
1392
+ private _autoPanActive;
1393
+ private _autoPanEdgePx;
1394
+ private _autoPanMaxSpeedPx;
1395
+ private _options;
1396
+ constructor(options?: AreaSelectionPluginOptions);
757
1397
  protected onAttach(core: CoreEngine): void;
758
1398
  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;
1399
+ private _startAutoPanLoop;
1400
+ private _stopAutoPanLoop;
1401
+ private _finalizeArea;
1402
+ private _clearSelection;
1403
+ private _getSelectionPlugin;
1404
+ private _rectsIntersect;
1405
+ private _findOwningGroupBaseNode;
1406
+ private _isAncestor;
1407
+ private _isPermanentGroupSelected;
1408
+ private _currentGroupNode;
768
1409
  }
769
1410
 
770
1411
  interface CameraHotkeysOptions {
@@ -802,295 +1443,297 @@ declare class CameraHotkeysPlugin extends Plugin {
802
1443
  private _pan;
803
1444
  }
804
1445
 
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;
1446
+ interface ContentFromClipboardPluginOptions {
1447
+ target?: Window | Document | HTMLElement | EventTarget;
1448
+ ignoreEditableTargets?: boolean;
1449
+ maxImageSize?: number;
1450
+ enableDragDrop?: boolean;
1451
+ }
1452
+ declare class ContentFromClipboardPlugin extends Plugin {
1453
+ private _core?;
1454
+ private _options;
1455
+ private _dragDropAddon;
1456
+ constructor(options?: ContentFromClipboardPluginOptions);
1457
+ protected onAttach(core: CoreEngine): void;
1458
+ protected onDetach(_core: CoreEngine): void;
1459
+ private _onPaste;
1460
+ private _handlePaste;
1461
+ handleDrop(e: DragEvent): Promise<void>;
1462
+ private _handleDataTransfer;
1463
+ private _pasteSvgText;
1464
+ private _pasteSvgFile;
1465
+ private _pasteImageFile;
1466
+ private _pasteVideoFile;
1467
+ private _fitIntoMaxSize;
1468
+ private _isEditableTarget;
1469
+ private _getPastePosition;
1470
+ private _isPointerOnScreen;
1471
+ private _getScreenCenter;
1472
+ private _getAsString;
1473
+ private _looksLikeVideoFile;
1474
+ private _htmlToText;
1475
+ private _readFileAsText;
1476
+ private _extractSvgMarkup;
1477
+ private _svgTextToSrc;
1478
+ private _parseSvgNumber;
1479
+ private _extractSvgSize;
1480
+ private _loadImageSize;
1481
+ }
1482
+
1483
+ interface GridPluginOptions {
1484
+ stepX?: number;
1485
+ stepY?: number;
1486
+ color?: string;
1487
+ lineWidth?: number;
1488
+ visible?: boolean;
1489
+ minScaleToShow?: number | null;
1490
+ enableSnap?: boolean;
813
1491
  }
814
1492
  /**
815
- * MultiGroupControllerthin 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.
1493
+ * GridPlugindraws a grid and implements snap to grid on drag/resize.
1494
+ * Architecture is identical to other plugins: onAttach/onDetach, own layer with Konva.Shape.
1495
+ *
1496
+ * Important points of the current architecture:
1497
+ * - Panning/scale is performed by Stage transformations.
1498
+ * - Nodes are placed on the NodeManager layer (core.nodes.layer), also Transformers are added to it.
818
1499
  */
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;
1500
+ declare class GridPlugin extends Plugin {
1501
+ private _core?;
1502
+ private _layer;
1503
+ private _shape;
1504
+ private _stepX;
1505
+ private _stepY;
1506
+ private _color;
1507
+ private _lineWidth;
1508
+ private _visible;
1509
+ private _minScaleToShow;
1510
+ private _enableSnap;
1511
+ private _dragMoveHandler;
1512
+ private _nodesAddHandler;
1513
+ private _nodesRemoveHandler;
1514
+ private _redrawScheduled;
1515
+ private _transformersCache;
1516
+ private _cacheInvalidated;
1517
+ constructor(options?: GridPluginOptions);
1518
+ protected onAttach(core: CoreEngine): void;
1519
+ /**
1520
+ * Deferred redraw (throttling)
1521
+ */
1522
+ private _scheduleRedraw;
1523
+ protected onDetach(core: CoreEngine): void;
1524
+ setVisible(visible: boolean): void;
1525
+ get stepX(): number;
1526
+ get stepY(): number;
1527
+ get minScaleToShow(): number | null;
1528
+ setStep(stepX: number, stepY: number): void;
1529
+ setMinScaleToShow(value: number | null): void;
1530
+ setSnap(enabled: boolean): void;
829
1531
  }
830
1532
 
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;
1533
+ interface HistoryPluginOptions {
1534
+ /** DOM target для прослушивания клавиш (по умолчанию globalThis) */
1535
+ target?: Window | Document | HTMLElement | EventTarget;
1536
+ /** Игнорировать хоткеи, если фокус на редактируемом элементе */
1537
+ ignoreEditableTargets?: boolean;
1538
+ /** Максимальное количество действий в истории (0 = без лимита) */
1539
+ maxHistoryLength?: number;
839
1540
  }
840
1541
  /**
841
- * Universal selection and dragging plugin for nodes compatible with BaseNode.
1542
+ * HistoryPlugin плагин для Undo/Redo функциональности
842
1543
  *
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)
1544
+ * Хоткеи:
1545
+ * - Ctrl+Z / Cmd+Z Undo
1546
+ * - Ctrl+Shift+Z / Cmd+Shift+Z Redo
1547
+ *
1548
+ * Автоматически записывает в историю:
1549
+ * - Создание нод (node:created)
1550
+ * - Удаление нод (node:removed)
1551
+ * - Трансформации (node:transformed)
1552
+ * - Изменение z-index (node:zIndexChanged)
1553
+ * - Создание групп (group:created)
1554
+ * - Разгруппировка (group:ungrouped)
848
1555
  */
849
- declare class SelectionPlugin extends Plugin {
1556
+ declare class HistoryPlugin extends Plugin {
850
1557
  private _core?;
1558
+ private _history;
851
1559
  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;
1560
+ /** Флаг для предотвращения записи в историю при undo/redo */
1561
+ private _isUndoRedoInProgress;
1562
+ /** Кэш состояний нод ДО начала drag/transform */
1563
+ private _dragStartStateCache;
1564
+ /** Флаг batch-режима для временной группы */
1565
+ private _isBatchMode;
1566
+ /** Буфер действий для batch */
1567
+ private _batchBuffer;
1568
+ /** Таймер для автоматического завершения batch */
1569
+ private _batchCommitTimer;
1570
+ constructor(options?: HistoryPluginOptions);
895
1571
  /**
896
- * Deferred redraw (throttling)
897
- * Groups multiple batchDraw calls into one
1572
+ * Получить менеджер истории для внешнего доступа
898
1573
  */
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;
1574
+ getHistoryManager(): HistoryManager;
1575
+ /**
1576
+ * Начать batch-режим для группировки нескольких действий
1577
+ */
1578
+ startBatch(): void;
1579
+ /**
1580
+ * Завершить batch-режим (вызывается извне после эмита всех событий)
1581
+ */
1582
+ commitBatch(): void;
1583
+ /**
1584
+ * Проверить, активен ли batch-режим
1585
+ */
1586
+ isBatchMode(): boolean;
908
1587
  protected onAttach(core: CoreEngine): void;
909
1588
  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
- interface GridPluginOptions {
955
- stepX?: number;
956
- stepY?: number;
957
- color?: string;
958
- lineWidth?: number;
959
- visible?: boolean;
960
- minScaleToShow?: number | null;
961
- enableSnap?: boolean;
962
- }
963
- /**
964
- * GridPlugin — draws a grid and implements snap to grid on drag/resize.
965
- * Architecture is identical to other plugins: onAttach/onDetach, own layer with Konva.Shape.
966
- *
967
- * Important points of the current architecture:
968
- * - Panning/scale is performed by Stage transformations.
969
- * - Nodes are placed on the NodeManager layer (core.nodes.layer), also Transformers are added to it.
970
- */
971
- declare class GridPlugin extends Plugin {
972
- private _core?;
973
- private _layer;
974
- private _shape;
975
- private _stepX;
976
- private _stepY;
977
- private _color;
978
- private _lineWidth;
979
- private _visible;
980
- private _minScaleToShow;
981
- private _enableSnap;
982
- private _dragMoveHandler;
983
- private _nodesAddHandler;
984
- private _nodesRemoveHandler;
985
- private _redrawScheduled;
986
- private _transformersCache;
987
- private _cacheInvalidated;
988
- constructor(options?: GridPluginOptions);
989
- protected onAttach(core: CoreEngine): void;
1589
+ private _onKeyDown;
1590
+ private _isEditableTarget;
1591
+ private _onDragStart;
1592
+ private _onDragEnd;
990
1593
  /**
991
- * Deferred redraw (throttling)
1594
+ * Завершить batch-режим и записать составное действие
992
1595
  */
993
- private _scheduleRedraw;
994
- protected onDetach(core: CoreEngine): void;
995
- setVisible(visible: boolean): void;
996
- get stepX(): number;
997
- get stepY(): number;
998
- get minScaleToShow(): number | null;
999
- setStep(stepX: number, stepY: number): void;
1000
- setMinScaleToShow(value: number | null): void;
1001
- setSnap(enabled: boolean): void;
1002
- }
1003
-
1004
- interface RulerPluginOptions {
1005
- thicknessPx?: number;
1006
- fontFamily?: string;
1007
- fontSizePx?: number;
1008
- color?: string;
1009
- bgColor?: string;
1010
- borderColor?: string;
1011
- enabled?: boolean;
1012
- }
1013
- declare class RulerPlugin extends Plugin {
1014
- private _core?;
1015
- 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);
1596
+ private _finishBatch;
1597
+ private _onTransformStart;
1598
+ private _onTransformEnd;
1599
+ private _performUndo;
1600
+ private _performRedo;
1033
1601
  /**
1034
- * Calculate optimal step for ruler ticks
1035
- * Uses nice numbers: 1, 2, 5, 10, 20, 50, 100 and so on
1602
+ * Применить состояние из action (before или after)
1036
1603
  */
1037
- private _calculateNiceStep;
1604
+ private _applyActionState;
1038
1605
  /**
1039
- * Format number for display on ruler
1040
- * Always returns an integer without decimal places
1606
+ * Применить состояние трансформации к ноде
1041
1607
  */
1042
- private _formatNumber;
1608
+ private _applyTransformState;
1043
1609
  /**
1044
- * Calculate and cache parameters for ticks for current scale
1610
+ * Применить действие группировки
1045
1611
  */
1046
- private _getStepsConfig;
1047
- protected onAttach(core: CoreEngine): void;
1048
- protected onDetach(core: CoreEngine): void;
1612
+ private _applyGroupAction;
1049
1613
  /**
1050
- * Get active guide from RulerGuidesPlugin (with caching)
1614
+ * Применить действие разгруппировки
1051
1615
  */
1052
- private _getActiveGuideInfo;
1616
+ private _applyUngroupAction;
1053
1617
  /**
1054
- * Invalidate active guide cache
1618
+ * Воссоздать ноду по сериализованному состоянию
1055
1619
  */
1056
- private _invalidateGuideCache;
1620
+ private _recreateNode;
1621
+ private _onNodeCreated;
1622
+ private _onNodeRemoved;
1623
+ private _onNodeTransformed;
1057
1624
  /**
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
1625
+ * Запланировать завершение batch с debounce
1062
1626
  */
1063
- private _drawRuler;
1627
+ private _scheduleBatchCommit;
1628
+ private _onZIndexChanged;
1629
+ private _onGroupCreated;
1630
+ private _onGroupUngrouped;
1064
1631
  /**
1065
- * Draw horizontal ruler
1066
- * @param activeGuide - cached active guide info
1632
+ * Полная сериализация ноды для истории
1067
1633
  */
1068
- private _drawHorizontalRuler;
1634
+ private _serializeNode;
1069
1635
  /**
1070
- * Draw vertical ruler
1071
- * @param activeGuide - cached active guide info
1636
+ * Захватить состояние трансформации (для drag/transform)
1637
+ * Использует абсолютные координаты для корректной работы с временными группами
1072
1638
  */
1073
- private _drawVerticalRuler;
1639
+ private _captureTransformState;
1074
1640
  /**
1075
- * Full ruler redraw
1641
+ * Построить after-состояние из changes события node:transformed
1642
+ * Использует координаты из changes (world-local для temp-multi-group)
1076
1643
  */
1077
- private _redraw;
1644
+ private _buildAfterState;
1078
1645
  /**
1079
- * Deferred redraw with improved throttling
1080
- * Groups fast zoom/pan events for optimization
1081
- * @param isPanning - true for panning (more aggressive throttling)
1646
+ * Получить тип ноды из Konva className
1082
1647
  */
1083
- private _scheduleRedraw;
1084
- show(): void;
1085
- hide(): void;
1648
+ private _getNodeType;
1086
1649
  /**
1087
- * Toggle ruler visibility
1650
+ * Найти BaseNode по Konva.Node (ищет также по родителям)
1088
1651
  */
1089
- toggle(): void;
1652
+ private _findBaseNodeByKonva;
1090
1653
  /**
1091
- * Check if ruler is visible
1654
+ * Сравнить два состояния на равенство
1655
+ * Использует абсолютные координаты для сравнения (absX, absY, absRotation, etc.)
1092
1656
  */
1093
- isVisible(): boolean;
1657
+ private _statesEqual;
1658
+ /**
1659
+ * Обрезать историю если превышен лимит
1660
+ */
1661
+ private _trimHistoryIfNeeded;
1662
+ private _debug;
1663
+ }
1664
+
1665
+ interface LogoOptions {
1666
+ src: string;
1667
+ width: number;
1668
+ height: number;
1669
+ opacity?: number;
1670
+ }
1671
+ declare class LogoPlugin extends Plugin {
1672
+ private _core?;
1673
+ private _layer?;
1674
+ private _image?;
1675
+ private _src;
1676
+ private _width;
1677
+ private _height;
1678
+ private _opacity;
1679
+ constructor(options: LogoOptions);
1680
+ protected onAttach(core: CoreEngine): void;
1681
+ protected onDetach(core: CoreEngine): void;
1682
+ setOpacity(opacity: number): void;
1683
+ setSize({ width, height }: {
1684
+ width: number;
1685
+ height: number;
1686
+ }): void;
1687
+ setSource(src: string): void;
1688
+ private _setImage;
1689
+ private _loadImageFromString;
1690
+ private _layout;
1691
+ }
1692
+
1693
+ interface NodeHotkeysOptions {
1694
+ target?: Window | Document | HTMLElement | EventTarget;
1695
+ ignoreEditableTargets?: boolean;
1696
+ }
1697
+ declare class NodeHotkeysPlugin extends Plugin {
1698
+ private _core?;
1699
+ private _options;
1700
+ private _clipboard;
1701
+ private _selectionPlugin?;
1702
+ constructor(options?: NodeHotkeysOptions);
1703
+ protected onAttach(core: CoreEngine): void;
1704
+ protected onDetach(_core: CoreEngine): void;
1705
+ private _onPaste;
1706
+ private _onKeyDown;
1707
+ private _isEditableTarget;
1708
+ private _handleCopy;
1709
+ private _handleCut;
1710
+ private _handlePaste;
1711
+ private _handleDelete;
1712
+ private _getSelectedNodes;
1713
+ private _deleteNodes;
1714
+ private _serializeNode;
1715
+ private _serializeKonvaNode;
1716
+ private _getNodeTypeFromKonva;
1717
+ private _deserializeNode;
1718
+ private _getPastePosition;
1719
+ private _isPointerOnScreen;
1720
+ private _getScreenCenter;
1721
+ private _getClipboardCenter;
1722
+ private _computeSelectionWorldCenter;
1723
+ private _handleMoveUp;
1724
+ private _handleMoveDown;
1725
+ private _handleMoveToTop;
1726
+ private _handleMoveToBottom;
1727
+ /**
1728
+ * Checks if the node is inside a real group (not the group itself)
1729
+ */
1730
+ private _isNodeInsidePermanentGroup;
1731
+ /**
1732
+ * Checks if the node is inside a real group
1733
+ * - For group itself — do nothing (moveUp/moveDown already applied to the group)
1734
+ * - For node inside group — FORBIDDEN to change z-index
1735
+ */
1736
+ private _syncGroupZIndex;
1094
1737
  }
1095
1738
 
1096
1739
  interface RulerGuidesPluginOptions {
@@ -1266,126 +1909,473 @@ declare class RulerManagerPlugin extends Plugin {
1266
1909
  deleteActiveGuide(): boolean;
1267
1910
  }
1268
1911
 
1269
- interface AreaSelectionPluginOptions {
1270
- rectStroke?: string;
1271
- rectFill?: string;
1272
- rectStrokeWidth?: number;
1273
- rectOpacity?: number;
1274
- enableKeyboardShortcuts?: boolean;
1275
- }
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 {
1285
- private _core?;
1286
- private _layer;
1287
- private _rect;
1288
- private _start;
1289
- private _transformer;
1290
- private _selecting;
1291
- private _options;
1292
- constructor(options?: AreaSelectionPluginOptions);
1293
- protected onAttach(core: CoreEngine): void;
1294
- 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;
1912
+ interface RulerPluginOptions {
1913
+ thicknessPx?: number;
1914
+ fontFamily?: string;
1915
+ fontSizePx?: number;
1916
+ color?: string;
1917
+ bgColor?: string;
1918
+ borderColor?: string;
1919
+ enabled?: boolean;
1309
1920
  }
1310
- declare class NodeHotkeysPlugin extends Plugin {
1921
+ declare class RulerPlugin extends Plugin {
1311
1922
  private _core?;
1312
1923
  private _options;
1313
- private _clipboard;
1314
- private _selectionPlugin?;
1315
- constructor(options?: NodeHotkeysOptions);
1924
+ private _layer;
1925
+ private _hGroup?;
1926
+ private _vGroup?;
1927
+ private _hBg?;
1928
+ private _vBg?;
1929
+ private _hTicksShape?;
1930
+ private _vTicksShape?;
1931
+ private _hBorder?;
1932
+ private _vBorder?;
1933
+ private _redrawScheduled;
1934
+ private _lastRedrawTime;
1935
+ private _redrawThrottleMs;
1936
+ private _panThrottleMs;
1937
+ private _cachedActiveGuide;
1938
+ private _cacheInvalidated;
1939
+ private _stepsCache;
1940
+ constructor(options?: RulerPluginOptions);
1941
+ /**
1942
+ * Calculate optimal step for ruler ticks
1943
+ * Uses nice numbers: 1, 2, 5, 10, 20, 50, 100 and so on
1944
+ */
1945
+ private _calculateNiceStep;
1946
+ /**
1947
+ * Format number for display on ruler
1948
+ * Always returns an integer without decimal places
1949
+ */
1950
+ private _formatNumber;
1951
+ /**
1952
+ * Calculate and cache parameters for ticks for current scale
1953
+ */
1954
+ private _getStepsConfig;
1316
1955
  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;
1956
+ protected onDetach(core: CoreEngine): void;
1337
1957
  /**
1338
- * Checks if the node is inside a real group (not the group itself)
1958
+ * Get active guide from RulerGuidesPlugin (with caching)
1339
1959
  */
1340
- private _isNodeInsidePermanentGroup;
1960
+ private _getActiveGuideInfo;
1341
1961
  /**
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
1962
+ * Invalidate active guide cache
1345
1963
  */
1346
- private _syncGroupZIndex;
1964
+ private _invalidateGuideCache;
1965
+ /**
1966
+ * Universal ruler drawing (horizontal or vertical)
1967
+ * @param ctx - canvas context
1968
+ * @param axis - ruler axis ('h' for horizontal, 'v' for vertical)
1969
+ * @param activeGuide - cached active guide info
1970
+ */
1971
+ private _drawRuler;
1972
+ /**
1973
+ * Draw horizontal ruler
1974
+ * @param activeGuide - cached active guide info
1975
+ */
1976
+ private _drawHorizontalRuler;
1977
+ /**
1978
+ * Draw vertical ruler
1979
+ * @param activeGuide - cached active guide info
1980
+ */
1981
+ private _drawVerticalRuler;
1982
+ /**
1983
+ * Full ruler redraw
1984
+ */
1985
+ private _redraw;
1986
+ /**
1987
+ * Deferred redraw with improved throttling
1988
+ * Groups fast zoom/pan events for optimization
1989
+ * @param isPanning - true for panning (more aggressive throttling)
1990
+ */
1991
+ private _scheduleRedraw;
1992
+ show(): void;
1993
+ hide(): void;
1994
+ /**
1995
+ * Toggle ruler visibility
1996
+ */
1997
+ toggle(): void;
1998
+ /**
1999
+ * Check if ruler is visible
2000
+ */
2001
+ isVisible(): boolean;
1347
2002
  }
1348
2003
 
2004
+ interface VideoOverlayAddonOptions {
2005
+ zIndex?: number;
2006
+ marginPx?: number;
2007
+ controlsHeightPx?: number;
2008
+ speeds?: number[];
2009
+ minWidthPx?: number;
2010
+ minHeightPx?: number;
2011
+ maxWorldScaleToShow?: number | null;
2012
+ hideDuringCameraZoomMs?: number;
2013
+ uiBackgroundColor?: string;
2014
+ uiBorderColor?: string;
2015
+ uiTextColor?: string;
2016
+ uiMutedTextColor?: string;
2017
+ uiAccentColor?: string;
2018
+ uiTrackColor?: string;
2019
+ uiTrackFilledColor?: string;
2020
+ }
2021
+ declare class VideoOverlayAddon extends PluginAddon<SelectionPlugin> {
2022
+ private _core;
2023
+ private _rootEl;
2024
+ private _controlsEl;
2025
+ private _playBtn;
2026
+ private _muteBtn;
2027
+ private _speedBtn;
2028
+ private _timeLabel;
2029
+ private _seekInput;
2030
+ private _volInput;
2031
+ private _selectedVideoNode;
2032
+ private _selectedVideoEl;
2033
+ private _hiddenForDrag;
2034
+ private _hiddenForSize;
2035
+ private _hiddenForTransform;
2036
+ private _hiddenForZoom;
2037
+ private _hiddenForWorldScale;
2038
+ private _hiddenForNotReady;
2039
+ private _uiMode;
2040
+ private _zoomUnhideTimeoutId;
2041
+ private _onKonvaDragStart;
2042
+ private _onKonvaDragEnd;
2043
+ private _onKonvaTransformStart;
2044
+ private _onKonvaTransformEnd;
2045
+ private _onLayerTransformStart;
2046
+ private _onLayerTransformEnd;
2047
+ private _onLayerDragStart;
2048
+ private _onLayerDragEnd;
2049
+ private _rafId;
2050
+ private _options;
2051
+ private _onNodeSelected;
2052
+ private _onNodeDeselected;
2053
+ private _onSelectionCleared;
2054
+ private _onNodeTransformed;
2055
+ private _onStageResized;
2056
+ private _onCameraChanged;
2057
+ private _onWorldChanged;
2058
+ private _onTimeUpdate;
2059
+ private _onLoadedMetadata;
2060
+ private _onCanPlay;
2061
+ private _onPlayPauseSync;
2062
+ constructor(options?: VideoOverlayAddonOptions);
2063
+ protected onAttach(_plugin: SelectionPlugin, core: CoreEngine): void;
2064
+ protected onDetach(_plugin: SelectionPlugin, core: CoreEngine): void;
2065
+ private _ensureDom;
2066
+ private _updateRangeFill;
2067
+ private _tryShowForNode;
2068
+ private _show;
2069
+ private _hide;
2070
+ private _bindLayerInteractionEvents;
2071
+ private _unbindLayerInteractionEvents;
2072
+ private _bindKonvaDragEvents;
2073
+ private _unbindKonvaDragEvents;
2074
+ private _bindKonvaTransformEvents;
2075
+ private _unbindKonvaTransformEvents;
2076
+ private _hideTemporarilyForZoom;
2077
+ private _bindVideoEvents;
2078
+ private _unbindVideoEvents;
2079
+ private _isVideoReady;
2080
+ private _maybeShowWhenReady;
2081
+ private _scheduleSync;
2082
+ private _syncPosition;
2083
+ private _syncControls;
2084
+ private _formatTime;
2085
+ private _applyUiMode;
2086
+ }
2087
+
2088
+ interface MultiGroupControllerDeps {
2089
+ ensureTempMulti: (nodes: BaseNode[]) => void;
2090
+ destroyTempMulti: () => void;
2091
+ commitTempMultiToGroup: () => void;
2092
+ isActive: () => boolean;
2093
+ forceUpdate: () => void;
2094
+ onWorldChanged?: () => void;
2095
+ isInsideTempByTarget?: (target: Konva.Node) => boolean;
2096
+ }
1349
2097
  /**
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
2098
+ * MultiGroupController thin controller encapsulating work with temporary multi-group.
2099
+ * Actual logic lives in passed dependencies (SelectionPlugin),
2100
+ * thanks to which we don't duplicate code for frames/overlays and behavior.
2101
+ */
2102
+ declare class MultiGroupController {
2103
+ private deps;
2104
+ constructor(deps: MultiGroupControllerDeps);
2105
+ ensure(nodes: BaseNode[]): void;
2106
+ destroy(): void;
2107
+ commitToPermanentGroup(): void;
2108
+ isActive(): boolean;
2109
+ forceUpdateOverlays(): void;
2110
+ onWorldChanged(): void;
2111
+ isInsideTempByTarget(target: Konva.Node): boolean;
2112
+ }
2113
+
2114
+ interface SelectionPluginOptions {
2115
+ dragEnabled?: boolean;
2116
+ enableTransformer?: boolean;
2117
+ deselectOnEmptyClick?: boolean;
2118
+ selectablePredicate?: (node: Konva.Node) => boolean;
2119
+ autoPanEnabled?: boolean;
2120
+ autoPanEdgePx?: number;
2121
+ autoPanMaxSpeedPx?: number;
2122
+ enableVideoOverlay?: boolean | VideoOverlayAddonOptions;
2123
+ }
2124
+ /**
2125
+ * Universal selection and dragging plugin for nodes compatible with BaseNode.
1358
2126
  *
1359
- * onMouseMove() {
1360
- * if (!this._throttle.shouldExecute()) return;
1361
- * // Execute expensive operation
1362
- * }
1363
- * ```
2127
+ * Default behavior:
2128
+ * - Click on node in NodeManager layer selects the node
2129
+ * - Selected node becomes draggable (dragEnabled)
2130
+ * - Click on empty area deselects (deselectOnEmptyClick)
2131
+ * - Optionally enable Konva.Transformer (enableTransformer)
1364
2132
  */
1365
- declare class ThrottleHelper {
1366
- private _lastTime;
1367
- private _throttle;
2133
+ declare class SelectionPlugin extends Plugin {
2134
+ private _core?;
2135
+ private _options;
2136
+ private _selected;
2137
+ private _prevDraggable;
2138
+ private _transformer;
2139
+ private _transformerWasVisibleBeforeDrag;
2140
+ private _cornerHandlesWereVisibleBeforeDrag;
2141
+ private _sizeLabelWasVisibleBeforeDrag;
2142
+ private _rotateHandlesWereVisibleBeforeDrag;
2143
+ private _cornerHandlesGroup;
2144
+ private _cornerHandles;
2145
+ private _cornerHandlesSuppressed;
2146
+ private _transformOppositeCorner;
2147
+ private _sizeLabel;
2148
+ private _radiusLabel;
2149
+ private _rotateHandlesGroup;
2150
+ private _rotateHandles;
2151
+ private _rotateDragState;
2152
+ private _rotateCenterAbsStart;
2153
+ private _prevStageDraggableBeforeRotate;
2154
+ private _worldSyncRafId;
2155
+ private _onCameraZoomEvent;
2156
+ private _hoverTr;
2157
+ private _isPointerDown;
2158
+ private _autoPanRafId;
2159
+ private _autoPanActive;
2160
+ private _autoPanEdgePx;
2161
+ private _autoPanMaxSpeedPx;
2162
+ private _draggingNode;
2163
+ private _ratioKeyPressed;
2164
+ private _onGlobalKeyDown;
2165
+ private _onGlobalKeyUp;
2166
+ private _tempMultiSet;
2167
+ private _tempMultiNodes;
2168
+ private _tempMultiInitialTransforms;
2169
+ private _tempMultiGroup;
2170
+ private _tempOverlay;
2171
+ getMultiGroupController(): MultiGroupController;
2172
+ private _multiCtrl;
2173
+ private _startAutoPanLoop;
2174
+ private _stopAutoPanLoop;
1368
2175
  /**
1369
- * @param throttleMs - minimum interval between calls in milliseconds
2176
+ * Deferred redraw (throttling)
2177
+ * Groups multiple batchDraw calls into one
1370
2178
  */
1371
- constructor(throttleMs?: number);
2179
+ private _scheduleBatchDraw;
2180
+ private _parentGroupDuringChildEdit;
2181
+ private _parentGroupPrevDraggable;
2182
+ private _dragMoveScheduled;
2183
+ private _batchDrawScheduled;
2184
+ private _hoverThrottle;
2185
+ private _uiUpdateDebounce;
2186
+ constructor(options?: SelectionPluginOptions);
2187
+ setOptions(patch: Partial<SelectionPluginOptions>): void;
2188
+ protected onAttach(core: CoreEngine): void;
2189
+ protected onDetach(core: CoreEngine): void;
2190
+ private _onMouseDown;
2191
+ private _select;
2192
+ private _clearSelection;
1372
2193
  /**
1373
- * Checks if the operation can be executed
1374
- * @returns true if enough time has passed since the last call
2194
+ * Apply transformation from overlay group to actual nodes using matrix math.
2195
+ * Each node's transform is updated to match the overlay group's transformation,
2196
+ * while staying in its original parent.
1375
2197
  */
1376
- shouldExecute(): boolean;
2198
+ private _applyOverlayTransformToNodes;
1377
2199
  /**
1378
- * Resets the timer (the next call will be executed immediately)
2200
+ * Update overlay group bbox to match current positions of selected nodes.
2201
+ * Must be called after drag/transform when nodes change position.
1379
2202
  */
1380
- reset(): void;
2203
+ private _updateTempMultiOverlayBBox;
1381
2204
  /**
1382
- * Changes the throttling interval
2205
+ * Compute union bounding box for multiple nodes in world-local coordinates.
2206
+ * Used for overlay-only temporary multi-group.
2207
+ * Returns bbox that doesn't change when world transform (zoom/pan) changes.
1383
2208
  */
1384
- setThrottle(throttleMs: number): void;
2209
+ private _computeUnionBBox;
2210
+ private _ensureTempMulti;
2211
+ private _destroyTempMultiOverlayOnly;
2212
+ private _destroyTempMulti;
2213
+ private _commitTempMultiToGroup;
2214
+ private _tryUngroupSelectedGroup;
2215
+ private _ensureHoverTr;
2216
+ private _destroyHoverTr;
2217
+ private _onHoverMoveThrottled;
2218
+ private _onHoverMove;
2219
+ private _onHoverDown;
2220
+ private _onHoverUp;
2221
+ private _onHoverLeave;
2222
+ private _refreshTransformer;
2223
+ private _restyleSideAnchors;
2224
+ private _setupRotateHandles;
2225
+ private _destroyRotateHandles;
2226
+ private _getNodeCenterAbs;
1385
2227
  /**
1386
- * Returns the current throttling interval
2228
+ * Set custom rotation cursor with dynamic angle based on handle position
1387
2229
  */
1388
- getThrottle(): number;
2230
+ private _setRotateCursor;
2231
+ /**
2232
+ * Apply rotated cursor by creating a rotated SVG data URL
2233
+ */
2234
+ private _applyRotatedCursor;
2235
+ private _updateRotateHandlesPosition;
2236
+ private _setupSizeLabel;
2237
+ private _scheduleUIUpdate;
2238
+ private _updateSizeLabel;
2239
+ private _destroySizeLabel;
2240
+ private _isCornerRadiusSupported;
2241
+ private _getCornerRadiusArray;
2242
+ private _setCornerRadiusArray;
2243
+ private _setupCornerRadiusHandles;
2244
+ private _destroyCornerRadiusHandles;
2245
+ private _bakeRectScale;
2246
+ private _updateCornerRadiusHandlesPosition;
2247
+ private _updateCornerRadiusHandlesVisibility;
2248
+ private _ensureRadiusLabel;
2249
+ private _updateRadiusLabelAt;
2250
+ private _showRadiusLabelForCorner;
2251
+ private _hideRadiusLabel;
2252
+ private _destroyRadiusLabel;
2253
+ private _findBaseNodeByTarget;
2254
+ private _onNodeRemoved;
2255
+ private _disableGroupChildrenDragging;
2256
+ }
2257
+
2258
+ interface VisualGuidesPluginOptions {
2259
+ guidelineColor?: string;
2260
+ guidelineWidth?: number;
2261
+ guidelineDash?: number[];
2262
+ thresholdPx?: number;
2263
+ }
2264
+ /**
2265
+ * VisualGuidesPlugin — snapping of nodes and groups relative to other nodes/groups + stage borders.
2266
+ *
2267
+ * Implemented directly on top of Konva, based on the official guidelines example:
2268
+ * https://konvajs.org/docs/sandbox/Guides.html
2269
+ */
2270
+ declare class VisualGuidesPlugin extends Plugin {
2271
+ private _core?;
2272
+ private _options;
2273
+ private _layer;
2274
+ private _dragMoveHandler;
2275
+ private _dragEndHandler;
2276
+ private _nodesAddHandler;
2277
+ private _nodesRemoveHandler;
2278
+ constructor(options?: VisualGuidesPluginOptions);
2279
+ private _applyGapSnappingForDrag;
2280
+ protected onAttach(core: CoreEngine): void;
2281
+ protected onDetach(core: CoreEngine): void;
2282
+ private _onDragMove;
2283
+ private _clearGuides;
2284
+ private _drawDragGuides;
2285
+ private _pointOnRay;
2286
+ private _drawRayMarkerWithLabel;
2287
+ private _buildDragRays;
2288
+ private _collectOtherNodeBoxes;
2289
+ private _intersectRayWithBox;
2290
+ private _getLineGuideStops;
2291
+ private _getObjectSnappingEdges;
2292
+ private _getGuides;
2293
+ private _attachExistingTransformers;
2294
+ private _walkAttachTransformers;
2295
+ private _attachTransformer;
2296
+ private _walkDetachTransformers;
2297
+ private _handleTransformerTransform;
2298
+ private _drawResizeGuides;
2299
+ private _buildResizeRays;
2300
+ private _isVisualLinesShouldCleared;
2301
+ }
2302
+
2303
+ type ImageHoverFilterMode = 'sepia' | 'warm' | 'cool';
2304
+ interface ImageHoverFilterAddonOptions {
2305
+ mode?: ImageHoverFilterMode;
2306
+ /** 0..1 — effect strength for warm/cool */
2307
+ intensity?: number;
2308
+ /** pixelRatio for cache during hover. Default = devicePixelRatio or 1 */
2309
+ pixelRatio?: number;
2310
+ }
2311
+ declare class ImageHoverFilterAddon extends NodeAddon<ImageNode> {
2312
+ private readonly mode;
2313
+ private readonly intensity;
2314
+ private readonly pixelRatio;
2315
+ private readonly nodes;
2316
+ constructor(options?: ImageHoverFilterAddonOptions);
2317
+ protected onAttach(node: ImageNode): void;
2318
+ protected onDetach(node: ImageNode): void;
2319
+ }
2320
+
2321
+ /**
2322
+ * Addon for RulerPlugin, set up by RulerGuidesPlugin.
2323
+ */
2324
+ declare class RulerGuidesAddon extends PluginAddon<RulerPlugin> {
2325
+ private readonly _options;
2326
+ private _instance;
2327
+ private _owned;
2328
+ constructor(options?: RulerGuidesPluginOptions);
2329
+ protected onAttach(_plugin: RulerPlugin, core: CoreEngine): void;
2330
+ protected onDetach(_plugin: RulerPlugin, core: CoreEngine): void;
2331
+ }
2332
+
2333
+ /**
2334
+ * Addon for RulerPlugin, set up by RulerHighlightPlugin.
2335
+ */
2336
+ declare class RulerHighlightAddon extends PluginAddon<RulerPlugin> {
2337
+ private readonly _options;
2338
+ private _instance;
2339
+ private _owned;
2340
+ constructor(options?: RulerHighlightPluginOptions);
2341
+ protected onAttach(_plugin: RulerPlugin, core: CoreEngine): void;
2342
+ protected onDetach(_plugin: RulerPlugin, core: CoreEngine): void;
2343
+ }
2344
+
2345
+ /**
2346
+ * Addon for RulerPlugin, set up by RulerManagerPlugin.
2347
+ */
2348
+ declare class RulerManagerAddon extends PluginAddon<RulerPlugin> {
2349
+ private readonly _options;
2350
+ private _instance;
2351
+ private _owned;
2352
+ constructor(options?: RulerManagerPluginOptions);
2353
+ protected onAttach(_plugin: RulerPlugin, core: CoreEngine): void;
2354
+ protected onDetach(_plugin: RulerPlugin, core: CoreEngine): void;
2355
+ }
2356
+
2357
+ interface ShapeHoverHighlightAddonOptions {
2358
+ stroke?: string;
2359
+ strokeWidth?: number;
2360
+ fill?: string;
2361
+ mode?: 'stroke' | 'fill' | 'both';
2362
+ }
2363
+ declare class ShapeHoverHighlightAddon extends NodeAddon<ShapeNode> {
2364
+ private readonly stroke;
2365
+ private readonly strokeWidth;
2366
+ private readonly fill;
2367
+ private readonly mode;
2368
+ private readonly nodes;
2369
+ constructor(options?: ShapeHoverHighlightAddonOptions);
2370
+ protected onAttach(node: ShapeNode): void;
2371
+ protected onDetach(node: ShapeNode): void;
2372
+ }
2373
+
2374
+ declare class TextAutoTrimAddon extends NodeAddon<TextNode> {
2375
+ private readonly nodes;
2376
+ protected onAttach(node: TextNode): void;
2377
+ protected onDetach(node: TextNode): void;
2378
+ private normalize;
1389
2379
  }
1390
2380
 
1391
2381
  /**
@@ -1427,4 +2417,46 @@ declare class DebounceHelper {
1427
2417
  cancel(): void;
1428
2418
  }
1429
2419
 
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 };
2420
+ /**
2421
+ * ThrottleHelper - utility for throttling (limiting the frequency of calls)
2422
+ *
2423
+ * Used to limit the frequency of operation execution to a certain number of times per second.
2424
+ * For example, to limit UI updates to 60 FPS (16ms) or 30 FPS (32ms).
2425
+ *
2426
+ * @example
2427
+ * ```typescript
2428
+ * private _throttle = new ThrottleHelper(16); // 60 FPS
2429
+ *
2430
+ * onMouseMove() {
2431
+ * if (!this._throttle.shouldExecute()) return;
2432
+ * // Execute expensive operation
2433
+ * }
2434
+ * ```
2435
+ */
2436
+ declare class ThrottleHelper {
2437
+ private _lastTime;
2438
+ private _throttle;
2439
+ /**
2440
+ * @param throttleMs - minimum interval between calls in milliseconds
2441
+ */
2442
+ constructor(throttleMs?: number);
2443
+ /**
2444
+ * Checks if the operation can be executed
2445
+ * @returns true if enough time has passed since the last call
2446
+ */
2447
+ shouldExecute(): boolean;
2448
+ /**
2449
+ * Resets the timer (the next call will be executed immediately)
2450
+ */
2451
+ reset(): void;
2452
+ /**
2453
+ * Changes the throttling interval
2454
+ */
2455
+ setThrottle(throttleMs: number): void;
2456
+ /**
2457
+ * Returns the current throttling interval
2458
+ */
2459
+ getThrottle(): number;
2460
+ }
2461
+
2462
+ export { type ArcNodeHandle, type ArcNodeOptions, AreaSelectionPlugin, type AreaSelectionPluginOptions, type ArrowNodeHandle, type ArrowNodeOptions, type BaseNodeOptions, type CameraHotkeysOptions, CameraHotkeysPlugin, CameraManager, type CircleNodeHandle, type CircleNodeOptions, ContentFromClipboardPlugin, type ContentFromClipboardPluginOptions, CoreEngine, type CoreEngineOptions, type CoreEvents, DebounceHelper, type EllipseNodeHandle, type EllipseNodeOptions, EventBus, type GifNodeHandle, type GifNodeOptions, 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, MediaPlaceholder, type MediaPlaceholderOptions, 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, type SvgNodeHandle, type SvgNodeOptions, TextAutoTrimAddon, type TextNodeHandle, type TextNodeOptions, ThrottleHelper, type VideoNodeHandle, type VideoNodeOptions, VideoOverlayAddon, type VideoOverlayAddonOptions, VisualGuidesPlugin, type VisualGuidesPluginOptions };