@linkurious/ogma-annotations 2.1.2 → 2.1.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.
@@ -0,0 +1,1621 @@
1
+ import { GeometryObject, Feature, LineString, Geometry, Point as Point$2, Polygon as Polygon$1, FeatureCollection } from 'geojson';
2
+ import { Point as Point$1, Size, Ogma, Node } from '@linkurious/ogma';
3
+ import EventEmitter from 'eventemitter3';
4
+
5
+ /** Types of annotations supported */
6
+ type AnnotationType = "arrow" | "text" | "box" | "comment" | "polygon";
7
+ /**
8
+ * Base properties for all annotations.
9
+ */
10
+ interface AnnotationProps {
11
+ /** Type of annotation */
12
+ type: AnnotationType;
13
+ /** Optional style configuration */
14
+ style?: unknown;
15
+ }
16
+ /** Unique identifier type for annotations */
17
+ type Id = string | number;
18
+ /**
19
+ * Base interface for all annotation features.
20
+ * @template G - Geometry type
21
+ * @template P - Properties type
22
+ */
23
+ interface AnnotationFeature<G extends GeometryObject = GeometryObject, P = AnnotationProps> extends Feature<G, P> {
24
+ /** Unique identifier for the annotation */
25
+ id: Id;
26
+ }
27
+
28
+ /**
29
+ * Hex color string in format #RGB or #RRGGBB
30
+ * @example "#fff" | "#ffffff" | "#F0A" | "#FF00AA"
31
+ */
32
+ type HexColor = `#${string}`;
33
+ /**
34
+ * RGB color string in format rgb(r, g, b)
35
+ * @example "rgb(255, 0, 0)" | "rgb(128, 128, 128)"
36
+ */
37
+ type RgbColor = `rgb(${number}, ${number}, ${number})` | `rgb(${number},${number},${number})`;
38
+ /**
39
+ * RGBA color string in format rgba(r, g, b, a)
40
+ * @example "rgba(255, 0, 0, 1)" | "rgba(128, 128, 128, 0.5)"
41
+ */
42
+ type RgbaColor = `rgba(${number}, ${number}, ${number}, ${number})` | `rgba(${number},${number},${number},${number})`;
43
+ /**
44
+ * Any valid color format
45
+ */
46
+ type Color = HexColor | RgbColor | RgbaColor | "transparent" | "none" | string;
47
+ /**
48
+ * Type guard to check if a string is a valid hex color
49
+ */
50
+ declare function isHexColor(color: string): color is HexColor;
51
+ /**
52
+ * Type guard to check if a string is a valid RGB color
53
+ */
54
+ declare function isRgbColor(color: string): color is RgbColor;
55
+ /**
56
+ * Type guard to check if a string is a valid RGBA color
57
+ */
58
+ declare function isRgbaColor(color: string): color is RgbaColor;
59
+ /**
60
+ * Type guard to check if a string is a valid color
61
+ */
62
+ declare function isColor(color: string): color is Color;
63
+ /**
64
+ * Safely cast a string to a Color type with runtime validation
65
+ * @throws {Error} if the color format is invalid
66
+ */
67
+ declare function asColor(color: string): Color;
68
+ /**
69
+ * Safely cast a string to a HexColor type with runtime validation
70
+ * @throws {Error} if the color format is invalid
71
+ */
72
+ declare function asHexColor(color: string): HexColor;
73
+ /**
74
+ * Safely cast a string to an RgbColor type with runtime validation
75
+ * @throws {Error} if the color format is invalid
76
+ */
77
+ declare function asRgbColor(color: string): RgbColor;
78
+ /**
79
+ * Safely cast a string to an RgbaColor type with runtime validation
80
+ * @throws {Error} if the color format is invalid
81
+ */
82
+ declare function asRgbaColor(color: string): RgbaColor;
83
+
84
+ /** Stroke style options for annotations */
85
+ type StrokeOptions = {
86
+ /** Type of stroke: plain, dashed, or none */
87
+ strokeType?: StrokeType;
88
+ /** Stroke color: #f00, yellow... */
89
+ strokeColor?: Color;
90
+ /** Stroke width */
91
+ strokeWidth?: number;
92
+ };
93
+ /** Stroke types available for annotations */
94
+ type StrokeType = "plain" | "dashed" | "none";
95
+ /** Stroke style for arrow annotations */
96
+ type Stroke = {
97
+ /** Stroke type */
98
+ type: StrokeType;
99
+ /** Stroke color */
100
+ color: Color;
101
+ /** Stroke width */
102
+ width: number;
103
+ };
104
+ type StrokeStyle = Stroke;
105
+
106
+ declare const NONE = -1;
107
+ declare const EVT_DRAG = "dragging";
108
+ declare const EVT_DRAG_START = "dragstart";
109
+ declare const EVT_DRAG_END = "dragend";
110
+ declare const EVT_CLICK = "click";
111
+ declare const EVT_SELECT = "select";
112
+ declare const EVT_UNSELECT = "unselect";
113
+ declare const EVT_HOVER = "hover";
114
+ declare const EVT_UNHOVER = "unhover";
115
+ declare const EVT_REMOVE = "remove";
116
+ declare const EVT_ADD = "add";
117
+ declare const EVT_CANCEL_DRAWING = "cancelDrawing";
118
+ declare const EVT_COMPLETE_DRAWING = "completeDrawing";
119
+ declare const EVT_UPDATE = "update";
120
+ declare const EVT_LINK = "link";
121
+ declare const EVT_HISTORY = "history";
122
+ declare const DATA_ATTR = "data-annotation";
123
+ declare const handleDetectionThreshold = 5;
124
+ declare const handleRadius = 3;
125
+ /** @private */
126
+ declare const LAYERS: {
127
+ SHAPES: number;
128
+ EDITOR: number;
129
+ HANDLES: number;
130
+ };
131
+ declare const SIDE_START: "start";
132
+ declare const SIDE_END: "end";
133
+ /** @private */
134
+ declare const cursors: Record<string, Cursor>;
135
+ declare const COMMENT_MODE_COLLAPSED = "collapsed";
136
+ declare const COMMENT_MODE_EXPANDED = "expanded";
137
+ declare const TEXT_LINE_HEIGHT = 1.2;
138
+ declare const HL_BRIGHTEN = 0.2;
139
+ /** @private */
140
+ declare const TARGET_TYPES: {
141
+ TEXT: "text";
142
+ NODE: "node";
143
+ BOX: "box";
144
+ COMMENT: "comment";
145
+ POLYGON: "polygon";
146
+ ANNOTATION: "annotation";
147
+ EDGE: "edge";
148
+ };
149
+ /** Default send button icon (paper plane) */
150
+ declare const DEFAULT_SEND_ICON = "<svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M22 2L11 13M22 2L15 22L11 13M22 2L2 9L11 13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>";
151
+ declare const DEFAULT_EDIT_ICON = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M12 6.00015H7.33333C6.97971 6.00015 6.64057 6.14063 6.39052 6.39068C6.14048 6.64072 6 6.97986 6 7.33348V16.6668C6 17.0204 6.14048 17.3596 6.39052 17.6096C6.64057 17.8597 6.97971 18.0002 7.33333 18.0002H16.6667C17.0203 18.0002 17.3594 17.8597 17.6095 17.6096C17.8595 17.3596 18 17.0204 18 16.6668V12.0002M16.25 5.75015C16.5152 5.48493 16.8749 5.33594 17.25 5.33594C17.6251 5.33594 17.9848 5.48493 18.25 5.75015C18.5152 6.01537 18.6642 6.37508 18.6642 6.75015C18.6642 7.12522 18.5152 7.48493 18.25 7.75015L12.2413 13.7595C12.083 13.9176 11.8875 14.0334 11.6727 14.0962L9.75733 14.6562C9.69997 14.6729 9.63916 14.6739 9.58127 14.6591C9.52339 14.6442 9.47055 14.6141 9.4283 14.5719C9.38604 14.5296 9.35593 14.4768 9.3411 14.4189C9.32627 14.361 9.32727 14.3002 9.344 14.2428L9.904 12.3275C9.96702 12.1129 10.083 11.9175 10.2413 11.7595L16.25 5.75015Z\" stroke=\"#1A70E5\" stroke-width=\"1.33333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n";
152
+
153
+ /** 2D coordinate */
154
+ type Point = {
155
+ x: number;
156
+ y: number;
157
+ };
158
+ /** @private */
159
+ type Vector = Point;
160
+ /**
161
+ * Bounding box object, with the following properties:
162
+ * - [0]: min x
163
+ * - [1]: min y
164
+ * - [2]: max x
165
+ * - [3]: max y
166
+ */
167
+ type Bounds = [number, number, number, number];
168
+
169
+ type TargetType = (typeof TARGET_TYPES)[keyof typeof TARGET_TYPES];
170
+ type Side = typeof SIDE_START | typeof SIDE_END;
171
+ /** Arrow snapped to the center or perimeter of a node. */
172
+ type NodeMagnet = {
173
+ type: "node";
174
+ center: boolean;
175
+ };
176
+ /** Arrow snapped at parametric position t (0–1) along an edge path. */
177
+ type EdgeMagnet = {
178
+ type: "edge";
179
+ t: number;
180
+ };
181
+ /**
182
+ * Arrow snapped to a rectangular annotation (text, box, comment).
183
+ * nx/ny are center-relative fractions multiplied by width/height:
184
+ * left-center = { nx: -0.5, ny: 0 }
185
+ * right-center = { nx: 0.5, ny: 0 }
186
+ * center = { nx: 0, ny: 0 }
187
+ */
188
+ type BoxMagnet = {
189
+ type: "box";
190
+ nx: number;
191
+ ny: number;
192
+ };
193
+ /**
194
+ * Arrow snapped to a polygon annotation.
195
+ * rx/ry are 0–1 fractions of the polygon's bounding box from its top-left corner.
196
+ */
197
+ type PolygonMagnet = {
198
+ type: "polygon";
199
+ rx: number;
200
+ ry: number;
201
+ };
202
+ type Magnet = NodeMagnet | EdgeMagnet | BoxMagnet | PolygonMagnet;
203
+ /** Link between an arrow and a text or node */
204
+ interface Link {
205
+ /** arrow attached to the text or node */
206
+ arrow: Id;
207
+ /** id of the text the arrow is attached to */
208
+ id: Id;
209
+ /** On which end the arrow is tighten to the text */
210
+ side: Side;
211
+ /** id of the text or node the arrow is attached to */
212
+ target: Id;
213
+ /** Text or node */
214
+ targetType: TargetType;
215
+ /** Typed snap point — semantics depend on targetType, see Magnet union. */
216
+ magnet: Magnet;
217
+ }
218
+ /**
219
+ * Serialized link stored inside arrow.properties.link.
220
+ * Uses plain { x, y } for backward compatibility with saved annotations.
221
+ * Converted to the internal Magnet type by Links.add().
222
+ */
223
+ type ExportedLink = {
224
+ id: Id;
225
+ side: Side;
226
+ type: TargetType;
227
+ magnet?: Point;
228
+ };
229
+
230
+ /** Extremity types for arrow annotations. */
231
+ type Extremity = "none" | "arrow" | "arrow-plain" | "dot" | "halo-dot";
232
+ /**
233
+ * Styles specific to arrow annotations.
234
+ */
235
+ interface ArrowStyles extends StrokeOptions {
236
+ /** Tail extremity style */
237
+ tail?: Extremity;
238
+ /** Head extremity style */
239
+ head?: Extremity;
240
+ }
241
+ interface ArrowProperties extends AnnotationProps {
242
+ type: "arrow";
243
+ style?: ArrowStyles;
244
+ link?: Partial<Record<Side, ExportedLink>>;
245
+ }
246
+ /**
247
+ * Arrow annotation feature. Represents a directed line between two points,
248
+ * can connect a textbox to a shape.
249
+ */
250
+ interface Arrow extends AnnotationFeature<LineString, ArrowProperties> {
251
+ }
252
+ declare const isArrow: (a: AnnotationFeature<Geometry, AnnotationProps>) => a is Arrow;
253
+ /**
254
+ * @private
255
+ * @param a Arrow annotation
256
+ * @param point Point to test
257
+ * @param threshold Detection threshold
258
+ * @returns True if the point is on the arrow line within the given threshold
259
+ */
260
+ declare function detectArrow(a: Arrow, point: Point$1, threshold: number): boolean;
261
+ /**
262
+ * Default style configuration for arrow annotations.
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * {
267
+ * strokeType: "plain",
268
+ * strokeColor: "#202020",
269
+ * strokeWidth: 1,
270
+ * head: "none",
271
+ * tail: "none"
272
+ * }
273
+ * ```
274
+ */
275
+ declare const defaultArrowStyle: ArrowStyles;
276
+ /**
277
+ * Default options for creating new Arrow annotations.
278
+ * Contains the default arrow structure with {@link defaultArrowStyle}.
279
+ */
280
+ declare const defaultArrowOptions: Arrow;
281
+ declare const createArrow: (x0?: number, y0?: number, x1?: number, y1?: number, styles?: {
282
+ /** Tail extremity style */
283
+ tail?: Extremity | undefined;
284
+ /** Head extremity style */
285
+ head?: Extremity | undefined;
286
+ strokeType?: StrokeType | undefined;
287
+ strokeColor?: string | undefined;
288
+ strokeWidth?: number | undefined;
289
+ }) => Arrow;
290
+
291
+ /** Styles specific to box annotations. */
292
+ interface BoxStyle extends StrokeOptions {
293
+ /** background color: empty for transparent #f00, yellow...*/
294
+ background?: Color;
295
+ /** padding around the box */
296
+ padding?: number;
297
+ /** border radius */
298
+ borderRadius?: number;
299
+ /** if true, the box scales with zoom. Default is true */
300
+ scaled?: boolean;
301
+ /** box shadow in CSS format, e.g. "0px 4px 6px rgba(0, 0, 0, 0.1)" */
302
+ boxShadow?: string;
303
+ }
304
+ /** Properties specific to box annotations. */
305
+ interface BoxProperties extends AnnotationProps {
306
+ type: "box";
307
+ /** Width of the box */
308
+ width: number;
309
+ /** Height of the box */
310
+ height: number;
311
+ /** Style options for the box */
312
+ style?: BoxStyle;
313
+ }
314
+ /**
315
+ * Box annotation feature
316
+ */
317
+ interface Box extends AnnotationFeature<Point$2, BoxProperties> {
318
+ }
319
+ declare const isBox: (a: AnnotationFeature<Geometry, AnnotationProps>) => a is Box;
320
+ /** @private */
321
+ declare function detectBox(a: Box, p: Point, sin?: number, cos?: number, threshold?: number): boolean;
322
+ /**
323
+ * Default style configuration for box annotations.
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * {
328
+ * background: "#f5f5f5",
329
+ * strokeWidth: 0,
330
+ * borderRadius: 8,
331
+ * padding: 16,
332
+ * strokeType: "plain"
333
+ * }
334
+ * ```
335
+ */
336
+ declare const defaultBoxStyle: BoxStyle;
337
+ /**
338
+ * Default options for creating new Box annotations.
339
+ * Contains the default box structure with {@link defaultBoxStyle}.
340
+ */
341
+ declare const defaultBoxOptions: Box;
342
+ declare const createBox: (x?: number, y?: number, width?: number, height?: number, styles?: Partial<BoxStyle>) => Box;
343
+
344
+ interface TextStyle extends BoxStyle {
345
+ /** Helvetica, sans-serif... */
346
+ font?: string;
347
+ /** Font size, in pixels */
348
+ fontSize?: number | string;
349
+ /** text color: #f00, yellow...*/
350
+ color?: Color;
351
+ /** background color: empty for transparent #f00, yellow...*/
352
+ background?: Color;
353
+ /** padding around the text */
354
+ padding?: number;
355
+ /** Text box border radius */
356
+ borderRadius?: number;
357
+ /** When true, text maintains constant size regardless of zoom level */
358
+ fixedSize?: boolean;
359
+ /** Opt-in: when true, corner/edge-drag resize also updates fontScale, so
360
+ * the rendered font size scales with the box instead of the text
361
+ * rewrapping/truncating. Only set by defaultStickyNoteStyle. */
362
+ scaleFontOnResize?: boolean;
363
+ /** Accumulated multiplier applied to fontSize at render time:
364
+ * effectiveFontSize = fontSize * (fontScale ?? 1). Updated incrementally
365
+ * by TextHandler's corner/edge drag when scaleFontOnResize is true;
366
+ * absent (≡ 1) for every annotation that doesn't opt in. */
367
+ fontScale?: number;
368
+ /**
369
+ * Ghost text shown (via the textarea's native `placeholder` attribute)
370
+ * while `content` is empty - disappears the instant the user types, no
371
+ * selection/focus tricks needed. Overrides the global
372
+ * `ControllerOptions.textPlaceholder` for this annotation.
373
+ */
374
+ placeholder?: string;
375
+ /** Bold the rendered/edited text. Absent (≡ "normal") for every
376
+ * annotation that doesn't opt in - no italic, no other weights for v1. */
377
+ fontWeight?: "normal" | "bold";
378
+ /**
379
+ * Whether to render `properties.author` as a one-line, ellipsis-truncated
380
+ * signature at the bottom of the box. Toggled by `TextStyleToolbar`'s
381
+ * author-visibility cell. No-op when `properties.author` is unset or
382
+ * blank. Hidden (≡ false) by default.
383
+ */
384
+ showAuthor?: boolean;
385
+ /**
386
+ * Per-annotation override for the author line's appearance. Overrides
387
+ * the global `ControllerOptions.authorStyle` for this annotation only -
388
+ * same precedence pattern as `placeholder` vs
389
+ * `ControllerOptions.textPlaceholder`. Falls back to a small built-in
390
+ * default (`DEFAULT_AUTHOR_STYLE` in `renderer/shapes/text.ts`) for any
391
+ * field neither this nor the global option sets.
392
+ */
393
+ authorStyle?: Partial<AuthorLineStyle>;
394
+ }
395
+ /** Style overrides for the author line rendered under a Text's content
396
+ * when `showAuthor` is true and `properties.author` is non-empty. Only
397
+ * the line-level subset of `TextStyle` - box properties (background,
398
+ * padding, borderRadius...) don't apply to it. */
399
+ type AuthorLineStyle = Pick<TextStyle, "font" | "fontSize" | "color" | "fontWeight">;
400
+ interface TextProperties extends Omit<BoxProperties, "type"> {
401
+ type: "text";
402
+ /**text to display*/
403
+ content: string;
404
+ /** Author/signature line shown under the content when `style.showAuthor`
405
+ * is true. Set by the host app (via `properties.author` at creation or
406
+ * `control.update()`) - no built-in UI writes this string. */
407
+ author?: string;
408
+ /** Width of the text box */
409
+ width: number;
410
+ /** Height of the text box */
411
+ height: number;
412
+ style?: TextStyle;
413
+ }
414
+ /**
415
+ * Text annotation feature, represents a text box at a specific position
416
+ */
417
+ interface Text extends AnnotationFeature<Point$2, TextProperties> {
418
+ }
419
+ declare const isText: (a: AnnotationFeature<Geometry, AnnotationProps>) => a is Text;
420
+ /**
421
+ * Heuristic "is this Text a sticky note" check. Sticky notes are not a
422
+ * distinct annotation type - they're `Text` created via
423
+ * `Control.enableStickyNoteDrawing()` with `defaultStickyNoteStyle` (see
424
+ * `api/drawing.ts`) - so there is no dedicated marker to check yet.
425
+ *
426
+ * Checks two of that preset's characteristic style values rather than just
427
+ * `scaleFontOnResize` alone: that flag's own doc comment above notes it's
428
+ * "only set by defaultStickyNoteStyle", but a host app is free to set it
429
+ * manually on a plain Text too, so pairing it with `placeholder` (which -
430
+ * unlike `content` - never gets cleared by typing) cuts down on that
431
+ * false-positive risk. Still a heuristic: a host that overrides
432
+ * `styles.stickyNote.placeholder` when calling `enableStickyNoteDrawing`/
433
+ * `AnnotationToolbar` will miss here.
434
+ *
435
+ * Kept as a single function (not inlined at each call site) so swapping in
436
+ * a dedicated marker later - e.g. a `style.preset` field - is a one-place
437
+ * change.
438
+ */
439
+ declare const isStickyNote: (a: Text) => boolean;
440
+ /**
441
+ * Default style configuration for text annotations.
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * {
446
+ * font: "sans-serif",
447
+ * fontSize: 18,
448
+ * color: "#505050",
449
+ * background: "#f5f5f5",
450
+ * strokeWidth: 0,
451
+ * borderRadius: 8,
452
+ * padding: 16,
453
+ * strokeType: "plain",
454
+ * fixedSize: false
455
+ * }
456
+ * ```
457
+ */
458
+ declare const defaultTextStyle: TextStyle;
459
+ /**
460
+ * Default options for creating new Text annotations.
461
+ * Contains the default text structure with {@link defaultTextStyle}.
462
+ */
463
+ declare const defaultTextOptions: Text;
464
+ declare const createText: (x?: number, y?: number, width?: number, height?: number, content?: string, styles?: Partial<TextStyle>) => Text;
465
+ /**
466
+ * Detects whether a point is within a text annotation's bounds.
467
+ * @private
468
+ * @param a Text annotation
469
+ * @param p Point to test
470
+ * @param threshold Detection threshold
471
+ * @param sin Rotation sine
472
+ * @param cos Rotation cosine
473
+ * @param zoom Current zoom level
474
+ * @returns True if the point is within the text bounds, false otherwise
475
+ */
476
+ declare function detectText(a: Text, p: Point, threshold?: number, sin?: number, cos?: number, zoom?: number): boolean;
477
+
478
+ /**
479
+ * Style configuration for Comment annotations
480
+ */
481
+ interface CommentStyle extends TextStyle {
482
+ /** Background color for collapsed icon (default: "#FFD700") */
483
+ iconColor?: Color;
484
+ /** Icon to display when collapsed (default: "💬") */
485
+ iconSymbol?: string;
486
+ /** Border color for collapsed icon */
487
+ iconBorderColor?: Color;
488
+ /** Border width for collapsed icon */
489
+ iconBorderWidth?: number;
490
+ /** Minimum height (default: 60px) */
491
+ minHeight?: number;
492
+ /** Maximum height before scrolling (default: 480px, undefined = no limit) */
493
+ maxHeight?: number;
494
+ /** Size when collapsed (default: 32px) */
495
+ iconSize?: number;
496
+ /** Zoom threshold below which comment auto-collapses (default: 0.5) */
497
+ collapseZoomThreshold?: number;
498
+ /** Show "send" button in edit mode (default: true) */
499
+ showSendButton?: boolean;
500
+ /** Auto-grow height with content (default: true) */
501
+ autoGrow?: boolean;
502
+ /** Show drop shadow on comment box (default: true) */
503
+ shadow?: boolean;
504
+ /** Expand to full width when selected (default: false) */
505
+ expandOnSelect?: boolean;
506
+ /**
507
+ * Connector-line behavior when the attachment point moves (default: "rigid").
508
+ * - "rigid": the comment translates by the same offset as the moved
509
+ * attachment point — the arrow keeps its length/angle, whole callout moves.
510
+ * - "elastic": the comment stays put; the arrow re-anchors to the nearest
511
+ * point on the comment box, so the line can stretch/rotate.
512
+ */
513
+ connectorMode?: "rigid" | "elastic";
514
+ }
515
+ /**
516
+ * Properties for Comment annotations
517
+ *
518
+ * Comments are specialized annotations that:
519
+ * - Always maintain fixed screen-space size
520
+ * - Always have at least one arrow pointing TO them
521
+ * - Can be collapsed (icon) or expanded (text box)
522
+ * - Support multiple arrows pointing to them
523
+ */
524
+ interface CommentProps extends AnnotationProps {
525
+ type: "comment";
526
+ /** Text content (similar to text annotation) */
527
+ content: string;
528
+ /** Display mode: collapsed (icon) or expanded (text box) */
529
+ mode: typeof COMMENT_MODE_COLLAPSED | typeof COMMENT_MODE_EXPANDED;
530
+ /** Width in expanded mode (pixels) */
531
+ width: number;
532
+ /** Height (auto-grows with content, pixels) */
533
+ height: number;
534
+ /** Optional metadata */
535
+ author?: string;
536
+ timestamp?: Date;
537
+ /** Styling */
538
+ style?: CommentStyle;
539
+ }
540
+ /**
541
+ * Comment annotation type
542
+ * Geometry: Point (center position of comment box/icon)
543
+ *
544
+ * Note: Arrows are stored separately in Arrow features.
545
+ * Arrows reference comments via their link.start or link.end properties.
546
+ */
547
+ interface Comment extends AnnotationFeature<Point$2, CommentProps> {
548
+ }
549
+ /**
550
+ * Type guard to check if an annotation is a Comment
551
+ */
552
+ declare const isComment: (a: AnnotationFeature<Geometry, AnnotationProps>) => a is Comment;
553
+ /**
554
+ * Default style for Comment annotations
555
+ *
556
+ * @example
557
+ * ```typescript
558
+ * {
559
+ * // Box styling
560
+ * background: "#FFFACD", // Light yellow (sticky note color)
561
+ * padding: 8,
562
+ * borderRadius: 4,
563
+ * strokeColor: "#DDD",
564
+ * strokeWidth: 1,
565
+ * strokeType: "plain",
566
+ *
567
+ * // Icon styling (collapsed mode)
568
+ * iconColor: "#FFCB2F", // Gold
569
+ * iconSymbol: "💬",
570
+ * iconBorderColor: "#aaa",
571
+ * iconBorderWidth: 2,
572
+ *
573
+ * // Size properties
574
+ * minHeight: 60,
575
+ * iconSize: 32,
576
+ *
577
+ * // Text styling
578
+ * color: "#333",
579
+ * font: "Arial, sans-serif",
580
+ * fontSize: 12,
581
+ *
582
+ * // Editing UI
583
+ * showSendButton: true,
584
+ * autoGrow: true,
585
+ *
586
+ * // Visual effects
587
+ * shadow: true,
588
+ * expandOnSelect: false,
589
+ *
590
+ * // Fixed size (always screen-aligned)
591
+ * fixedSize: true
592
+ * }
593
+ * ```
594
+ */
595
+ declare const defaultCommentStyle: CommentStyle;
596
+ /**
597
+ * Default options for creating new Comments.
598
+ * Contains the default comment configuration with {@link defaultCommentStyle}.
599
+ *
600
+ * @example
601
+ * ```typescript
602
+ * {
603
+ * mode: "expanded",
604
+ * width: 200,
605
+ * height: 120,
606
+ * content: "",
607
+ * style: defaultCommentStyle
608
+ * }
609
+ * ```
610
+ */
611
+ declare const defaultCommentOptions: Partial<CommentProps>;
612
+ /**
613
+ * Create a new Comment annotation
614
+ *
615
+ * @param x - X coordinate of the comment box/icon center
616
+ * @param y - Y coordinate of the comment box/icon center
617
+ * @param content - Text content
618
+ * @param options - Optional configuration
619
+ * @returns New Comment feature
620
+ *
621
+ * @important This creates ONLY the comment box without an arrow. Since comments
622
+ * require at least one arrow, you should use {@link createCommentWithArrow}
623
+ * instead for programmatic creation. This function is primarily used internally
624
+ * by the interactive drawing handlers.
625
+ *
626
+ * @see createCommentWithArrow for creating comments programmatically
627
+ */
628
+ declare function createComment(x: number, y: number, content: string, options?: Partial<CommentProps>): Comment;
629
+ /**
630
+ * Toggle comment mode between collapsed and expanded
631
+ *
632
+ * @param comment - Comment to toggle
633
+ * @returns Updated comment with toggled mode
634
+ */
635
+ declare function toggleCommentMode(comment: Comment): Comment;
636
+ /**
637
+ * Detect if a point is within a comment's bounds
638
+ * @private
639
+ * @param comment - Comment to test
640
+ * @param point - Point to test
641
+ * @param threshold - Detection threshold in pixels
642
+ * @param zoom - Current zoom level
643
+ * @returns True if point is within comment bounds
644
+ */
645
+ declare function detectComment(comment: Comment, point: Point, threshold: number | undefined, sin: number, cos: number, zoom?: number): boolean;
646
+ /**
647
+ * Get the position (center) of a comment
648
+ *
649
+ * @param comment - Comment annotation
650
+ * @returns Center position
651
+ */
652
+ declare function getCommentPosition(comment: Comment): Point;
653
+ /**
654
+ * Get the dimensions of a comment based on its mode
655
+ *
656
+ * @param comment - Comment annotation
657
+ * @returns Width and height
658
+ */
659
+ declare function getCommentSize(comment: Comment): Size;
660
+ /**
661
+ * Calculate optimal zoom threshold for auto-collapse based on comment dimensions
662
+ *
663
+ * The threshold is computed so that the comment collapses when its screen-space
664
+ * size would be smaller than a minimum readable size.
665
+ *
666
+ * @param comment - Comment annotation
667
+ * @param minReadableWidth - Minimum readable width in pixels (default: 80)
668
+ * @returns Zoom threshold below which comment should collapse
669
+ *
670
+ * @example
671
+ * // A 200px wide comment with minReadable=80 will collapse at zoom < 0.4
672
+ * // because 200 * 0.4 = 80
673
+ */
674
+ declare function calculateCommentZoomThreshold(comment: Comment, minReadableWidth?: number): number;
675
+ /**
676
+ * Whether a comment's connector line should rigidly follow its attachment
677
+ * point (translate the comment by the same offset) rather than elastically
678
+ * re-anchoring to the nearest point on the comment box.
679
+ *
680
+ * @param comment - Comment annotation
681
+ * @returns True unless the comment's style explicitly sets `connectorMode: "elastic"`
682
+ */
683
+ declare function isRigidConnector(comment: Comment): boolean;
684
+ /**
685
+ * Get the effective zoom threshold for a comment
686
+ * Uses explicit threshold if set, otherwise calculates from dimensions
687
+ *
688
+ * @param comment - Comment annotation
689
+ * @returns Effective zoom threshold
690
+ */
691
+ declare function getCommentZoomThreshold(comment: Comment): number;
692
+ /**
693
+ * Create a comment with an arrow pointing to a target location
694
+ *
695
+ * This is the recommended way to create comments programmatically, as it ensures
696
+ * that the comment always has at least one arrow (which is required).
697
+ *
698
+ * @param targetX - X coordinate where the arrow points to
699
+ * @param targetY - Y coordinate where the arrow points to
700
+ * @param commentX - X coordinate of the comment box center
701
+ * @param commentY - Y coordinate of the comment box center
702
+ * @param content - Text content of the comment
703
+ * @param options - Optional configuration
704
+ * @param options.commentStyle - Style options for the comment
705
+ * @param options.arrowStyle - Style options for the arrow
706
+ * @returns Object containing the comment and arrow features
707
+ *
708
+ * @example
709
+ * ```typescript
710
+ * import { createCommentWithArrow } from '@linkurious/ogma-annotations';
711
+ *
712
+ * // Create a comment pointing to a node at (100, 100)
713
+ * const { comment, arrow } = createCommentWithArrow(
714
+ * 100, 100, // Target position (where arrow points)
715
+ * 300, 50, // Comment position
716
+ * "Important node!", // Comment text
717
+ * {
718
+ * commentStyle: {
719
+ * style: {
720
+ * background: "#FFFACD",
721
+ * color: "#333"
722
+ * }
723
+ * },
724
+ * arrowStyle: {
725
+ * strokeColor: "#3498db",
726
+ * strokeWidth: 2,
727
+ * head: "arrow"
728
+ * }
729
+ * }
730
+ * );
731
+ *
732
+ * // Add both to the controller
733
+ * controller.add(comment);
734
+ * controller.add(arrow);
735
+ *
736
+ * // The arrow is automatically linked to the comment
737
+ * ```
738
+ */
739
+ declare function createCommentWithArrow(targetX: number, targetY: number, commentX: number, commentY: number, content?: string, options?: {
740
+ commentStyle?: Partial<CommentProps>;
741
+ arrowStyle?: Partial<ArrowStyles>;
742
+ }): {
743
+ comment: Comment;
744
+ arrow: Arrow;
745
+ };
746
+
747
+ interface PolygonStyle extends BoxStyle {
748
+ }
749
+ interface PolygonProperties extends AnnotationProps {
750
+ type: "polygon";
751
+ style?: PolygonStyle;
752
+ }
753
+ /**
754
+ * Polygon placed on the graph, use it to highlight areas
755
+ */
756
+ interface Polygon extends AnnotationFeature<Polygon$1, PolygonProperties> {
757
+ }
758
+ declare const isPolygon: (a: AnnotationFeature<Geometry, AnnotationProps>) => a is Polygon;
759
+ /**
760
+ * Point-in-polygon detection using ray casting algorithm
761
+ * @private
762
+ * @param polygon The polygon annotation
763
+ * @param point The point to test
764
+ * @param threshold Detection threshold in pixels
765
+ * @return True if the point is inside the polygon or within the threshold distance from its edges
766
+ */
767
+ declare function detectPolygon(polygon: Polygon, point: Point, threshold?: number): boolean;
768
+ /**
769
+ * Create a polygon annotation
770
+ */
771
+ declare function createPolygon(coordinates: [number, number][][], properties?: Partial<Omit<PolygonProperties, "type">> & {
772
+ id?: Id;
773
+ }): Polygon;
774
+ /**
775
+ * Default style configuration for polygon annotations.
776
+ *
777
+ * @example
778
+ * ```typescript
779
+ * {
780
+ * background: "transparent",
781
+ * strokeWidth: 2,
782
+ * borderRadius: 8,
783
+ * padding: 16,
784
+ * strokeType: "plain",
785
+ * strokeColor: "#000000"
786
+ * }
787
+ * ```
788
+ */
789
+ declare const defaultPolygonStyle: PolygonStyle;
790
+ /**
791
+ * Default polygon properties for creating new Polygon annotations.
792
+ * Contains the default polygon configuration with {@link defaultPolygonStyle}.
793
+ */
794
+ declare const defaultPolygonProperties: PolygonProperties;
795
+
796
+ /** Union type of all Annotation features */
797
+ type Annotation = Arrow | Box | Text | Comment | Polygon;
798
+ /** Collection of Annotations, GeoJSON FeatureCollection */
799
+ interface AnnotationCollection extends FeatureCollection {
800
+ features: Annotation[];
801
+ }
802
+ /** Helper to check if a feature collection is an annotation collection */
803
+ declare const isAnnotationCollection: (a: AnnotationFeature<Geometry, AnnotationProps> | FeatureCollection) => a is AnnotationCollection;
804
+ /** Function type to get an Annotation by its id */
805
+ type AnnotationGetter = (id: Id) => Annotation | undefined;
806
+
807
+ /** Event related to multiple annotation features */
808
+ interface FeaturesEvent {
809
+ /** Annotation IDs involved in the event */
810
+ ids: Id[];
811
+ }
812
+ /** Event related to a single annotation feature */
813
+ interface FeatureEvent {
814
+ /** Annotation ID involved in the event */
815
+ id: Id;
816
+ }
817
+ /** Event related to a single annotation feature */
818
+ interface ClickEvent {
819
+ /** Annotation ID involved in the event */
820
+ id?: Id;
821
+ /** Mouse position in pixel coordinates */
822
+ position: {
823
+ x: number;
824
+ y: number;
825
+ };
826
+ }
827
+ /** Event related to a single annotation feature */
828
+ interface DragEvent {
829
+ /** Annotation ID involved in the event */
830
+ id: Id;
831
+ /** Current mouse position in pixel coordinates during the drag */
832
+ position: {
833
+ x: number;
834
+ y: number;
835
+ };
836
+ }
837
+ /** History stack change event */
838
+ interface HistoryEvent {
839
+ /** Indicates if undo operation is available */
840
+ canUndo: boolean;
841
+ /** Indicates if redo operation is available */
842
+ canRedo: boolean;
843
+ }
844
+ type FeatureEvents = {
845
+ /**
846
+ * Event trigerred when selecting an annotation
847
+ * @param evt The annotation selected
848
+ */
849
+ [EVT_SELECT]: (evt: FeaturesEvent) => void;
850
+ /**
851
+ * Event trigerred when unselecting an annotation
852
+ * @param evt The annotation unselected
853
+ */
854
+ [EVT_UNSELECT]: (evt: FeaturesEvent) => void;
855
+ /**
856
+ * Event trigerred when removing an annotation
857
+ * @param evt The annotation removed
858
+ */
859
+ [EVT_REMOVE]: (evt: FeatureEvent) => void;
860
+ /**
861
+ * Event trigerred when adding an annotation
862
+ * @param evt The annotation added
863
+ */
864
+ [EVT_ADD]: (evt: FeatureEvent) => void;
865
+ /**
866
+ * Event trigerred when canceling drawing mode
867
+ */
868
+ [EVT_CANCEL_DRAWING]: () => void;
869
+ /**
870
+ * Event trigerred when completing a drawing operation
871
+ * @param evt Contains the ID of the completed annotation
872
+ */
873
+ [EVT_COMPLETE_DRAWING]: (evt: FeatureEvent) => void;
874
+ /**
875
+ * Event trigerred when updating an annotation.
876
+ * This fires after any modification including drag operations, style changes, scaling, etc.
877
+ * @param evt The updated annotation with all changes applied
878
+ */
879
+ [EVT_UPDATE]: (evt: Annotation) => void;
880
+ /**
881
+ * Event trigerred when linking an arrow to a node or annotation
882
+ * @param evt Contains the arrow and link details
883
+ */
884
+ [EVT_LINK]: (evt: {
885
+ arrow: Arrow;
886
+ link: Link;
887
+ }) => void;
888
+ /**
889
+ * Event trigerred when history state changes (after undo/redo operations)
890
+ * @param evt Contains boolean flags for undo/redo availability
891
+ */
892
+ [EVT_HISTORY]: (evt: HistoryEvent) => void;
893
+ /**
894
+ * Event triggered when a drag operation starts on an annotation
895
+ */
896
+ [EVT_DRAG_START]: (evt: DragEvent) => void;
897
+ /**
898
+ * Event triggered when a drag operation ends on an annotation
899
+ */
900
+ [EVT_DRAG_END]: (evt: DragEvent) => void;
901
+ /**
902
+ * Event triggered when a click completes on an annotation (mouseup without drag)
903
+ */
904
+ [EVT_CLICK]: (evt: ClickEvent) => void;
905
+ };
906
+
907
+ /**
908
+ * Options for the annotations control
909
+ */
910
+ type ControllerOptions = {
911
+ /**
912
+ * The radius in which arrows are attracted
913
+ */
914
+ magnetRadius: number;
915
+ /**
916
+ * The margin in which the Texts are detected when looking for magnet points
917
+ */
918
+ detectMargin: number;
919
+ /**
920
+ * Display size of the magnet point
921
+ */
922
+ magnetHandleRadius: number;
923
+ /**
924
+ * Placeholder for the text input
925
+ */
926
+ textPlaceholder: string;
927
+ /**
928
+ * Editor-wide default style for every Text annotation's author line. Only
929
+ * applied when an annotation's `style.showAuthor` is true and
930
+ * `properties.author` is set; a given field here is overridden by that
931
+ * annotation's own `style.authorStyle` if set (see `TextStyle.authorStyle`).
932
+ */
933
+ authorStyle?: Partial<AuthorLineStyle>;
934
+ /**
935
+ * Minimum on-screen font size, in pixels, for a scalable (non-fixedSize)
936
+ * Text annotation's content or author line to actually be rendered.
937
+ * Below this, the text is skipped entirely (the box/background still
938
+ * renders) - avoids illegible sub-pixel text and the layout work that
939
+ * produces it. Ignored for `fixedSize` text (its on-screen size never
940
+ * shrinks with zoom) and during SVG/PNG export (export always renders
941
+ * in full, same as viewport culling). Set to 0 to disable.
942
+ */
943
+ minReadableFontSize: number;
944
+ /**
945
+ * Show send button in text editor
946
+ */
947
+ showSendButton: boolean;
948
+ /**
949
+ * Show edit button in text editor
950
+ */
951
+ showEditButton: boolean;
952
+ /**
953
+ * SVG icon for the send button in text editor
954
+ * Should be a complete SVG string (e.g., '<svg>...</svg>')
955
+ */
956
+ sendButtonIcon: string;
957
+ /**
958
+ * SVG icon for the edit button in text editor
959
+ * Should be a complete SVG string (e.g., '<svg>...</svg>')
960
+ */
961
+ editButtonIcon: string;
962
+ /**
963
+ * Minimum height of the arrow in units
964
+ */
965
+ minArrowHeight: number;
966
+ /**
967
+ * Maximum height of the arrow in units
968
+ */
969
+ maxArrowHeight: number;
970
+ /**
971
+ * Called to decide whether an annotation can be dragged, resized, restyled,
972
+ * text-edited, deleted, or re-linked. Defaults to always `true`. Selection
973
+ * (click to highlight, `getSelectedAnnotations()`) is unaffected - a
974
+ * non-editable annotation stays fully selectable, just not mutable.
975
+ *
976
+ * Keep this cheap and synchronous - it can run once per affected
977
+ * annotation on every relevant edit attempt. To react to a change that
978
+ * isn't reflected in the annotation's own data (e.g. a host-side
979
+ * "read-only mode" toggle), call `control.setOptions({ isEditable })`
980
+ * again with a new function reference - passing the same reference is a
981
+ * no-op.
982
+ */
983
+ isEditable: (annotation: Annotation) => boolean;
984
+ /**
985
+ * Called to decide whether an annotation is rendered (including in SVG
986
+ * export) and hit-testable (hover/select/drag via the mouse). Defaults to
987
+ * always `true`. A hidden annotation stays fully present in
988
+ * `getAnnotations()`, `getAnnotation()`, and `getSelectedAnnotations()` -
989
+ * visibility only controls what's drawn and clickable, not data access.
990
+ *
991
+ * Keep this cheap and synchronous - it can run once per annotation on
992
+ * every render pass while the view is changing (drag, pan, zoom). Same
993
+ * reactivity note as `isEditable` applies to changing this after the fact.
994
+ */
995
+ isVisible: (annotation: Annotation) => boolean;
996
+ };
997
+ type AnnotationOptions = {
998
+ handleSize: number;
999
+ placeholder?: string;
1000
+ };
1001
+ /** @private */
1002
+ type Cursor = "default" | "pointer" | "move" | "grab" | "grabbing" | "auto" | "resize" | "col-resize" | "row-resize" | "all-scroll" | "n-resize" | "e-resize" | "s-resize" | "w-resize" | "ne-resize" | "nw-resize" | "se-resize" | "sw-resize" | "ew-resize" | "ns-resize" | "nesw-resize" | "nwse-resize" | "alias" | "crosshair";
1003
+ type ClientMouseEvent = {
1004
+ clientX: number;
1005
+ clientY: number;
1006
+ };
1007
+ type DeepPartial<T> = {
1008
+ [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
1009
+ };
1010
+
1011
+ /**
1012
+ * Main controller class for managing annotations.
1013
+ * It manages rendering and editing of annotations.
1014
+ */
1015
+ declare class Control extends EventEmitter<FeatureEvents> {
1016
+ private ogma;
1017
+ private store;
1018
+ private renderers;
1019
+ private interactions;
1020
+ private editor;
1021
+ private links;
1022
+ private index;
1023
+ private drawing;
1024
+ private snapping;
1025
+ private selectionManager;
1026
+ private historyManager;
1027
+ private updateManager;
1028
+ private commentManager;
1029
+ constructor(ogma: Ogma, options?: Partial<ControllerOptions>);
1030
+ private initializeRenderers;
1031
+ private setupEvents;
1032
+ private onRotate;
1033
+ private onZoom;
1034
+ private onLayout;
1035
+ /**
1036
+ * The underlying Ogma instance this controller was created with. Needed by
1037
+ * UI built on top of `Control`'s public API (e.g. `TextAnnotationToolbar`)
1038
+ * that must mount its own `ogma.layers.addOverlay(...)` layer rather than
1039
+ * going through a renderer/handler internal to `Control`.
1040
+ */
1041
+ getOgma(): Ogma;
1042
+ /**
1043
+ * Current global annotation-rotation angle (radians) - see
1044
+ * `TextStyle`/`Handles`' `counterRotation`. Needed alongside `getZoom()` by
1045
+ * `TextAnnotationToolbar` to anchor its pill above a (possibly rotated)
1046
+ * Text box's world-space bounds.
1047
+ */
1048
+ getRotation(): number;
1049
+ /** Current zoom level - needed by `TextAnnotationToolbar` to convert a
1050
+ * `fixedSize` Text annotation's screen-pixel dimensions back to graph
1051
+ * units for its anchor-point math (same conversion `Handles` does). */
1052
+ getZoom(): number;
1053
+ /**
1054
+ * Set the options for the controller
1055
+ * @param options new Options
1056
+ * @returns the updated options
1057
+ */
1058
+ setOptions(options?: Partial<ControllerOptions>): {
1059
+ showSendButton: boolean;
1060
+ showEditButton: boolean;
1061
+ sendButtonIcon: string;
1062
+ editButtonIcon: string;
1063
+ minArrowHeight: number;
1064
+ maxArrowHeight: number;
1065
+ detectMargin: number;
1066
+ magnetRadius: number;
1067
+ magnetHandleRadius: number;
1068
+ textPlaceholder: string;
1069
+ authorStyle?: Partial<AuthorLineStyle> | undefined;
1070
+ minReadableFontSize: number;
1071
+ isEditable: (annotation: Annotation) => boolean;
1072
+ isVisible: (annotation: Annotation) => boolean;
1073
+ };
1074
+ /** Can `id` be dragged/resized/restyled/text-edited/deleted/re-linked
1075
+ * right now, per the `isEditable` option? `false` for an unknown id. */
1076
+ isAnnotationEditable(id: Id): boolean;
1077
+ /** Is `id` rendered and hit-testable right now, per the `isVisible`
1078
+ * option? `false` for an unknown id. */
1079
+ isAnnotationVisible(id: Id): boolean;
1080
+ /**
1081
+ * (Re-)apply `id`'s handler attachment (drag/resize/text-edit) to match
1082
+ * `isEditable` right now, instead of waiting for a deselect/reselect -
1083
+ * this is the same call a selection change makes internally. Call it
1084
+ * directly after changing whatever external state your `isEditable`
1085
+ * predicate depends on (e.g. a `locked` flag kept outside the
1086
+ * annotation - see the lock-toolbar-item.ts example): closes any open
1087
+ * text editor and detaches the handler if `id` just became
1088
+ * non-editable, or attaches it if `id` is selected and just became
1089
+ * editable again.
1090
+ */
1091
+ editFeature(id: Id): this;
1092
+ /**
1093
+ * Add an annotation to the controller
1094
+ * @param annotation The annotation to add
1095
+ */
1096
+ add(annotation: Annotation | AnnotationCollection): this;
1097
+ /**
1098
+ * Remove an annotation or an array of annotations from the controller
1099
+ * @param annotation The annotation(s) to remove
1100
+ */
1101
+ remove(annotation: Annotation | AnnotationCollection): this;
1102
+ /**
1103
+ * Undo the last change
1104
+ * @returns true if undo was successful, false if no changes to undo
1105
+ */
1106
+ undo(): boolean;
1107
+ /**
1108
+ * Redo the last undone change
1109
+ * @returns true if redo was successful, false if no changes to redo
1110
+ */
1111
+ redo(): boolean;
1112
+ /**
1113
+ * Check if there are changes to undo
1114
+ * @returns true if undo is possible
1115
+ */
1116
+ canUndo(): boolean;
1117
+ /**
1118
+ * Check if there are changes to redo
1119
+ * @returns true if redo is possible
1120
+ */
1121
+ canRedo(): boolean;
1122
+ /**
1123
+ * Clear the undo/redo history
1124
+ */
1125
+ clearHistory(): void;
1126
+ /**
1127
+ * Get all annotations in the controller
1128
+ * @returns A FeatureCollection containing all annotations
1129
+ */
1130
+ getAnnotations(): AnnotationCollection;
1131
+ /**
1132
+ * Select one or more annotations by id
1133
+ * @param annotations The id(s) of the annotation(s) to select
1134
+ * @returns this for chaining
1135
+ */
1136
+ select(annotations: Id | Id[]): this;
1137
+ /**
1138
+ * Unselect one or more annotations, or all if no ids provided
1139
+ * @param annotations The id(s) of the annotation(s) to unselect, or undefined to unselect all
1140
+ * @returns this for chaining
1141
+ */
1142
+ unselect(annotations?: Id | Id[]): this;
1143
+ /**
1144
+ * Cancel the current drawing operation
1145
+ * @returns this for chaining
1146
+ */
1147
+ cancelDrawing(): this;
1148
+ /**
1149
+ * Enable arrow drawing mode - the recommended way to add arrows.
1150
+ *
1151
+ * Call this method when the user clicks an "Add Arrow" button. The control will:
1152
+ * 1. Wait for the next mousedown event
1153
+ * 2. Create an arrow at that position with the specified style
1154
+ * 3. Start the interactive drawing process
1155
+ * 4. Clean up automatically when done
1156
+ *
1157
+ * **This is the recommended API for 99% of use cases.** Only use `startArrow()`
1158
+ * if you need to implement custom mouse handling or positioning logic.
1159
+ *
1160
+ * @example
1161
+ * ```ts
1162
+ * addArrowButton.addEventListener('click', () => {
1163
+ * control.enableArrowDrawing({ strokeColor: '#3A03CF', strokeWidth: 2 });
1164
+ * });
1165
+ * ```
1166
+ *
1167
+ * @param style Arrow style options
1168
+ * @returns this for chaining
1169
+ * @see startArrow for low-level programmatic control
1170
+ */
1171
+ enableArrowDrawing(style?: Partial<Arrow["properties"]["style"]>): this;
1172
+ /**
1173
+ * Enable text drawing mode - the recommended way to add text annotations.
1174
+ *
1175
+ * Call this method when the user clicks an "Add Text" button. The control will:
1176
+ * 1. Wait for the next mousedown event
1177
+ * 2. Create a text box at that position with the specified style
1178
+ * 3. Start the interactive drawing/editing process
1179
+ * 4. Clean up automatically when done
1180
+ *
1181
+ * **This is the recommended API for 99% of use cases.** Only use `startText()`
1182
+ * if you need to implement custom mouse handling or positioning logic.
1183
+ *
1184
+ * @example
1185
+ * ```ts
1186
+ * addTextButton.addEventListener('click', () => {
1187
+ * control.enableTextDrawing({ color: '#3A03CF', fontSize: 24 });
1188
+ * });
1189
+ * ```
1190
+ *
1191
+ * @param style Text style options
1192
+ * @returns this for chaining
1193
+ * @see startText for low-level programmatic control
1194
+ */
1195
+ enableTextDrawing(style?: Partial<Text["properties"]["style"]>): this;
1196
+ /**
1197
+ * Enable box drawing mode - the recommended way to add boxes.
1198
+ *
1199
+ * Call this method when the user clicks an "Add Box" button. The control will:
1200
+ * 1. Wait for the next mousedown event
1201
+ * 2. Create a box at that position with the specified style
1202
+ * 3. Start the interactive drawing process (drag to size)
1203
+ * 4. Clean up automatically when done
1204
+ *
1205
+ * **This is the recommended API for 99% of use cases.** Only use `startBox()`
1206
+ * if you need to implement custom mouse handling or positioning logic.
1207
+ *
1208
+ * @example
1209
+ * ```ts
1210
+ * addBoxButton.addEventListener('click', () => {
1211
+ * control.enableBoxDrawing({ background: '#EDE6FF', borderRadius: 8 });
1212
+ * });
1213
+ * ```
1214
+ *
1215
+ * @param style Box style options
1216
+ * @returns this for chaining
1217
+ * @see startBox for low-level programmatic control
1218
+ */
1219
+ enableBoxDrawing(style?: Partial<Box["properties"]["style"]>): this;
1220
+ /**
1221
+ * Enable polygon drawing mode - the recommended way to add polygons.
1222
+ *
1223
+ * Call this method when the user clicks an "Add Polygon" button. The control will:
1224
+ * 1. Wait for the next mousedown event
1225
+ * 2. Create a polygon starting at that position with the specified style
1226
+ * 3. Start the interactive drawing process (click points to draw shape)
1227
+ * 4. Clean up automatically when done
1228
+ *
1229
+ * **This is the recommended API for 99% of use cases.** Only use `startPolygon()`
1230
+ * if you need to implement custom mouse handling or positioning logic.
1231
+ *
1232
+ * @example
1233
+ * ```ts
1234
+ * addPolygonButton.addEventListener('click', () => {
1235
+ * control.enablePolygonDrawing({ strokeColor: '#3A03CF', background: 'rgba(58, 3, 207, 0.15)' });
1236
+ * });
1237
+ * ```
1238
+ *
1239
+ * @param style Polygon style options
1240
+ * @returns this for chaining
1241
+ * @see startPolygon for low-level programmatic control
1242
+ */
1243
+ enablePolygonDrawing(style?: Partial<Polygon["properties"]["style"]>): this;
1244
+ /**
1245
+ * Enable comment drawing mode - the recommended way to add comments.
1246
+ *
1247
+ * Call this method when the user clicks an "Add Comment" button. The control will:
1248
+ * 1. Wait for the next mousedown event
1249
+ * 2. Create a comment with an arrow pointing to that position
1250
+ * 3. Smart positioning: automatically finds the best placement for the comment box
1251
+ * 4. Start the interactive editing process
1252
+ * 5. Clean up automatically when done
1253
+ *
1254
+ * **This is the recommended API for 99% of use cases.** Only use `startComment()`
1255
+ * if you need to implement custom mouse handling or positioning logic.
1256
+ *
1257
+ * @example
1258
+ * ```ts
1259
+ * addCommentButton.addEventListener('click', () => {
1260
+ * control.enableCommentDrawing({
1261
+ * commentStyle: { color: '#3A03CF', background: '#EDE6FF' },
1262
+ * arrowStyle: { strokeColor: '#3A03CF', head: 'halo-dot' }
1263
+ * });
1264
+ * });
1265
+ * ```
1266
+ *
1267
+ * @param options Drawing options including offsets and styles
1268
+ * @param options.offsetX Manual X offset for comment placement (overrides smart positioning)
1269
+ * @param options.offsetY Manual Y offset for comment placement (overrides smart positioning)
1270
+ * @param options.commentStyle Style options for the comment box
1271
+ * @param options.arrowStyle Style options for the arrow
1272
+ * @returns this for chaining
1273
+ * @see startComment for low-level programmatic control
1274
+ */
1275
+ enableCommentDrawing(options?: {
1276
+ offsetX?: number;
1277
+ offsetY?: number;
1278
+ commentStyle?: Partial<CommentProps>;
1279
+ arrowStyle?: Partial<ArrowProperties>;
1280
+ }): this;
1281
+ /**
1282
+ * Enable sticky note drawing mode - drops a plain, resizable text box
1283
+ * (empty content, "Quick note…" ghost placeholder, no connector arrow)
1284
+ * like a Miro sticky note, unlike `enableCommentDrawing`. It's a regular
1285
+ * `text` annotation, so it's placed the same interactive way as
1286
+ * `enableBoxDrawing`/`enableTextDrawing`: click for a default-size square,
1287
+ * or drag to size it - either way it keeps the usual corner/edge drag
1288
+ * handles to resize it afterward.
1289
+ *
1290
+ * Call this method when the user clicks an "Add sticky note" button. The
1291
+ * control will:
1292
+ * 1. Wait for the next mousedown event
1293
+ * 2. Create the note at that position and start the interactive
1294
+ * corner-drag, already selected
1295
+ * 3. On release: a plain click (no drag) gets a default square size, a
1296
+ * drag gets sized to match instead - either way it drops straight
1297
+ * into editing (the placeholder is just ghost text, so typing
1298
+ * immediately replaces it)
1299
+ * 4. Clean up automatically when done
1300
+ *
1301
+ * @example
1302
+ * ```ts
1303
+ * addStickyNoteButton.addEventListener('click', () => {
1304
+ * control.enableStickyNoteDrawing({ background: '#FFEB99' });
1305
+ * });
1306
+ * ```
1307
+ *
1308
+ * @param style Sticky note style options (merged over the sticky note defaults)
1309
+ * @returns this for chaining
1310
+ * @see startStickyNote for low-level programmatic control
1311
+ */
1312
+ enableStickyNoteDrawing(style?: Partial<Text["properties"]["style"]>): this;
1313
+ /**
1314
+ * Enable erase mode: every click on an annotation deletes it immediately.
1315
+ * Stays armed across multiple clicks until `disableEraseMode()` is called,
1316
+ * or another drawing tool is enabled / `cancelDrawing()` is called.
1317
+ *
1318
+ * @returns this for chaining
1319
+ * @see disableEraseMode to turn erase mode off
1320
+ */
1321
+ enableEraseMode(): this;
1322
+ /** Turn erase mode off. No-op if it isn't active. */
1323
+ disableEraseMode(): this;
1324
+ /** Whether erase mode is currently active. */
1325
+ isEraseModeActive(): boolean;
1326
+ /**
1327
+ * Place a pre-created annotation by moving it with the cursor.
1328
+ * The annotation follows the mouse until the user clicks to place it.
1329
+ * Press Escape to cancel.
1330
+ *
1331
+ * @param annotation The text or box annotation to place
1332
+ * @returns this for chaining
1333
+ */
1334
+ enablePlacement(annotation: Text | Box): this;
1335
+ /**
1336
+ * **Advanced API:** Programmatically start drawing a comment at specific coordinates.
1337
+ *
1338
+ * This is a low-level method that gives you full control over the drawing process.
1339
+ * You must handle mouse events and create the comment object yourself.
1340
+ *
1341
+ * **For most use cases, use `enableCommentDrawing()` instead** - it handles all
1342
+ * mouse events and annotation creation automatically.
1343
+ *
1344
+ * Use this method only when you need:
1345
+ * - Custom mouse event handling (e.g., custom cursors, right-click menus)
1346
+ * - Programmatic placement without user interaction
1347
+ * - Integration with custom UI frameworks
1348
+ *
1349
+ * @example
1350
+ * ```ts
1351
+ * // Custom cursor example
1352
+ * ogma.setOptions({ cursor: { default: 'crosshair' } });
1353
+ * ogma.events.once('mousedown', (evt) => {
1354
+ * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
1355
+ * const comment = createComment(x, y, 'My comment', { color: '#3A03CF' });
1356
+ * control.startComment(x, y, comment);
1357
+ * });
1358
+ * ```
1359
+ *
1360
+ * @param x X coordinate to start drawing
1361
+ * @param y Y coordinate to start drawing
1362
+ * @param comment The comment annotation to add
1363
+ * @param options Drawing options including offsets and styles
1364
+ * @returns this for chaining
1365
+ * @see enableCommentDrawing for the recommended high-level API
1366
+ */
1367
+ startComment(x: number, y: number, comment: Comment, options?: {
1368
+ offsetX?: number;
1369
+ offsetY?: number;
1370
+ commentStyle?: Partial<CommentProps>;
1371
+ arrowStyle?: Partial<ArrowProperties>;
1372
+ }): this;
1373
+ /**
1374
+ * **Advanced API:** Programmatically start drawing a sticky note at
1375
+ * specific coordinates - same interactive corner-drag as `startBox`.
1376
+ * You must handle mouse events yourself (or immediately release/complete
1377
+ * it via the same events `enableStickyNoteDrawing` would).
1378
+ *
1379
+ * **For most use cases, use `enableStickyNoteDrawing()` instead.**
1380
+ *
1381
+ * @param x X coordinate for the note's top-left corner
1382
+ * @param y Y coordinate for the note's top-left corner
1383
+ * @param style Sticky note style options
1384
+ * @returns this for chaining
1385
+ * @see enableStickyNoteDrawing for the recommended high-level API
1386
+ */
1387
+ startStickyNote(x: number, y: number, style?: Partial<Text["properties"]["style"]>): this;
1388
+ /**
1389
+ * **Advanced API:** Programmatically start drawing a box at specific coordinates.
1390
+ *
1391
+ * This is a low-level method that gives you full control over the drawing process.
1392
+ * You must handle mouse events and optionally create the box object yourself.
1393
+ *
1394
+ * **For most use cases, use `enableBoxDrawing()` instead** - it handles all
1395
+ * mouse events and annotation creation automatically.
1396
+ *
1397
+ * Use this method only when you need:
1398
+ * - Custom mouse event handling (e.g., custom cursors, right-click menus)
1399
+ * - Programmatic placement without user interaction
1400
+ * - Integration with custom UI frameworks
1401
+ *
1402
+ * @example
1403
+ * ```ts
1404
+ * // Custom cursor example
1405
+ * ogma.setOptions({ cursor: { default: 'crosshair' } });
1406
+ * ogma.events.once('mousedown', (evt) => {
1407
+ * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
1408
+ * const box = createBox(x, y, 100, 50, { background: '#EDE6FF' });
1409
+ * control.startBox(x, y, box);
1410
+ * });
1411
+ * ```
1412
+ *
1413
+ * @param x X coordinate for the box origin
1414
+ * @param y Y coordinate for the box origin
1415
+ * @param box The box annotation to add (optional, will be created if not provided)
1416
+ * @returns this for chaining
1417
+ * @see enableBoxDrawing for the recommended high-level API
1418
+ */
1419
+ startBox(x: number, y: number, box?: Box): this;
1420
+ /**
1421
+ * **Advanced API:** Programmatically start drawing an arrow at specific coordinates.
1422
+ *
1423
+ * This is a low-level method that gives you full control over the drawing process.
1424
+ * You must handle mouse events and optionally create the arrow object yourself.
1425
+ *
1426
+ * **For most use cases, use `enableArrowDrawing()` instead** - it handles all
1427
+ * mouse events and annotation creation automatically.
1428
+ *
1429
+ * Use this method only when you need:
1430
+ * - Custom mouse event handling (e.g., custom cursors, right-click menus)
1431
+ * - Programmatic placement without user interaction
1432
+ * - Integration with custom UI frameworks
1433
+ *
1434
+ * @example
1435
+ * ```ts
1436
+ * // Custom cursor example
1437
+ * ogma.setOptions({ cursor: { default: 'crosshair' } });
1438
+ * ogma.events.once('mousedown', (evt) => {
1439
+ * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
1440
+ * const arrow = createArrow(x, y, x, y, { strokeColor: '#3A03CF' });
1441
+ * control.startArrow(x, y, arrow);
1442
+ * });
1443
+ * ```
1444
+ *
1445
+ * @param x X coordinate for the arrow start
1446
+ * @param y Y coordinate for the arrow start
1447
+ * @param arrow The arrow annotation to add (optional, will be created if not provided)
1448
+ * @returns this for chaining
1449
+ * @see enableArrowDrawing for the recommended high-level API
1450
+ */
1451
+ startArrow(x: number, y: number, arrow?: Arrow): this;
1452
+ /**
1453
+ * **Advanced API:** Programmatically start drawing a text annotation at specific coordinates.
1454
+ *
1455
+ * This is a low-level method that gives you full control over the drawing process.
1456
+ * You must handle mouse events and optionally create the text object yourself.
1457
+ *
1458
+ * **For most use cases, use `enableTextDrawing()` instead** - it handles all
1459
+ * mouse events and annotation creation automatically.
1460
+ *
1461
+ * Use this method only when you need:
1462
+ * - Custom mouse event handling (e.g., custom cursors, right-click menus)
1463
+ * - Programmatic placement without user interaction
1464
+ * - Integration with custom UI frameworks
1465
+ *
1466
+ * @example
1467
+ * ```ts
1468
+ * // Custom cursor example
1469
+ * ogma.setOptions({ cursor: { default: 'crosshair' } });
1470
+ * ogma.events.once('mousedown', (evt) => {
1471
+ * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
1472
+ * const text = createText(x, y, 0, 0, 'Hello', { color: '#3A03CF' });
1473
+ * control.startText(x, y, text);
1474
+ * });
1475
+ * ```
1476
+ *
1477
+ * @param x X coordinate for the text
1478
+ * @param y Y coordinate for the text
1479
+ * @param text The text annotation to add (optional, will be created if not provided)
1480
+ * @returns this for chaining
1481
+ * @see enableTextDrawing for the recommended high-level API
1482
+ */
1483
+ startText(x: number, y: number, text?: Text): this;
1484
+ /**
1485
+ * **Advanced API:** Programmatically start drawing a polygon at specific coordinates.
1486
+ *
1487
+ * This is a low-level method that gives you full control over the drawing process.
1488
+ * You must handle mouse events and create the polygon object yourself.
1489
+ *
1490
+ * **For most use cases, use `enablePolygonDrawing()` instead** - it handles all
1491
+ * mouse events and annotation creation automatically.
1492
+ *
1493
+ * Use this method only when you need:
1494
+ * - Custom mouse event handling (e.g., custom cursors, right-click menus)
1495
+ * - Programmatic placement without user interaction
1496
+ * - Integration with custom UI frameworks
1497
+ *
1498
+ * @example
1499
+ * ```ts
1500
+ * // Custom cursor example
1501
+ * ogma.setOptions({ cursor: { default: 'crosshair' } });
1502
+ * ogma.events.once('mousedown', (evt) => {
1503
+ * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
1504
+ * const polygon = createPolygon([[[x, y]]], { strokeColor: '#3A03CF' });
1505
+ * control.startPolygon(x, y, polygon);
1506
+ * });
1507
+ * ```
1508
+ *
1509
+ * @param x X coordinate to start drawing
1510
+ * @param y Y coordinate to start drawing
1511
+ * @param polygon The polygon annotation to add
1512
+ * @returns this for chaining
1513
+ * @see enablePolygonDrawing for the recommended high-level API
1514
+ */
1515
+ startPolygon(x: number, y: number, polygon: Polygon): this;
1516
+ /**
1517
+ * Get the currently selected annotations as a collection
1518
+ * @returns A FeatureCollection of selected annotations
1519
+ */
1520
+ getSelectedAnnotations(): AnnotationCollection;
1521
+ /**
1522
+ * Get the first selected annotation (for backwards compatibility)
1523
+ * @returns The currently selected annotation, or null if none selected
1524
+ */
1525
+ getSelected(): Annotation | null;
1526
+ /**
1527
+ * Get a specific annotation by id
1528
+ * @param id The id of the annotation to retrieve
1529
+ * @returns The annotation with the given id, or undefined if not found
1530
+ */
1531
+ getAnnotation<T = Annotation>(id: Id): T | undefined;
1532
+ /**
1533
+ * Scale an annotation by a given factor around an origin point
1534
+ * @param id The id of the annotation to scale
1535
+ * @param scale The scale factor
1536
+ * @param ox Origin x coordinate
1537
+ * @param oy Origin y coordinate
1538
+ * @returns this for chaining
1539
+ */
1540
+ setScale(id: Id, scale: number, ox: number, oy: number): this;
1541
+ /**
1542
+ * Toggle a comment between collapsed and expanded mode
1543
+ * @param id The id of the comment to toggle
1544
+ * @returns this for chaining
1545
+ */
1546
+ toggleComment(id: Id): this;
1547
+ /**
1548
+ * Destroy the controller and its elements
1549
+ */
1550
+ destroy(): void;
1551
+ /**
1552
+ * Update the style of the annotation with the given id
1553
+ * @param id The id of the annotation to update
1554
+ * @param style The new style
1555
+ */
1556
+ updateStyle<A extends Annotation>(id: Id, style: A["properties"]["style"]): this;
1557
+ /**
1558
+ * Update an annotation with partial updates
1559
+ *
1560
+ * This method allows you to update any properties of an annotation, including
1561
+ * geometry, properties, and style. Updates are merged with existing data.
1562
+ *
1563
+ * @param annotation Partial annotation object with id and properties to update
1564
+ * @returns this for chaining
1565
+ *
1566
+ * @example
1567
+ * ```ts
1568
+ * // Update arrow geometry
1569
+ * controller.update({
1570
+ * id: arrowId,
1571
+ * geometry: {
1572
+ * type: 'LineString',
1573
+ * coordinates: [[0, 0], [200, 200]]
1574
+ * }
1575
+ * });
1576
+ *
1577
+ * // Update text content and position
1578
+ * controller.update({
1579
+ * id: textId,
1580
+ * geometry: {
1581
+ * type: 'Point',
1582
+ * coordinates: [100, 100]
1583
+ * },
1584
+ * properties: {
1585
+ * content: 'Updated text'
1586
+ * }
1587
+ * });
1588
+ *
1589
+ * // Update style only (prefer updateStyle for style-only updates)
1590
+ * controller.update({
1591
+ * id: boxId,
1592
+ * properties: {
1593
+ * style: {
1594
+ * background: '#ff0000'
1595
+ * }
1596
+ * }
1597
+ * });
1598
+ * ```
1599
+ */
1600
+ update<A extends Annotation>(annotation: DeepPartial<A> & {
1601
+ id: Id;
1602
+ }): this;
1603
+ /**
1604
+ * Attach an arrow to a node at the specified side
1605
+ * @param arrowId
1606
+ * @param targetNode
1607
+ * @param side
1608
+ */
1609
+ link(arrowId: Id, targetNode: Node, side: Side): this;
1610
+ /**
1611
+ * Attach an arrow to an annotation at the specified side
1612
+ * @param arrowId
1613
+ * @param target
1614
+ * @param side
1615
+ */
1616
+ link(arrowId: Id, target: Id, side: Side): this;
1617
+ isDrawing(): boolean;
1618
+ }
1619
+
1620
+ export { EVT_UNSELECT as $, DATA_ATTR as D, DEFAULT_EDIT_ICON as E, DEFAULT_SEND_ICON as F, EVT_ADD as K, EVT_CANCEL_DRAWING as L, EVT_CLICK as M, EVT_COMPLETE_DRAWING as N, EVT_DRAG as O, EVT_DRAG_END as Q, EVT_DRAG_START as U, EVT_HISTORY as V, EVT_HOVER as W, EVT_LINK as X, EVT_REMOVE as Y, EVT_SELECT as Z, EVT_UNHOVER as _, isBox as a$, EVT_UPDATE as a0, HL_BRIGHTEN as a7, LAYERS as a9, createComment as aA, createCommentWithArrow as aB, createPolygon as aC, createText as aD, cursors as aE, defaultArrowOptions as aF, defaultArrowStyle as aG, defaultBoxOptions as aH, defaultBoxStyle as aI, defaultCommentOptions as aJ, defaultCommentStyle as aK, defaultPolygonProperties as aL, defaultPolygonStyle as aM, defaultTextOptions as aN, defaultTextStyle as aO, detectArrow as aP, detectBox as aQ, detectComment as aR, detectPolygon as aS, detectText as aT, getCommentPosition as aU, getCommentSize as aV, getCommentZoomThreshold as aW, handleDetectionThreshold as aX, handleRadius as aY, isAnnotationCollection as aZ, isArrow as a_, NONE as ac, SIDE_END as ah, SIDE_START as ai, TARGET_TYPES as an, TEXT_LINE_HEIGHT as ao, asColor as at, asHexColor as au, asRgbColor as av, asRgbaColor as aw, calculateCommentZoomThreshold as ax, createArrow as ay, createBox as az, isColor as b0, isComment as b1, isHexColor as b2, isPolygon as b3, isRgbColor as b4, isRgbaColor as b5, isRigidConnector as b6, isStickyNote as b7, isText as b8, toggleCommentMode as b9, COMMENT_MODE_COLLAPSED as r, COMMENT_MODE_EXPANDED as s, Control as x };
1621
+ export type { AnnotationCollection as A, Bounds as B, Color as C, DeepPartial as G, HexColor as H, Id as I, DragEvent as J, Polygon as P, RgbaColor as R, Side as S, Text as T, Point as a, EdgeMagnet as a1, ExportedLink as a2, Extremity as a3, FeatureEvent as a4, FeatureEvents as a5, FeaturesEvent as a6, HistoryEvent as a8, Link as aa, Magnet as ab, NodeMagnet as ad, PolygonMagnet as ae, PolygonProperties as af, PolygonStyle as ag, Stroke as aj, StrokeOptions as ak, StrokeStyle as al, StrokeType as am, TargetType as ap, TextProperties as aq, TextStyle as ar, Vector as as, ClientMouseEvent as b, Arrow as c, Annotation as d, RgbColor as e, Box as f, AnnotationFeature as g, AnnotationGetter as h, AnnotationOptions as i, AnnotationProps as j, AnnotationType as k, ArrowProperties as l, ArrowStyles as m, AuthorLineStyle as n, BoxMagnet as o, BoxProperties as p, BoxStyle as q, ClickEvent as t, Comment as u, CommentProps as v, CommentStyle as w, ControllerOptions as y, Cursor as z };