@linkurious/ogma-annotations 2.1.1 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1415 +1,742 @@
1
- import { default as default_2 } from 'eventemitter3';
2
- import { Feature } from 'geojson';
3
- import { FeatureCollection } from 'geojson';
4
- import { GeometryObject } from 'geojson';
5
- import { LineString } from 'geojson';
6
- import { Node as Node_2 } from '@linkurious/ogma';
7
- import { Ogma } from '@linkurious/ogma';
8
- import { Point as Point_2 } from 'geojson';
9
- import { Polygon as Polygon_2 } from 'geojson';
10
-
11
- /** Union type of all Annotation features */
12
- declare type Annotation = Arrow | Box | Text_2 | Comment_2 | Polygon;
13
-
14
- /** Collection of Annotations, GeoJSON FeatureCollection */
15
- declare interface AnnotationCollection extends FeatureCollection {
16
- features: Annotation[];
17
- }
18
-
19
- /**
20
- * Base interface for all annotation features.
21
- * @template G - Geometry type
22
- * @template P - Properties type
23
- */
24
- declare interface AnnotationFeature<G extends GeometryObject = GeometryObject, P = AnnotationProps> extends Feature<G, P> {
25
- /** Unique identifier for the annotation */
26
- id: Id;
27
- }
28
-
29
- export declare class AnnotationPanel {
30
- private control;
31
- private panel;
32
- private panelBody;
33
- private mode;
34
- private currentAnnotation;
35
- private currentColor;
36
- private recent;
37
- private colorCircles;
38
- private colorPickerOverlay;
39
- private colorPicker;
40
- private detachVisibility;
41
- private documentClickHandler;
42
- constructor(options: AnnotationPanelOptions);
43
- private setAnnotation;
44
- private renderArrow;
45
- private renderText;
46
- private renderPolygon;
47
- private section;
48
- private icon;
49
- private colorSelector;
50
- private backgroundSelector;
51
- private fontSelector;
52
- private extremitySelector;
53
- private dropdown;
54
- private slider;
55
- private lineTypeButtons;
56
- private bind;
57
- private updateColorFromAnnotation;
58
- private updateColorCircles;
59
- private toggleColorPicker;
60
- private closeColorPicker;
61
- private updateStyle;
62
- setPlacement(placement: PanelPlacement): void;
63
- setOrientation(orientation: PanelOrientation): void;
64
- show(): void;
65
- hide: () => void;
66
- destroy(): void;
67
- }
68
-
69
- export declare interface AnnotationPanelOptions {
70
- control: Control;
71
- /**
72
- * Element the panel mounts into. The panel creates and manages its own root
73
- * `<div class="annotation-panel">` inside it. Defaults to `document.body`.
74
- */
75
- container?: HTMLElement;
76
- /**
77
- * Which screen edge/corner the panel docks to. Defaults to `"right"`
78
- * (vertically centered on the right edge — the original look). Change at
79
- * runtime with {@link AnnotationPanel.setPlacement}.
80
- */
81
- placement?: PanelPlacement;
82
- /**
83
- * Whether panel sections stack vertically or run horizontally as a
84
- * toolbar. Defaults to `"vertical"`. Change at runtime with
85
- * {@link AnnotationPanel.setOrientation}.
86
- */
87
- orientation?: PanelOrientation;
88
- }
89
-
90
- /**
91
- * Base properties for all annotations.
92
- */
93
- declare interface AnnotationProps {
94
- /** Type of annotation */
95
- type: AnnotationType;
96
- /** Optional style configuration */
97
- style?: unknown;
98
- }
99
-
100
- /**
101
- * Styled drawing/undo-redo toolbar: add arrow, comment, box, text and
102
- * polygon annotations, undo/redo, and delete the current selection. The
103
- * React equivalent is `AddMenu` (`@linkurious/ogma-annotations-react/ui`) -
104
- * this class mirrors its buttons and defaults for a vanilla consumer.
105
- */
106
- export declare class AnnotationToolbar {
107
- private control;
108
- private root;
109
- private activeButton;
110
- private undoButton;
111
- private redoButton;
112
- private handleDrawingEnd;
113
- private handleHistory;
114
- constructor(options: AnnotationToolbarOptions);
115
- private drawingButton;
116
- private button;
117
- private separator;
118
- private setActiveMode;
119
- private updateUndoRedo;
120
- setPlacement(placement: PanelPlacement): void;
121
- setOrientation(orientation: PanelOrientation): void;
122
- destroy(): void;
123
- }
124
-
125
- export declare interface AnnotationToolbarOptions {
126
- control: Control;
127
- /**
128
- * Element the toolbar mounts into. The toolbar creates and manages its
129
- * own root `<div class="annotation-toolbar">` inside it. Defaults to
130
- * `document.body`.
131
- */
132
- container?: HTMLElement;
133
- /**
134
- * Which screen edge/corner the toolbar docks to. Defaults to `"bottom"`.
135
- * Change at runtime with {@link AnnotationToolbar.setPlacement}.
136
- */
137
- placement?: PanelPlacement;
138
- /**
139
- * Whether buttons run left-to-right or stack top-to-bottom. Defaults to
140
- * `"horizontal"`. Change at runtime with
141
- * {@link AnnotationToolbar.setOrientation}.
142
- */
143
- orientation?: PanelOrientation;
144
- /**
145
- * Which drawing tools to show, and in what order. Defaults to all six:
146
- * `["arrow", "comment", "sticky-note", "box", "text", "polygon"]`.
147
- */
148
- enabledTypes?: ToolbarDrawingType[];
149
- /** Per-type default style overrides for the drawing tool buttons. */
150
- styles?: AnnotationToolbarStyles;
151
- /**
152
- * Which deletion control(s) to show - the erase tool, the
153
- * select-then-trash button, or both. Defaults to `"erase"`.
154
- */
155
- deleteMode?: DeleteMode;
156
- /** Called when the SVG export button is clicked. Omit to hide the button. */
157
- onSvgExport?: () => void;
158
- /** Called when the JSON export button is clicked. Omit to hide the button. */
159
- onJsonExport?: () => void;
160
- }
161
-
162
- /**
163
- * Per-type default style overrides, merged over the toolbar's own built-in
164
- * defaults (which stay in effect for anything you don't override). See
165
- * `Control.enableArrowDrawing` etc. for what each shape configures.
166
- */
167
- export declare interface AnnotationToolbarStyles {
168
- arrow?: Partial<Arrow["properties"]["style"]>;
169
- box?: Partial<Box["properties"]["style"]>;
170
- text?: Partial<Text_2["properties"]["style"]>;
171
- polygon?: Partial<Polygon["properties"]["style"]>;
172
- comment?: {
173
- offsetX?: number;
174
- offsetY?: number;
175
- commentStyle?: Partial<CommentProps>;
176
- arrowStyle?: Partial<ArrowProperties>;
177
- };
178
- stickyNote?: Partial<Text_2["properties"]["style"]>;
179
- }
180
-
181
- /** Types of annotations supported */
182
- declare type AnnotationType = "arrow" | "text" | "box" | "comment" | "polygon";
183
-
184
- /**
185
- * Arrow annotation feature. Represents a directed line between two points,
186
- * can connect a textbox to a shape.
187
- */
188
- declare interface Arrow extends AnnotationFeature<LineString, ArrowProperties> {
189
- }
190
-
191
- declare interface ArrowProperties extends AnnotationProps {
192
- type: "arrow";
193
- style?: ArrowStyles;
194
- link?: Partial<Record<Side, ExportedLink>>;
195
- }
196
-
197
- /**
198
- * Styles specific to arrow annotations.
199
- */
200
- declare interface ArrowStyles extends StrokeOptions {
201
- /** Tail extremity style */
202
- tail?: Extremity;
203
- /** Head extremity style */
204
- head?: Extremity;
205
- }
206
-
207
- /**
208
- * Wires `control` events to show/hide callbacks. Returns a `detach` function
209
- * that removes every listener it registered.
210
- */
211
- export declare function attachPanelVisibility(control: PanelVisibilityControl, { onShow, onHide }: PanelVisibilityHandlers): () => void;
212
-
213
- export declare interface BackgroundOption {
214
- value: string;
215
- /** Inline style for the swatch's color circle. */
216
- style: string;
217
- }
218
-
219
- export declare const BACKGROUNDS: BackgroundOption[];
220
-
221
- /**
222
- * Box annotation feature
223
- */
224
- declare interface Box extends AnnotationFeature<Point_2, BoxProperties> {
225
- }
226
-
227
- /**
228
- * Arrow snapped to a rectangular annotation (text, box, comment).
229
- * nx/ny are center-relative fractions multiplied by width/height:
230
- * left-center = { nx: -0.5, ny: 0 }
231
- * right-center = { nx: 0.5, ny: 0 }
232
- * center = { nx: 0, ny: 0 }
233
- */
234
- declare type BoxMagnet = {
235
- type: "box";
236
- nx: number;
237
- ny: number;
238
- };
239
-
240
- /** Properties specific to box annotations. */
241
- declare interface BoxProperties extends AnnotationProps {
242
- type: "box";
243
- /** Width of the box */
244
- width: number;
245
- /** Height of the box */
246
- height: number;
247
- /** Style options for the box */
248
- style?: BoxStyle;
249
- }
250
-
251
- /** Styles specific to box annotations. */
252
- declare interface BoxStyle extends StrokeOptions {
253
- /** background color: empty for transparent #f00, yellow...*/
254
- background?: Color;
255
- /** padding around the box */
256
- padding?: number;
257
- /** border radius */
258
- borderRadius?: number;
259
- /** if true, the box scales with zoom. Default is true */
260
- scaled?: boolean;
261
- /** box shadow in CSS format, e.g. "0px 4px 6px rgba(0, 0, 0, 0.1)" */
262
- boxShadow?: string;
263
- }
264
-
265
- /** Event related to a single annotation feature */
266
- declare interface ClickEvent {
267
- /** Annotation ID involved in the event */
268
- id?: Id;
269
- /** Mouse position in pixel coordinates */
270
- position: {
271
- x: number;
272
- y: number;
273
- };
274
- }
275
-
276
- /**
277
- * Any valid color format
278
- */
279
- declare type Color = HexColor | RgbColor | RgbaColor_2 | "transparent" | "none" | string;
280
-
281
- /**
282
- * Comment annotation type
283
- * Geometry: Point (center position of comment box/icon)
284
- *
285
- * Note: Arrows are stored separately in Arrow features.
286
- * Arrows reference comments via their link.start or link.end properties.
287
- */
288
- declare interface Comment_2 extends AnnotationFeature<Point_2, CommentProps> {
289
- }
290
-
291
- declare const COMMENT_MODE_COLLAPSED = "collapsed";
292
-
293
- declare const COMMENT_MODE_EXPANDED = "expanded";
294
-
295
- /**
296
- * Properties for Comment annotations
297
- *
298
- * Comments are specialized annotations that:
299
- * - Always maintain fixed screen-space size
300
- * - Always have at least one arrow pointing TO them
301
- * - Can be collapsed (icon) or expanded (text box)
302
- * - Support multiple arrows pointing to them
303
- */
304
- declare interface CommentProps extends AnnotationProps {
305
- type: "comment";
306
- /** Text content (similar to text annotation) */
307
- content: string;
308
- /** Display mode: collapsed (icon) or expanded (text box) */
309
- mode: typeof COMMENT_MODE_COLLAPSED | typeof COMMENT_MODE_EXPANDED;
310
- /** Width in expanded mode (pixels) */
311
- width: number;
312
- /** Height (auto-grows with content, pixels) */
313
- height: number;
314
- /** Optional metadata */
315
- author?: string;
316
- timestamp?: Date;
317
- /** Styling */
318
- style?: CommentStyle;
319
- }
320
-
321
- /**
322
- * Style configuration for Comment annotations
323
- */
324
- declare interface CommentStyle extends TextStyle {
325
- /** Background color for collapsed icon (default: "#FFD700") */
326
- iconColor?: Color;
327
- /** Icon to display when collapsed (default: "💬") */
328
- iconSymbol?: string;
329
- /** Border color for collapsed icon */
330
- iconBorderColor?: Color;
331
- /** Border width for collapsed icon */
332
- iconBorderWidth?: number;
333
- /** Minimum height (default: 60px) */
334
- minHeight?: number;
335
- /** Maximum height before scrolling (default: 480px, undefined = no limit) */
336
- maxHeight?: number;
337
- /** Size when collapsed (default: 32px) */
338
- iconSize?: number;
339
- /** Zoom threshold below which comment auto-collapses (default: 0.5) */
340
- collapseZoomThreshold?: number;
341
- /** Show "send" button in edit mode (default: true) */
342
- showSendButton?: boolean;
343
- /** Auto-grow height with content (default: true) */
344
- autoGrow?: boolean;
345
- /** Show drop shadow on comment box (default: true) */
346
- shadow?: boolean;
347
- /** Expand to full width when selected (default: false) */
348
- expandOnSelect?: boolean;
349
- }
350
-
351
- /**
352
- * Main controller class for managing annotations.
353
- * It manages rendering and editing of annotations.
354
- */
355
- declare class Control extends default_2<FeatureEvents> {
356
- private ogma;
357
- private store;
358
- private renderers;
359
- private interactions;
360
- private editor;
361
- private links;
362
- private index;
363
- private drawing;
364
- private snapping;
365
- private selectionManager;
366
- private historyManager;
367
- private updateManager;
368
- private commentManager;
369
- constructor(ogma: Ogma, options?: Partial<ControllerOptions>);
370
- private initializeRenderers;
371
- private setupEvents;
372
- private onRotate;
373
- private onZoom;
374
- private onLayout;
375
- /**
376
- * Set the options for the controller
377
- * @param options new Options
378
- * @returns the updated options
379
- */
380
- setOptions(options?: Partial<ControllerOptions>): {
381
- showSendButton: boolean;
382
- showEditButton: boolean;
383
- sendButtonIcon: string;
384
- editButtonIcon: string;
385
- minArrowHeight: number;
386
- maxArrowHeight: number;
387
- detectMargin: number;
388
- magnetRadius: number;
389
- magnetHandleRadius: number;
390
- textPlaceholder: string;
391
- };
392
- /**
393
- * Add an annotation to the controller
394
- * @param annotation The annotation to add
395
- */
396
- add(annotation: Annotation | AnnotationCollection): this;
397
- /**
398
- * Remove an annotation or an array of annotations from the controller
399
- * @param annotation The annotation(s) to remove
400
- */
401
- remove(annotation: Annotation | AnnotationCollection): this;
402
- /**
403
- * Undo the last change
404
- * @returns true if undo was successful, false if no changes to undo
405
- */
406
- undo(): boolean;
407
- /**
408
- * Redo the last undone change
409
- * @returns true if redo was successful, false if no changes to redo
410
- */
411
- redo(): boolean;
412
- /**
413
- * Check if there are changes to undo
414
- * @returns true if undo is possible
415
- */
416
- canUndo(): boolean;
417
- /**
418
- * Check if there are changes to redo
419
- * @returns true if redo is possible
420
- */
421
- canRedo(): boolean;
422
- /**
423
- * Clear the undo/redo history
424
- */
425
- clearHistory(): void;
426
- /**
427
- * Get all annotations in the controller
428
- * @returns A FeatureCollection containing all annotations
429
- */
430
- getAnnotations(): AnnotationCollection;
431
- /**
432
- * Select one or more annotations by id
433
- * @param annotations The id(s) of the annotation(s) to select
434
- * @returns this for chaining
435
- */
436
- select(annotations: Id | Id[]): this;
437
- /**
438
- * Unselect one or more annotations, or all if no ids provided
439
- * @param annotations The id(s) of the annotation(s) to unselect, or undefined to unselect all
440
- * @returns this for chaining
441
- */
442
- unselect(annotations?: Id | Id[]): this;
443
- /**
444
- * Cancel the current drawing operation
445
- * @returns this for chaining
446
- */
447
- cancelDrawing(): this;
448
- /**
449
- * Enable arrow drawing mode - the recommended way to add arrows.
450
- *
451
- * Call this method when the user clicks an "Add Arrow" button. The control will:
452
- * 1. Wait for the next mousedown event
453
- * 2. Create an arrow at that position with the specified style
454
- * 3. Start the interactive drawing process
455
- * 4. Clean up automatically when done
456
- *
457
- * **This is the recommended API for 99% of use cases.** Only use `startArrow()`
458
- * if you need to implement custom mouse handling or positioning logic.
459
- *
460
- * @example
461
- * ```ts
462
- * addArrowButton.addEventListener('click', () => {
463
- * control.enableArrowDrawing({ strokeColor: '#3A03CF', strokeWidth: 2 });
464
- * });
465
- * ```
466
- *
467
- * @param style Arrow style options
468
- * @returns this for chaining
469
- * @see startArrow for low-level programmatic control
470
- */
471
- enableArrowDrawing(style?: Partial<Arrow["properties"]["style"]>): this;
472
- /**
473
- * Enable text drawing mode - the recommended way to add text annotations.
474
- *
475
- * Call this method when the user clicks an "Add Text" button. The control will:
476
- * 1. Wait for the next mousedown event
477
- * 2. Create a text box at that position with the specified style
478
- * 3. Start the interactive drawing/editing process
479
- * 4. Clean up automatically when done
480
- *
481
- * **This is the recommended API for 99% of use cases.** Only use `startText()`
482
- * if you need to implement custom mouse handling or positioning logic.
483
- *
484
- * @example
485
- * ```ts
486
- * addTextButton.addEventListener('click', () => {
487
- * control.enableTextDrawing({ color: '#3A03CF', fontSize: 24 });
488
- * });
489
- * ```
490
- *
491
- * @param style Text style options
492
- * @returns this for chaining
493
- * @see startText for low-level programmatic control
494
- */
495
- enableTextDrawing(style?: Partial<Text_2["properties"]["style"]>): this;
496
- /**
497
- * Enable box drawing mode - the recommended way to add boxes.
498
- *
499
- * Call this method when the user clicks an "Add Box" button. The control will:
500
- * 1. Wait for the next mousedown event
501
- * 2. Create a box at that position with the specified style
502
- * 3. Start the interactive drawing process (drag to size)
503
- * 4. Clean up automatically when done
504
- *
505
- * **This is the recommended API for 99% of use cases.** Only use `startBox()`
506
- * if you need to implement custom mouse handling or positioning logic.
507
- *
508
- * @example
509
- * ```ts
510
- * addBoxButton.addEventListener('click', () => {
511
- * control.enableBoxDrawing({ background: '#EDE6FF', borderRadius: 8 });
512
- * });
513
- * ```
514
- *
515
- * @param style Box style options
516
- * @returns this for chaining
517
- * @see startBox for low-level programmatic control
518
- */
519
- enableBoxDrawing(style?: Partial<Box["properties"]["style"]>): this;
520
- /**
521
- * Enable polygon drawing mode - the recommended way to add polygons.
522
- *
523
- * Call this method when the user clicks an "Add Polygon" button. The control will:
524
- * 1. Wait for the next mousedown event
525
- * 2. Create a polygon starting at that position with the specified style
526
- * 3. Start the interactive drawing process (click points to draw shape)
527
- * 4. Clean up automatically when done
528
- *
529
- * **This is the recommended API for 99% of use cases.** Only use `startPolygon()`
530
- * if you need to implement custom mouse handling or positioning logic.
531
- *
532
- * @example
533
- * ```ts
534
- * addPolygonButton.addEventListener('click', () => {
535
- * control.enablePolygonDrawing({ strokeColor: '#3A03CF', background: 'rgba(58, 3, 207, 0.15)' });
536
- * });
537
- * ```
538
- *
539
- * @param style Polygon style options
540
- * @returns this for chaining
541
- * @see startPolygon for low-level programmatic control
542
- */
543
- enablePolygonDrawing(style?: Partial<Polygon["properties"]["style"]>): this;
544
- /**
545
- * Enable comment drawing mode - the recommended way to add comments.
546
- *
547
- * Call this method when the user clicks an "Add Comment" button. The control will:
548
- * 1. Wait for the next mousedown event
549
- * 2. Create a comment with an arrow pointing to that position
550
- * 3. Smart positioning: automatically finds the best placement for the comment box
551
- * 4. Start the interactive editing process
552
- * 5. Clean up automatically when done
553
- *
554
- * **This is the recommended API for 99% of use cases.** Only use `startComment()`
555
- * if you need to implement custom mouse handling or positioning logic.
556
- *
557
- * @example
558
- * ```ts
559
- * addCommentButton.addEventListener('click', () => {
560
- * control.enableCommentDrawing({
561
- * commentStyle: { color: '#3A03CF', background: '#EDE6FF' },
562
- * arrowStyle: { strokeColor: '#3A03CF', head: 'halo-dot' }
563
- * });
564
- * });
565
- * ```
566
- *
567
- * @param options Drawing options including offsets and styles
568
- * @param options.offsetX Manual X offset for comment placement (overrides smart positioning)
569
- * @param options.offsetY Manual Y offset for comment placement (overrides smart positioning)
570
- * @param options.commentStyle Style options for the comment box
571
- * @param options.arrowStyle Style options for the arrow
572
- * @returns this for chaining
573
- * @see startComment for low-level programmatic control
574
- */
575
- enableCommentDrawing(options?: {
576
- offsetX?: number;
577
- offsetY?: number;
578
- commentStyle?: Partial<CommentProps>;
579
- arrowStyle?: Partial<ArrowProperties>;
580
- }): this;
581
- /**
582
- * Enable sticky note drawing mode - drops a plain, resizable text box
583
- * (empty content, "Quick note…" ghost placeholder, no connector arrow)
584
- * like a Miro sticky note, unlike `enableCommentDrawing`. It's a regular
585
- * `text` annotation, so it's placed the same interactive way as
586
- * `enableBoxDrawing`/`enableTextDrawing`: click for a default-size square,
587
- * or drag to size it - either way it keeps the usual corner/edge drag
588
- * handles to resize it afterward.
589
- *
590
- * Call this method when the user clicks an "Add sticky note" button. The
591
- * control will:
592
- * 1. Wait for the next mousedown event
593
- * 2. Create the note at that position and start the interactive
594
- * corner-drag, already selected
595
- * 3. On release: a plain click (no drag) gets a default square size, a
596
- * drag gets sized to match instead - either way it drops straight
597
- * into editing (the placeholder is just ghost text, so typing
598
- * immediately replaces it)
599
- * 4. Clean up automatically when done
600
- *
601
- * @example
602
- * ```ts
603
- * addStickyNoteButton.addEventListener('click', () => {
604
- * control.enableStickyNoteDrawing({ background: '#FFEB99' });
605
- * });
606
- * ```
607
- *
608
- * @param style Sticky note style options (merged over the sticky note defaults)
609
- * @returns this for chaining
610
- * @see startStickyNote for low-level programmatic control
611
- */
612
- enableStickyNoteDrawing(style?: Partial<Text_2["properties"]["style"]>): this;
613
- /**
614
- * Enable erase mode: every click on an annotation deletes it immediately.
615
- * Stays armed across multiple clicks until `disableEraseMode()` is called,
616
- * or another drawing tool is enabled / `cancelDrawing()` is called.
617
- *
618
- * @returns this for chaining
619
- * @see disableEraseMode to turn erase mode off
620
- */
621
- enableEraseMode(): this;
622
- /** Turn erase mode off. No-op if it isn't active. */
623
- disableEraseMode(): this;
624
- /** Whether erase mode is currently active. */
625
- isEraseModeActive(): boolean;
626
- /**
627
- * Place a pre-created annotation by moving it with the cursor.
628
- * The annotation follows the mouse until the user clicks to place it.
629
- * Press Escape to cancel.
630
- *
631
- * @param annotation The text or box annotation to place
632
- * @returns this for chaining
633
- */
634
- enablePlacement(annotation: Text_2 | Box): this;
635
- /**
636
- * **Advanced API:** Programmatically start drawing a comment at specific coordinates.
637
- *
638
- * This is a low-level method that gives you full control over the drawing process.
639
- * You must handle mouse events and create the comment object yourself.
640
- *
641
- * **For most use cases, use `enableCommentDrawing()` instead** - it handles all
642
- * mouse events and annotation creation automatically.
643
- *
644
- * Use this method only when you need:
645
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
646
- * - Programmatic placement without user interaction
647
- * - Integration with custom UI frameworks
648
- *
649
- * @example
650
- * ```ts
651
- * // Custom cursor example
652
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
653
- * ogma.events.once('mousedown', (evt) => {
654
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
655
- * const comment = createComment(x, y, 'My comment', { color: '#3A03CF' });
656
- * control.startComment(x, y, comment);
657
- * });
658
- * ```
659
- *
660
- * @param x X coordinate to start drawing
661
- * @param y Y coordinate to start drawing
662
- * @param comment The comment annotation to add
663
- * @param options Drawing options including offsets and styles
664
- * @returns this for chaining
665
- * @see enableCommentDrawing for the recommended high-level API
666
- */
667
- startComment(x: number, y: number, comment: Comment_2, options?: {
668
- offsetX?: number;
669
- offsetY?: number;
670
- commentStyle?: Partial<CommentProps>;
671
- arrowStyle?: Partial<ArrowProperties>;
672
- }): this;
673
- /**
674
- * **Advanced API:** Programmatically start drawing a sticky note at
675
- * specific coordinates - same interactive corner-drag as `startBox`.
676
- * You must handle mouse events yourself (or immediately release/complete
677
- * it via the same events `enableStickyNoteDrawing` would).
678
- *
679
- * **For most use cases, use `enableStickyNoteDrawing()` instead.**
680
- *
681
- * @param x X coordinate for the note's top-left corner
682
- * @param y Y coordinate for the note's top-left corner
683
- * @param style Sticky note style options
684
- * @returns this for chaining
685
- * @see enableStickyNoteDrawing for the recommended high-level API
686
- */
687
- startStickyNote(x: number, y: number, style?: Partial<Text_2["properties"]["style"]>): this;
688
- /**
689
- * **Advanced API:** Programmatically start drawing a box at specific coordinates.
690
- *
691
- * This is a low-level method that gives you full control over the drawing process.
692
- * You must handle mouse events and optionally create the box object yourself.
693
- *
694
- * **For most use cases, use `enableBoxDrawing()` instead** - it handles all
695
- * mouse events and annotation creation automatically.
696
- *
697
- * Use this method only when you need:
698
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
699
- * - Programmatic placement without user interaction
700
- * - Integration with custom UI frameworks
701
- *
702
- * @example
703
- * ```ts
704
- * // Custom cursor example
705
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
706
- * ogma.events.once('mousedown', (evt) => {
707
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
708
- * const box = createBox(x, y, 100, 50, { background: '#EDE6FF' });
709
- * control.startBox(x, y, box);
710
- * });
711
- * ```
712
- *
713
- * @param x X coordinate for the box origin
714
- * @param y Y coordinate for the box origin
715
- * @param box The box annotation to add (optional, will be created if not provided)
716
- * @returns this for chaining
717
- * @see enableBoxDrawing for the recommended high-level API
718
- */
719
- startBox(x: number, y: number, box?: Box): this;
720
- /**
721
- * **Advanced API:** Programmatically start drawing an arrow at specific coordinates.
722
- *
723
- * This is a low-level method that gives you full control over the drawing process.
724
- * You must handle mouse events and optionally create the arrow object yourself.
725
- *
726
- * **For most use cases, use `enableArrowDrawing()` instead** - it handles all
727
- * mouse events and annotation creation automatically.
728
- *
729
- * Use this method only when you need:
730
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
731
- * - Programmatic placement without user interaction
732
- * - Integration with custom UI frameworks
733
- *
734
- * @example
735
- * ```ts
736
- * // Custom cursor example
737
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
738
- * ogma.events.once('mousedown', (evt) => {
739
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
740
- * const arrow = createArrow(x, y, x, y, { strokeColor: '#3A03CF' });
741
- * control.startArrow(x, y, arrow);
742
- * });
743
- * ```
744
- *
745
- * @param x X coordinate for the arrow start
746
- * @param y Y coordinate for the arrow start
747
- * @param arrow The arrow annotation to add (optional, will be created if not provided)
748
- * @returns this for chaining
749
- * @see enableArrowDrawing for the recommended high-level API
750
- */
751
- startArrow(x: number, y: number, arrow?: Arrow): this;
752
- /**
753
- * **Advanced API:** Programmatically start drawing a text annotation at specific coordinates.
754
- *
755
- * This is a low-level method that gives you full control over the drawing process.
756
- * You must handle mouse events and optionally create the text object yourself.
757
- *
758
- * **For most use cases, use `enableTextDrawing()` instead** - it handles all
759
- * mouse events and annotation creation automatically.
760
- *
761
- * Use this method only when you need:
762
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
763
- * - Programmatic placement without user interaction
764
- * - Integration with custom UI frameworks
765
- *
766
- * @example
767
- * ```ts
768
- * // Custom cursor example
769
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
770
- * ogma.events.once('mousedown', (evt) => {
771
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
772
- * const text = createText(x, y, 0, 0, 'Hello', { color: '#3A03CF' });
773
- * control.startText(x, y, text);
774
- * });
775
- * ```
776
- *
777
- * @param x X coordinate for the text
778
- * @param y Y coordinate for the text
779
- * @param text The text annotation to add (optional, will be created if not provided)
780
- * @returns this for chaining
781
- * @see enableTextDrawing for the recommended high-level API
782
- */
783
- startText(x: number, y: number, text?: Text_2): this;
784
- /**
785
- * **Advanced API:** Programmatically start drawing a polygon at specific coordinates.
786
- *
787
- * This is a low-level method that gives you full control over the drawing process.
788
- * You must handle mouse events and create the polygon object yourself.
789
- *
790
- * **For most use cases, use `enablePolygonDrawing()` instead** - it handles all
791
- * mouse events and annotation creation automatically.
792
- *
793
- * Use this method only when you need:
794
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
795
- * - Programmatic placement without user interaction
796
- * - Integration with custom UI frameworks
797
- *
798
- * @example
799
- * ```ts
800
- * // Custom cursor example
801
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
802
- * ogma.events.once('mousedown', (evt) => {
803
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
804
- * const polygon = createPolygon([[[x, y]]], { strokeColor: '#3A03CF' });
805
- * control.startPolygon(x, y, polygon);
806
- * });
807
- * ```
808
- *
809
- * @param x X coordinate to start drawing
810
- * @param y Y coordinate to start drawing
811
- * @param polygon The polygon annotation to add
812
- * @returns this for chaining
813
- * @see enablePolygonDrawing for the recommended high-level API
814
- */
815
- startPolygon(x: number, y: number, polygon: Polygon): this;
816
- /**
817
- * Get the currently selected annotations as a collection
818
- * @returns A FeatureCollection of selected annotations
819
- */
820
- getSelectedAnnotations(): AnnotationCollection;
821
- /**
822
- * Get the first selected annotation (for backwards compatibility)
823
- * @returns The currently selected annotation, or null if none selected
824
- */
825
- getSelected(): Annotation | null;
826
- /**
827
- * Get a specific annotation by id
828
- * @param id The id of the annotation to retrieve
829
- * @returns The annotation with the given id, or undefined if not found
830
- */
831
- getAnnotation<T = Annotation>(id: Id): T | undefined;
832
- /**
833
- * Scale an annotation by a given factor around an origin point
834
- * @param id The id of the annotation to scale
835
- * @param scale The scale factor
836
- * @param ox Origin x coordinate
837
- * @param oy Origin y coordinate
838
- * @returns this for chaining
839
- */
840
- setScale(id: Id, scale: number, ox: number, oy: number): this;
841
- /**
842
- * Toggle a comment between collapsed and expanded mode
843
- * @param id The id of the comment to toggle
844
- * @returns this for chaining
845
- */
846
- toggleComment(id: Id): this;
847
- /**
848
- * Destroy the controller and its elements
849
- */
850
- destroy(): void;
851
- /**
852
- * Update the style of the annotation with the given id
853
- * @param id The id of the annotation to update
854
- * @param style The new style
855
- */
856
- updateStyle<A extends Annotation>(id: Id, style: A["properties"]["style"]): this;
857
- /**
858
- * Update an annotation with partial updates
859
- *
860
- * This method allows you to update any properties of an annotation, including
861
- * geometry, properties, and style. Updates are merged with existing data.
862
- *
863
- * @param annotation Partial annotation object with id and properties to update
864
- * @returns this for chaining
865
- *
866
- * @example
867
- * ```ts
868
- * // Update arrow geometry
869
- * controller.update({
870
- * id: arrowId,
871
- * geometry: {
872
- * type: 'LineString',
873
- * coordinates: [[0, 0], [200, 200]]
874
- * }
875
- * });
876
- *
877
- * // Update text content and position
878
- * controller.update({
879
- * id: textId,
880
- * geometry: {
881
- * type: 'Point',
882
- * coordinates: [100, 100]
883
- * },
884
- * properties: {
885
- * content: 'Updated text'
886
- * }
887
- * });
888
- *
889
- * // Update style only (prefer updateStyle for style-only updates)
890
- * controller.update({
891
- * id: boxId,
892
- * properties: {
893
- * style: {
894
- * background: '#ff0000'
895
- * }
896
- * }
897
- * });
898
- * ```
899
- */
900
- update<A extends Annotation>(annotation: DeepPartial<A> & {
901
- id: Id;
902
- }): this;
903
- /**
904
- * Attach an arrow to a node at the specified side
905
- * @param arrowId
906
- * @param targetNode
907
- * @param side
908
- */
909
- link(arrowId: Id, targetNode: Node_2, side: Side): this;
910
- /**
911
- * Attach an arrow to an annotation at the specified side
912
- * @param arrowId
913
- * @param target
914
- * @param side
915
- */
916
- link(arrowId: Id, target: Id, side: Side): this;
917
- isDrawing(): boolean;
918
- }
919
-
920
- /**
921
- * Options for the annotations control
922
- */
923
- declare type ControllerOptions = {
924
- /**
925
- * The radius in which arrows are attracted
926
- */
927
- magnetRadius: number;
928
- /**
929
- * The margin in which the Texts are detected when looking for magnet points
930
- */
931
- detectMargin: number;
932
- /**
933
- * Display size of the magnet point
934
- */
935
- magnetHandleRadius: number;
936
- /**
937
- * Placeholder for the text input
938
- */
939
- textPlaceholder: string;
940
- /**
941
- * Show send button in text editor
942
- */
943
- showSendButton: boolean;
944
- /**
945
- * Show edit button in text editor
946
- */
947
- showEditButton: boolean;
948
- /**
949
- * SVG icon for the send button in text editor
950
- * Should be a complete SVG string (e.g., '<svg>...</svg>')
951
- */
952
- sendButtonIcon: string;
953
- /**
954
- * SVG icon for the edit button in text editor
955
- * Should be a complete SVG string (e.g., '<svg>...</svg>')
956
- */
957
- editButtonIcon: string;
958
- /**
959
- * Minimum height of the arrow in units
960
- */
961
- minArrowHeight: number;
962
- /**
963
- * Maximum height of the arrow in units
964
- */
965
- maxArrowHeight: number;
966
- };
967
-
968
- /**
969
- * Registers `<rgba-color-picker>` if it isn't already, then returns a fresh
970
- * instance. Safe to call repeatedly and from multiple module copies.
971
- */
972
- export declare function createRgbaColorPicker(): RgbaColorPicker;
973
-
974
- declare type DeepPartial<T> = {
975
- [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
976
- };
977
-
978
- export declare const DEFAULT_PANEL_ORIENTATION: PanelOrientation;
979
-
980
- /** Preserves the panel's original vertically-centered, right-docked look. */
981
- export declare const DEFAULT_PANEL_PLACEMENT: PanelPlacement;
982
-
983
- /** Default palette used to seed the recent-colors strip. */
984
- export declare const DEFAULT_RECENT_COLORS: string[];
985
-
986
- /**
987
- * Which deletion control(s) the toolbar shows - `"erase"` (default) deletes
988
- * on click via the erase tool; `"select"` selects an annotation then deletes
989
- * it with the trash button; `"both"` shows both, since they serve slightly
990
- * different workflows.
991
- */
992
- export declare type DeleteMode = "select" | "erase" | "both";
993
-
994
- /** Event related to a single annotation feature */
995
- declare interface DragEvent_2 {
996
- /** Annotation ID involved in the event */
997
- id: Id;
998
- /** Current mouse position in pixel coordinates during the drag */
999
- position: {
1000
- x: number;
1001
- y: number;
1002
- };
1003
- }
1004
-
1005
- /** Arrow snapped at parametric position t (0–1) along an edge path. */
1006
- declare type EdgeMagnet = {
1007
- type: "edge";
1008
- t: number;
1009
- };
1010
-
1011
- declare const EVT_ADD = "add";
1012
-
1013
- declare const EVT_CANCEL_DRAWING = "cancelDrawing";
1014
-
1015
- declare const EVT_CLICK = "click";
1016
-
1017
- declare const EVT_COMPLETE_DRAWING = "completeDrawing";
1018
-
1019
- declare const EVT_DRAG_END = "dragend";
1020
-
1021
- declare const EVT_DRAG_START = "dragstart";
1022
-
1023
- declare const EVT_HISTORY = "history";
1024
-
1025
- declare const EVT_LINK = "link";
1026
-
1027
- declare const EVT_REMOVE = "remove";
1028
-
1029
- declare const EVT_SELECT = "select";
1030
-
1031
- declare const EVT_UNSELECT = "unselect";
1032
-
1033
- declare const EVT_UPDATE = "update";
1034
-
1035
- /**
1036
- * Serialized link stored inside arrow.properties.link.
1037
- * Uses plain { x, y } for backward compatibility with saved annotations.
1038
- * Converted to the internal Magnet type by Links.add().
1039
- */
1040
- declare type ExportedLink = {
1041
- id: Id;
1042
- side: Side;
1043
- type: TargetType;
1044
- magnet?: Point;
1045
- };
1046
-
1047
- /** Extremity types for arrow annotations. */
1048
- declare type Extremity = "none" | "arrow" | "arrow-plain" | "dot" | "halo-dot";
1049
-
1050
- export declare const EXTREMITY_OPTIONS: ExtremityOption[];
1051
-
1052
- export declare interface ExtremityOption {
1053
- value: string;
1054
- label: string;
1055
- icon: IconName;
1056
- }
1057
-
1058
- /** Event related to a single annotation feature */
1059
- declare interface FeatureEvent {
1060
- /** Annotation ID involved in the event */
1061
- id: Id;
1062
- }
1063
-
1064
- declare type FeatureEvents = {
1065
- /**
1066
- * Event trigerred when selecting an annotation
1067
- * @param evt The annotation selected
1068
- */
1069
- [EVT_SELECT]: (evt: FeaturesEvent) => void;
1070
- /**
1071
- * Event trigerred when unselecting an annotation
1072
- * @param evt The annotation unselected
1073
- */
1074
- [EVT_UNSELECT]: (evt: FeaturesEvent) => void;
1075
- /**
1076
- * Event trigerred when removing an annotation
1077
- * @param evt The annotation removed
1078
- */
1079
- [EVT_REMOVE]: (evt: FeatureEvent) => void;
1080
- /**
1081
- * Event trigerred when adding an annotation
1082
- * @param evt The annotation added
1083
- */
1084
- [EVT_ADD]: (evt: FeatureEvent) => void;
1085
- /**
1086
- * Event trigerred when canceling drawing mode
1087
- */
1088
- [EVT_CANCEL_DRAWING]: () => void;
1089
- /**
1090
- * Event trigerred when completing a drawing operation
1091
- * @param evt Contains the ID of the completed annotation
1092
- */
1093
- [EVT_COMPLETE_DRAWING]: (evt: FeatureEvent) => void;
1094
- /**
1095
- * Event trigerred when updating an annotation.
1096
- * This fires after any modification including drag operations, style changes, scaling, etc.
1097
- * @param evt The updated annotation with all changes applied
1098
- */
1099
- [EVT_UPDATE]: (evt: Annotation) => void;
1100
- /**
1101
- * Event trigerred when linking an arrow to a node or annotation
1102
- * @param evt Contains the arrow and link details
1103
- */
1104
- [EVT_LINK]: (evt: {
1105
- arrow: Arrow;
1106
- link: Link;
1107
- }) => void;
1108
- /**
1109
- * Event trigerred when history state changes (after undo/redo operations)
1110
- * @param evt Contains boolean flags for undo/redo availability
1111
- */
1112
- [EVT_HISTORY]: (evt: HistoryEvent) => void;
1113
- /**
1114
- * Event triggered when a drag operation starts on an annotation
1115
- */
1116
- [EVT_DRAG_START]: (evt: DragEvent_2) => void;
1117
- /**
1118
- * Event triggered when a drag operation ends on an annotation
1119
- */
1120
- [EVT_DRAG_END]: (evt: DragEvent_2) => void;
1121
- /**
1122
- * Event triggered when a click completes on an annotation (mouseup without drag)
1123
- */
1124
- [EVT_CLICK]: (evt: ClickEvent) => void;
1125
- };
1126
-
1127
- /** Event related to multiple annotation features */
1128
- declare interface FeaturesEvent {
1129
- /** Annotation IDs involved in the event */
1130
- ids: Id[];
1131
- }
1132
-
1133
- export declare interface FontOption {
1134
- value: string;
1135
- label: string;
1136
- /** Icon name from the shared icon set (see `ui/icons`). */
1137
- icon: IconName;
1138
- }
1139
-
1140
- export declare const FONTS: FontOption[];
1141
-
1142
- /**
1143
- * Hex color string in format #RGB or #RRGGBB
1144
- * @example "#fff" | "#ffffff" | "#F0A" | "#FF00AA"
1145
- */
1146
- declare type HexColor = `#${string}`;
1147
-
1148
- /** History stack change event */
1149
- declare interface HistoryEvent {
1150
- /** Indicates if undo operation is available */
1151
- canUndo: boolean;
1152
- /** Indicates if redo operation is available */
1153
- canRedo: boolean;
1154
- }
1155
-
1156
- /** Inner SVG markup for each icon (paths only; no <svg> wrapper). */
1157
- export declare const ICON_PATHS: Record<IconName, string>;
1158
-
1159
- /**
1160
- * Inline SVG icon set shared by the vanilla and React UI.
1161
- *
1162
- * Each entry is the *inner* markup of a 24x24 lucide icon (stroke-based,
1163
- * `currentColor`). The vanilla panel wraps these with `svgIcon()`; the React
1164
- * package wraps the same paths in a small `<Icon>` component. Keeping the path
1165
- * data here means consumers need no icon font and no `lucide-react` runtime
1166
- * dependency.
1167
- *
1168
- * Source: lucide.dev (ISC license).
1169
- */
1170
- export declare type IconName = "chevron-down" | "x" | "arrow-left" | "arrow-right" | "play" | "circle-dot" | "dot" | "circle" | "circle-dashed" | "type" | "italic" | "code" | "trash" | "undo" | "redo" | "pentagon" | "rectangle-horizontal" | "message-square" | "sticky-note" | "eraser" | "download" | "camera" | "rotate-cw" | "rotate-ccw" | "minimize";
1171
-
1172
- /** Unique identifier type for annotations */
1173
- declare type Id = string | number;
1174
-
1175
- export declare function initialRecentColors(): RecentColorsState;
1176
-
1177
- export declare const LINE_TYPES: LineTypeOption[];
1178
-
1179
- export declare interface LineTypeOption {
1180
- value: string;
1181
- icon: IconName;
1182
- }
1183
-
1184
- /** Link between an arrow and a text or node */
1185
- declare interface Link {
1186
- /** arrow attached to the text or node */
1187
- arrow: Id;
1188
- /** id of the text the arrow is attached to */
1189
- id: Id;
1190
- /** On which end the arrow is tighten to the text */
1191
- side: Side;
1192
- /** id of the text or node the arrow is attached to */
1193
- target: Id;
1194
- /** Text or node */
1195
- targetType: TargetType;
1196
- /** Typed snap point — semantics depend on targetType, see Magnet union. */
1197
- magnet: Magnet;
1198
- }
1199
-
1200
- declare type Magnet = NodeMagnet | EdgeMagnet | BoxMagnet | PolygonMagnet;
1201
-
1202
- export declare const MAX_RECENT_COLORS = 3;
1203
-
1204
- /** Arrow snapped to the center or perimeter of a node. */
1205
- declare type NodeMagnet = {
1206
- type: "node";
1207
- center: boolean;
1208
- };
1209
-
1210
- /** Whether panel sections stack top-to-bottom or run left-to-right. */
1211
- export declare type PanelOrientation = "vertical" | "horizontal";
1212
-
1213
- /**
1214
- * Placement and orientation options shared by the vanilla `AnnotationPanel`
1215
- * and the React panel/controller. Both are applied as `data-placement` /
1216
- * `data-orientation` attributes on the panel root, consumed by the rules in
1217
- * `ui/styles.css` — no inline layout styles are set from JS.
1218
- */
1219
- /** Which screen edge/corner the panel docks to. */
1220
- export declare type PanelPlacement = "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
1221
-
1222
- /**
1223
- * The slice of `Control` this state machine relies on. Declared structurally so
1224
- * the function does not pull the full `Control` type into the `/ui` entry's
1225
- * rolled declarations (which would otherwise create a duplicate, incompatible
1226
- * `Control` identity for consumers).
1227
- */
1228
- export declare interface PanelVisibilityControl {
1229
- on(event: string, handler: (...args: any[]) => void): unknown;
1230
- off(event: string, handler: (...args: any[]) => void): unknown;
1231
- once(event: string, handler: (...args: any[]) => void): unknown;
1232
- getAnnotation(id: string | number): Annotation | undefined;
1233
- isDrawing(): boolean;
1234
- }
1235
-
1236
- export declare interface PanelVisibilityHandlers {
1237
- /** Called when the panel should be shown for `annotation`. */
1238
- onShow: (annotation: Annotation) => void;
1239
- /** Called when the panel should be hidden. */
1240
- onHide: () => void;
1241
- }
1242
-
1243
- /** 2D coordinate */
1244
- declare type Point = {
1245
- x: number;
1246
- y: number;
1247
- };
1248
-
1249
- /**
1250
- * Polygon placed on the graph, use it to highlight areas
1251
- */
1252
- declare interface Polygon extends AnnotationFeature<Polygon_2, PolygonProperties> {
1253
- }
1254
-
1255
- /**
1256
- * Arrow snapped to a polygon annotation.
1257
- * rx/ry are 0–1 fractions of the polygon's bounding box from its top-left corner.
1258
- */
1259
- declare type PolygonMagnet = {
1260
- type: "polygon";
1261
- rx: number;
1262
- ry: number;
1263
- };
1264
-
1265
- declare interface PolygonProperties extends AnnotationProps {
1266
- type: "polygon";
1267
- style?: PolygonStyle;
1268
- }
1269
-
1270
- declare interface PolygonStyle extends BoxStyle {
1271
- }
1272
-
1273
- export declare interface RecentColorsState {
1274
- colors: string[];
1275
- /** Index of the currently active swatch. */
1276
- activeIndex: number;
1277
- }
1278
-
1279
- /** Channel-wise rgba, matching the shape emitted by `vanilla-colorful`. */
1280
- export declare interface RgbaChannels {
1281
- r: number;
1282
- g: number;
1283
- b: number;
1284
- a: number;
1285
- }
1286
-
1287
- /** Channel-wise rgba color, as read/written by the picker's `color` property. */
1288
- export declare interface RgbaColor {
1289
- r: number;
1290
- g: number;
1291
- b: number;
1292
- a: number;
1293
- }
1294
-
1295
- /**
1296
- * RGBA color string in format rgba(r, g, b, a)
1297
- * @example "rgba(255, 0, 0, 1)" | "rgba(128, 128, 128, 0.5)"
1298
- */
1299
- declare type RgbaColor_2 = `rgba(${number}, ${number}, ${number}, ${number})` | `rgba(${number},${number},${number},${number})`;
1300
-
1301
- /**
1302
- * The public surface of the `<rgba-color-picker>` element we rely on. Declared
1303
- * locally (rather than re-exporting `vanilla-colorful`'s class type) so that
1304
- * consumers don't have to resolve the package's deep `lib/entrypoints` types,
1305
- * which it does not expose via its `exports` map.
1306
- */
1307
- export declare interface RgbaColorPicker extends HTMLElement {
1308
- color: RgbaColor;
1309
- addEventListener(type: "color-changed", listener: (event: CustomEvent<{
1310
- value: RgbaColor;
1311
- }>) => void): void;
1312
- addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
1313
- }
1314
-
1315
- /** Serializes an rgba object (as emitted by the color picker) to a CSS string. */
1316
- export declare function rgbaToString(color: RgbaChannels): string;
1317
-
1318
- /**
1319
- * RGB color string in format rgb(r, g, b)
1320
- * @example "rgb(255, 0, 0)" | "rgb(128, 128, 128)"
1321
- */
1322
- declare type RgbColor = `rgb(${number}, ${number}, ${number})` | `rgb(${number},${number},${number})`;
1323
-
1324
- declare type Side = typeof SIDE_START | typeof SIDE_END;
1325
-
1326
- declare const SIDE_END: "end";
1327
-
1328
- declare const SIDE_START: "start";
1329
-
1330
- /** Stroke style options for annotations */
1331
- declare type StrokeOptions = {
1332
- /** Type of stroke: plain, dashed, or none */
1333
- strokeType?: StrokeType;
1334
- /** Stroke color: #f00, yellow... */
1335
- strokeColor?: Color;
1336
- /** Stroke width */
1337
- strokeWidth?: number;
1338
- };
1339
-
1340
- /** Stroke types available for annotations */
1341
- declare type StrokeType = "plain" | "dashed" | "none";
1342
-
1343
- /**
1344
- * Returns a complete `<svg>` string for the named icon, ready to drop into
1345
- * `innerHTML`. Used by the vanilla panel.
1346
- */
1347
- export declare function svgIcon(name: IconName, size?: number): string;
1348
-
1349
- /** @private */
1350
- declare const TARGET_TYPES: {
1351
- TEXT: "text";
1352
- NODE: "node";
1353
- BOX: "box";
1354
- COMMENT: "comment";
1355
- POLYGON: "polygon";
1356
- ANNOTATION: "annotation";
1357
- EDGE: "edge";
1358
- };
1359
-
1360
- declare type TargetType = (typeof TARGET_TYPES)[keyof typeof TARGET_TYPES];
1361
-
1362
- /**
1363
- * Text annotation feature, represents a text box at a specific position
1364
- */
1365
- declare interface Text_2 extends AnnotationFeature<Point_2, TextProperties> {
1366
- }
1367
-
1368
- declare interface TextProperties extends Omit<BoxProperties, "type"> {
1369
- type: "text";
1370
- /**text to display*/
1371
- content: string;
1372
- /** Width of the text box */
1373
- width: number;
1374
- /** Height of the text box */
1375
- height: number;
1376
- style?: TextStyle;
1377
- }
1378
-
1379
- declare interface TextStyle extends BoxStyle {
1380
- /** Helvetica, sans-serif... */
1381
- font?: string;
1382
- /** Font size, in pixels */
1383
- fontSize?: number | string;
1384
- /** text color: #f00, yellow...*/
1385
- color?: Color;
1386
- /** background color: empty for transparent #f00, yellow...*/
1387
- background?: Color;
1388
- /** padding around the text */
1389
- padding?: number;
1390
- /** Text box border radius */
1391
- borderRadius?: number;
1392
- /** When true, text maintains constant size regardless of zoom level */
1393
- fixedSize?: boolean;
1394
- /**
1395
- * Ghost text shown (via the textarea's native `placeholder` attribute)
1396
- * while `content` is empty - disappears the instant the user types, no
1397
- * selection/focus tricks needed. Overrides the global
1398
- * `ControllerOptions.textPlaceholder` for this annotation.
1399
- */
1400
- placeholder?: string;
1401
- }
1402
-
1403
- /** One of the toolbar's drawing tools - see `enabledTypes`. */
1404
- export declare type ToolbarDrawingType = "arrow" | "comment" | "sticky-note" | "box" | "text" | "polygon";
1405
-
1406
- /**
1407
- * Given the current recent-colors state and a color coming from the selected
1408
- * annotation, returns the next state: if the color is already known it becomes
1409
- * active, otherwise it is unshifted to the front (capped at MAX_RECENT_COLORS).
1410
- *
1411
- * Pure — callers (React/vanilla) hold the state and apply the result.
1412
- */
1413
- export declare function withColorFromAnnotation(state: RecentColorsState, color: string): RecentColorsState;
1414
-
1415
- export { }
1
+ import { Annotation, Control, Arrow, Box, Text, Polygon, CommentProps, ArrowProperties, Id } from './index.js';
2
+ import { Ogma } from '@linkurious/ogma';
3
+ import 'geojson';
4
+ import 'eventemitter3';
5
+
6
+ /**
7
+ * Placement and orientation options shared by the vanilla `AnnotationPanel`
8
+ * and the React panel/controller. Both are applied as `data-placement` /
9
+ * `data-orientation` attributes on the panel root, consumed by the rules in
10
+ * `ui/styles.css` — no inline layout styles are set from JS.
11
+ */
12
+ /** Which screen edge/corner the panel docks to. */
13
+ type PanelPlacement = "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
14
+ /** Whether panel sections stack top-to-bottom or run left-to-right. */
15
+ type PanelOrientation = "vertical" | "horizontal";
16
+ /** Preserves the panel's original vertically-centered, right-docked look. */
17
+ declare const DEFAULT_PANEL_PLACEMENT: PanelPlacement;
18
+ declare const DEFAULT_PANEL_ORIENTATION: PanelOrientation;
19
+
20
+ /**
21
+ * Which selected-annotation *kind* a panel/toolbar responds to - shared
22
+ * between the vanilla `AnnotationPanel` and the React
23
+ * `AnnotationPanelController`/`useAnnotationPanel` so both filter selection
24
+ * the same way (one classification function, not two copies).
25
+ *
26
+ * Distinct from `AnnotationToolbar`'s `ToolbarDrawingType`: that one is
27
+ * about which *drawing tool* to offer (and splits `"sticky-note"` out from
28
+ * `"text"` since they're different buttons to create), this one is about
29
+ * which *already-selected annotation's data type* to react to - a sticky
30
+ * note is still just `"text"` here, same as any other Text annotation,
31
+ * since the docked panel (and, for Text specifically, the floating
32
+ * `TextAnnotationToolbar`) don't have a separate UI for it.
33
+ */
34
+
35
+ type PanelAnnotationType = "arrow" | "text" | "box" | "comment" | "polygon";
36
+ declare const ALL_PANEL_ANNOTATION_TYPES: PanelAnnotationType[];
37
+ /** `null` for an annotation kind the panel has no UI for at all (none
38
+ * today, but new annotation types may be added before their panel support
39
+ * is). Callers should treat `null` the same as "not enabled". */
40
+ declare function classifyPanelAnnotationType(a: Annotation): PanelAnnotationType | null;
41
+
42
+ interface AnnotationPanelOptions {
43
+ control: Control;
44
+ /**
45
+ * Element the panel mounts into. The panel creates and manages its own root
46
+ * `<div class="annotation-panel">` inside it. Defaults to `document.body`.
47
+ */
48
+ container?: HTMLElement;
49
+ /**
50
+ * Which screen edge/corner the panel docks to. Defaults to `"right"`
51
+ * (vertically centered on the right edge — the original look). Change at
52
+ * runtime with {@link AnnotationPanel.setPlacement}.
53
+ */
54
+ placement?: PanelPlacement;
55
+ /**
56
+ * Whether panel sections stack vertically or run horizontally as a
57
+ * toolbar. Defaults to `"vertical"`. Change at runtime with
58
+ * {@link AnnotationPanel.setOrientation}.
59
+ */
60
+ orientation?: PanelOrientation;
61
+ /**
62
+ * Which selected-annotation types the panel responds to. Defaults to all
63
+ * five (`["arrow", "text", "box", "comment", "polygon"]`). Exclude
64
+ * `"text"` once you've adopted `TextAnnotationToolbar` for Text
65
+ * annotations and sticky notes, so the two don't show at once for the
66
+ * same selection.
67
+ */
68
+ enabledTypes?: PanelAnnotationType[];
69
+ /**
70
+ * Whether a selected-but-locked (`isEditable: false`) annotation keeps
71
+ * the panel hidden, same as no selection. Defaults to `true`. See
72
+ * `attachPanelVisibility`'s `hideWhenNotEditable` for the full contract.
73
+ */
74
+ hideWhenNotEditable?: boolean;
75
+ }
76
+ declare class AnnotationPanel {
77
+ private control;
78
+ private panel;
79
+ private panelBody;
80
+ private mode;
81
+ private currentAnnotation;
82
+ private currentColor;
83
+ private recent;
84
+ private colorCircles;
85
+ private colorPickerOverlay;
86
+ private colorPicker;
87
+ private detachVisibility;
88
+ private documentClickHandler;
89
+ private enabledTypes;
90
+ constructor(options: AnnotationPanelOptions);
91
+ private setAnnotation;
92
+ private renderArrow;
93
+ private renderText;
94
+ private renderPolygon;
95
+ private section;
96
+ private icon;
97
+ private colorSelector;
98
+ private backgroundSelector;
99
+ private fontSelector;
100
+ private extremitySelector;
101
+ private dropdown;
102
+ private slider;
103
+ private lineTypeButtons;
104
+ private bind;
105
+ private updateColorFromAnnotation;
106
+ private updateColorCircles;
107
+ private toggleColorPicker;
108
+ private closeColorPicker;
109
+ private updateStyle;
110
+ setPlacement(placement: PanelPlacement): void;
111
+ setOrientation(orientation: PanelOrientation): void;
112
+ show(): void;
113
+ hide: () => void;
114
+ destroy(): void;
115
+ }
116
+
117
+ /** One of the toolbar's drawing tools - see `enabledTypes`. */
118
+ type ToolbarDrawingType = "arrow" | "comment" | "sticky-note" | "box" | "text" | "polygon";
119
+ /**
120
+ * Which deletion control(s) the toolbar shows - `"erase"` (default) deletes
121
+ * on click via the erase tool; `"select"` selects an annotation then deletes
122
+ * it with the trash button; `"both"` shows both, since they serve slightly
123
+ * different workflows.
124
+ */
125
+ type DeleteMode = "select" | "erase" | "both";
126
+ /**
127
+ * Per-type default style overrides, merged over the toolbar's own built-in
128
+ * defaults (which stay in effect for anything you don't override). See
129
+ * `Control.enableArrowDrawing` etc. for what each shape configures.
130
+ */
131
+ interface AnnotationToolbarStyles {
132
+ arrow?: Partial<Arrow["properties"]["style"]>;
133
+ box?: Partial<Box["properties"]["style"]>;
134
+ text?: Partial<Text["properties"]["style"]>;
135
+ polygon?: Partial<Polygon["properties"]["style"]>;
136
+ comment?: {
137
+ offsetX?: number;
138
+ offsetY?: number;
139
+ commentStyle?: Partial<CommentProps>;
140
+ arrowStyle?: Partial<ArrowProperties>;
141
+ };
142
+ stickyNote?: Partial<Text["properties"]["style"]>;
143
+ }
144
+ interface AnnotationToolbarOptions {
145
+ control: Control;
146
+ /**
147
+ * Element the toolbar mounts into. The toolbar creates and manages its
148
+ * own root `<div class="annotation-toolbar">` inside it. Defaults to
149
+ * `document.body`.
150
+ */
151
+ container?: HTMLElement;
152
+ /**
153
+ * Which screen edge/corner the toolbar docks to. Defaults to `"bottom"`.
154
+ * Change at runtime with {@link AnnotationToolbar.setPlacement}.
155
+ */
156
+ placement?: PanelPlacement;
157
+ /**
158
+ * Whether buttons run left-to-right or stack top-to-bottom. Defaults to
159
+ * `"horizontal"`. Change at runtime with
160
+ * {@link AnnotationToolbar.setOrientation}.
161
+ */
162
+ orientation?: PanelOrientation;
163
+ /**
164
+ * Which drawing tools to show, and in what order. Defaults to all six:
165
+ * `["arrow", "comment", "sticky-note", "box", "text", "polygon"]`.
166
+ */
167
+ enabledTypes?: ToolbarDrawingType[];
168
+ /** Per-type default style overrides for the drawing tool buttons. */
169
+ styles?: AnnotationToolbarStyles;
170
+ /**
171
+ * Which deletion control(s) to show - the erase tool, the
172
+ * select-then-trash button, or both. Defaults to `"erase"`.
173
+ */
174
+ deleteMode?: DeleteMode;
175
+ /** Called when the SVG export button is clicked. Omit to hide the button. */
176
+ onSvgExport?: () => void;
177
+ /** Called when the JSON export button is clicked. Omit to hide the button. */
178
+ onJsonExport?: () => void;
179
+ }
180
+ /**
181
+ * Styled drawing/undo-redo toolbar: add arrow, comment, box, text and
182
+ * polygon annotations, undo/redo, and delete the current selection. The
183
+ * React equivalent is `AddMenu` (`@linkurious/ogma-annotations-react/ui`) -
184
+ * this class mirrors its buttons and defaults for a vanilla consumer.
185
+ */
186
+ declare class AnnotationToolbar {
187
+ private control;
188
+ private root;
189
+ private activeButton;
190
+ private undoButton;
191
+ private redoButton;
192
+ private handleDrawingEnd;
193
+ private handleHistory;
194
+ constructor(options: AnnotationToolbarOptions);
195
+ private drawingButton;
196
+ private button;
197
+ private separator;
198
+ private setActiveMode;
199
+ private updateUndoRedo;
200
+ setPlacement(placement: PanelPlacement): void;
201
+ setOrientation(orientation: PanelOrientation): void;
202
+ destroy(): void;
203
+ }
204
+
205
+ /**
206
+ * What a cell needs from its host toolbar - small and structural (not the
207
+ * full `Control`) so cells stay easy to unit test and don't reach past the
208
+ * annotation they're editing. Mirrors the shape of the React
209
+ * `*Controller.tsx` components' props, minus the framework.
210
+ */
211
+ interface ToolbarCellContext {
212
+ /** The annotation this toolbar instance is currently showing. Read fresh
213
+ * on every call (not cached by the cell) so it reflects the latest style
214
+ * after another cell's edit. */
215
+ getAnnotation(): Text;
216
+ /** Merges `patch` into the annotation's style via `control.updateStyle`. */
217
+ updateStyle(patch: Partial<Text["properties"]["style"]>): void;
218
+ /** Removes the annotation via `control.remove`. */
219
+ deleteAnnotation(): void;
220
+ }
221
+ /**
222
+ * One button/control in the floating toolbar's pill. `element` is inserted
223
+ * directly into the pill's DOM by `AnnotationStyleToolbar`; a divider is
224
+ * inserted after every cell but the last.
225
+ */
226
+ interface ToolbarCell {
227
+ readonly element: HTMLElement;
228
+ /** Called whenever the shown annotation's data may have changed (initial
229
+ * render, another cell's edit, or an external update) so the cell can
230
+ * reflect the current value (e.g. the size cell's displayed number). */
231
+ update(annotation: Text): void;
232
+ destroy(): void;
233
+ }
234
+
235
+ /**
236
+ * Inline SVG icon set shared by the vanilla and React UI.
237
+ *
238
+ * Each entry is the *inner* markup of a 24x24 lucide icon (stroke-based,
239
+ * `currentColor`). The vanilla panel wraps these with `svgIcon()`; the React
240
+ * package wraps the same paths in a small `<Icon>` component. Keeping the path
241
+ * data here means consumers need no icon font and no `lucide-react` runtime
242
+ * dependency.
243
+ *
244
+ * Source: lucide.dev (ISC license).
245
+ */
246
+ type IconName = "chevron-down" | "x" | "arrow-left" | "arrow-right" | "play" | "circle-dot" | "dot" | "circle" | "circle-dashed" | "type" | "italic" | "bold" | "code" | "trash" | "undo" | "redo" | "pentagon" | "rectangle-horizontal" | "message-square" | "sticky-note" | "eraser" | "download" | "camera" | "rotate-cw" | "rotate-ccw" | "minimize" | "user";
247
+ /** Inner SVG markup for each icon (paths only; no <svg> wrapper). */
248
+ declare const ICON_PATHS: Record<IconName, string>;
249
+ /**
250
+ * Returns a complete `<svg>` string for the named icon, ready to drop into
251
+ * `innerHTML`. Used by the vanilla panel.
252
+ */
253
+ declare function svgIcon(name: IconName, size?: number): string;
254
+
255
+ /**
256
+ * Declarative description of one toolbar entry - what `TextStyleToolbar`/
257
+ * `StickyNoteStyleToolbar`'s `getItems()` returns. A generic renderer turns
258
+ * each item into a `ToolbarCell`:
259
+ *
260
+ * - `"button"` -> `ButtonItemCell` (Bold, Delete, the author toggle - any
261
+ * simple action/toggle button).
262
+ * - `"dropdown"` -> `DropdownItemCell` (Font family, Font size - anything
263
+ * that's "pick one of a list").
264
+ * - `"separator"` -> a plain divider, no cell.
265
+ * - `"custom"` -> an escape hatch for a cell that doesn't reduce to
266
+ * button/dropdown - the color picker is the one case today (a swatch grid
267
+ * opening a secondary popover, not a flat option list).
268
+ *
269
+ * This is what makes the built-in toolbar configurable from the outside:
270
+ * `getItems()` builds this list from `TextStyleToolbarOptions` (fonts, font
271
+ * sizes, swatches - each with a shipped default, all overridable), rather
272
+ * than hardcoding specific cell classes/values.
273
+ */
274
+ type ToolbarItem = ToolbarButtonItem | ToolbarDropdownItem | ToolbarSeparatorItem | ToolbarCustomItem;
275
+ interface ToolbarButtonItem {
276
+ kind: "button";
277
+ /** Stable identifier for a built-in item (e.g. `"bold"`, `"delete"`) -
278
+ * lets a `TextStyleToolbarOptions.items` transform address one by
279
+ * `id` (`filter`/`find`) instead of by array position, so it keeps
280
+ * working if the built-in list's order or length changes in a later
281
+ * version. Not read by the toolbar itself; unset on your own items,
282
+ * which only your own `items` function ever sees. */
283
+ id?: string;
284
+ /** Tooltip label, shown on hover via the existing `data-tooltip` CSS. */
285
+ title: string;
286
+ icon: IconName;
287
+ action: (ctx: ToolbarCellContext) => void;
288
+ /** Toggle state - button gets the `.active` class when this returns
289
+ * true. Omit for a plain action button (e.g. Delete). */
290
+ isActive?: (annotation: Text) => boolean;
291
+ /** Red-on-hover styling, used by Delete. */
292
+ danger?: boolean;
293
+ }
294
+ /** `value` is `string | number` (not a per-item generic) so a plain object
295
+ * literal - e.g. an inline font-size preset list - type-checks directly
296
+ * against `ToolbarItem` without an explicit type argument. */
297
+ type ToolbarDropdownValue = string | number;
298
+ interface ToolbarDropdownOption {
299
+ value: ToolbarDropdownValue;
300
+ label: string;
301
+ /** Inline style hint applied to this option's row - e.g. `{fontFamily:
302
+ * value}` so a font option previews in its own face. */
303
+ style?: Partial<CSSStyleDeclaration>;
304
+ }
305
+ interface ToolbarDropdownItem {
306
+ kind: "dropdown";
307
+ /** See `ToolbarButtonItem.id`. */
308
+ id?: string;
309
+ title: string;
310
+ options: ToolbarDropdownOption[];
311
+ getValue: (annotation: Text) => ToolbarDropdownValue;
312
+ onSelect: (value: ToolbarDropdownValue, ctx: ToolbarCellContext) => void;
313
+ /** Trigger label shown when closed - defaults to the selected option's
314
+ * `label`. Override for e.g. font (always shows "Aa", not the font name)
315
+ * or size (shows the live effective number even between presets). */
316
+ getLabel?: (value: ToolbarDropdownValue, annotation: Text) => string;
317
+ }
318
+ interface ToolbarSeparatorItem {
319
+ kind: "separator";
320
+ /** See `ToolbarButtonItem.id` - lets an `items` transform address a
321
+ * specific divider (e.g. "the one right before Delete") without
322
+ * counting positions. Rarely needed; addressing the item next to it is
323
+ * usually enough. */
324
+ id?: string;
325
+ }
326
+ interface ToolbarCustomItem {
327
+ kind: "custom";
328
+ /** See `ToolbarButtonItem.id`. */
329
+ id?: string;
330
+ build: (ctx: ToolbarCellContext) => ToolbarCell;
331
+ }
332
+
333
+ interface AnnotationStyleToolbarOptions {
334
+ control: Control;
335
+ }
336
+ /**
337
+ * Base class for a floating, per-selection style pill: mounts an
338
+ * `ogma.layers.addOverlay(...)` layer holding a row of cells built from
339
+ * `getItems()`'s declarative `ToolbarItem[]`, and keeps it anchored above
340
+ * the annotation's (possibly rotated) top edge.
341
+ *
342
+ * Generic over its options type (`TOptions extends
343
+ * AnnotationStyleToolbarOptions`) so a subclass can add its own
344
+ * configuration - fonts/sizes/swatches for `TextStyleToolbar`, say - and
345
+ * read it back via `this.options` from within `getItems()`, which the base
346
+ * constructor calls before any subclass field initializer would run. Pass
347
+ * that configuration through the options object itself (as
348
+ * `TextStyleToolbar` does), not extra constructor parameters.
349
+ *
350
+ * Written directly against `Text` rather than also being generic over the
351
+ * annotation type - `TextStyleToolbar`/`StickyNoteStyleToolbar` are the
352
+ * only subclasses today. A future non-Text style toolbar (arrows/boxes/
353
+ * polygons - explicitly undecided, see the toolbar's design notes) would
354
+ * most likely copy this class's shape (overlay mount + item-driven cell
355
+ * row + anchor tracking) rather than force a second type parameter through
356
+ * a base no second subclass has yet exercised.
357
+ *
358
+ * One instance is tied to one annotation for its whole lifetime - the host
359
+ * `TextAnnotationToolbar` destroys and recreates the toolbar when the
360
+ * selection changes to a different annotation (or between plain-Text and
361
+ * sticky-note), and calls `update()` on the same instance for everything
362
+ * else (an edit from one of its own cells, or an external update).
363
+ */
364
+ declare abstract class AnnotationStyleToolbar<TOptions extends AnnotationStyleToolbarOptions = AnnotationStyleToolbarOptions> {
365
+ protected control: Control;
366
+ protected ogma: Ogma;
367
+ protected options: TOptions;
368
+ readonly annotationId: Id;
369
+ private overlay;
370
+ /** The element handed to `addOverlay` - Ogma writes its own
371
+ * `style.transform` (translate/rotate/scale, recomputed on every
372
+ * viewChanged/zoom/rotate/move) directly onto this node to place it at
373
+ * `position`, so nothing of ours may also set `transform` here or Ogma's
374
+ * assignment clobbers it (inline style, last writer wins - there's no way
375
+ * to compose two separate `transform` writers on one element). Zero-size
376
+ * and otherwise unstyled; it exists purely as the anchor point. */
377
+ private anchor;
378
+ /** The actual visible pill - a child of `anchor`, `position: absolute`
379
+ * with its own `transform: translate(-50%, -100%)` (see `styles.css`) to
380
+ * center horizontally and sit fully above the anchor point. This is where
381
+ * our own positioning lives, kept off `anchor` for exactly that reason. */
382
+ private root;
383
+ private cells;
384
+ private documentClickHandler;
385
+ constructor(options: TOptions, annotation: Text);
386
+ /** The toolbar's entries, in order - implemented by
387
+ * `TextStyleToolbar`/`StickyNoteStyleToolbar`. Declarative
388
+ * (`ToolbarItem[]`) rather than pre-built cells, so a consumer can
389
+ * reconfigure the built-in items (fonts, sizes, swatches) via
390
+ * `this.options` without subclassing any cell. */
391
+ protected abstract getItems(ctx: ToolbarCellContext): ToolbarItem[];
392
+ /** Turns one `ToolbarItem` into its `ToolbarCell` - `"separator"` has
393
+ * none (handled by `setItems` directly), `"custom"` calls its own
394
+ * `build()` (the color picker's escape hatch), `"button"`/`"dropdown"`
395
+ * go through the two generic cell classes. */
396
+ private buildCell;
397
+ private setItems;
398
+ /**
399
+ * Top-center of the annotation's box, in graph space, offset outward by
400
+ * `ANCHOR_GAP` (screen-constant, so divided by zoom) and rotated by the
401
+ * global annotation-rotation angle - the same `ctx.rotate(rotation)`
402
+ * transform `Handles.renderOutline` applies to a Text box's outline, so
403
+ * the pill tracks the box exactly as it visually rotates.
404
+ */
405
+ private getAnchor;
406
+ /** Refreshes every cell and repositions the pill for `annotation` -
407
+ * called after a select, an own-cell edit, or an external update to the
408
+ * same annotation. Does not rebuild cells; the host recreates the whole
409
+ * instance when the annotation identity/kind changes. */
410
+ update(annotation: Text): void;
411
+ destroy(): void;
412
+ }
413
+
414
+ /**
415
+ * Fixed swatch-grid palette for the floating text toolbar's color cell,
416
+ * extracted from the Figma "Sticky Note Toolbar" color-picker-dropdown
417
+ * export. Distinct from `ui/config.ts`'s `BACKGROUNDS`/`DEFAULT_RECENT_COLORS`
418
+ * (the docked `AnnotationPanel`'s recent-colors strip) - this is a static
419
+ * palette, not an MRU list.
420
+ *
421
+ * The export only fully captured 8 fill/stroke pairs for what reads as a
422
+ * 3x3(+) grid - the 9th cell (a color, or a "more colors" affordance) was
423
+ * unconfirmed, so a `"transparent"` swatch (a checkerboard circle in the CSS
424
+ * - see `.oa-toolbar-swatch-cell-transparent` in `styles.css`, same "no
425
+ * fill" convention `ui/config.ts`'s `BACKGROUNDS` already uses for the
426
+ * docked `AnnotationPanel`) fills that slot. `ColorCell` opens the existing
427
+ * `vanilla-colorful` picker (see `colorPicker.ts`) as a secondary "more
428
+ * colors" popover for anything not in this fixed set.
429
+ */
430
+ interface Swatch {
431
+ /** Fill color, used as both the swatch circle's fill and the annotation's
432
+ * `color`/`background` style value when picked. The CSS keyword
433
+ * `"transparent"` is a valid value here - `ColorCell` gives it a
434
+ * checkerboard swatch instead of a solid (indistinguishable-from-empty)
435
+ * circle. */
436
+ fill: string;
437
+ /** 1px ring stroke color around the swatch circle. */
438
+ stroke: string;
439
+ }
440
+ declare const STICKY_SWATCHES: Swatch[];
441
+
442
+ interface ColorCellOptions {
443
+ /** Fixed swatch-grid palette - defaults to `STICKY_SWATCHES`
444
+ * (`TextStyleToolbar`'s `swatches` option), overridable per instance. */
445
+ swatches: Swatch[];
446
+ /**
447
+ * Called instead of opening the built-in `vanilla-colorful` popover when
448
+ * "More colors…" is clicked - use this to hand off to your own color
449
+ * picker (a native `<input type="color">`, a design-system component,
450
+ * whatever) instead of the bundled one. Read the current color via
451
+ * `ctx.getAnnotation().properties.style?.background`, and call
452
+ * `ctx.updateStyle({ background: yourPickedColor })` (as many times as
453
+ * you like, e.g. live while the user drags in your own picker) to apply
454
+ * it - same `ToolbarCellContext` every other cell action gets.
455
+ * `anchor` is the "More colors…" button itself, there to position your
456
+ * own popover against if you want to anchor it the same place the
457
+ * built-in one would have opened. This toolbar's own popover closes
458
+ * right before this is called either way.
459
+ */
460
+ onMoreColors?: (ctx: ToolbarCellContext, anchor: HTMLElement) => void;
461
+ }
462
+ /**
463
+ * Note-color cell - the toolbar's one hand-built ("custom") item, per its
464
+ * own edge-case shape (a swatch grid opening a secondary popover, not a
465
+ * flat option list a `DropdownItemCell` could render). Edits `background`
466
+ * (the note's fill - the single most visible "color" of a Text/sticky
467
+ * note, and what the Figma swatch grid's colors are for), not `color` (the
468
+ * text color, already covered by the docked `AnnotationPanel`'s separate
469
+ * Color/Background sections).
470
+ *
471
+ * Primary UI is the `swatches` grid, matching the Figma dropdown export. A
472
+ * "More colors…" cell at the end opens the existing `vanilla-colorful`
473
+ * picker (`colorPicker.ts`) as a secondary popover, so this doesn't
474
+ * reimplement a full color picker - it wraps the one `AnnotationPanel`
475
+ * already uses - unless the host supplies `onMoreColors`, in which case
476
+ * that's called instead and the built-in picker never gets built at all.
477
+ */
478
+ declare class ColorCell implements ToolbarCell {
479
+ private ctx;
480
+ private options;
481
+ readonly element: HTMLElement;
482
+ private dropdown;
483
+ private swatch;
484
+ private more;
485
+ private morePicker;
486
+ private morePickerHost;
487
+ constructor(ctx: ToolbarCellContext, options: ColorCellOptions);
488
+ private pick;
489
+ private openMorePicker;
490
+ private closeMorePicker;
491
+ update(annotation: Text): void;
492
+ destroy(): void;
493
+ }
494
+
495
+ /** Font options offered by the Font-family cell. Real font names need the
496
+ * actual webfont loaded by the host page to render as shown (e.g. the demo
497
+ * at `web/index.html` loads IBM Plex Sans/Mono) - same "best effort, no
498
+ * bundled font loading" convention `defaultStickyNoteStyle`/
499
+ * `defaultCommentStyle` already use for `font: "IBM Plex Sans"`. */
500
+ declare const DEFAULT_TOOLBAR_FONTS: ToolbarDropdownOption[];
501
+ /** Preset sizes offered by the Font-size cell - same range as the docked
502
+ * `AnnotationPanel`'s font-size slider (8-72). */
503
+ declare const DEFAULT_TOOLBAR_FONT_SIZES: number[];
504
+ interface TextStyleToolbarOptions extends AnnotationStyleToolbarOptions {
505
+ /** Font-family cell options - defaults to `DEFAULT_TOOLBAR_FONTS`. */
506
+ fonts?: ToolbarDropdownOption[];
507
+ /** Font-size cell presets - defaults to `DEFAULT_TOOLBAR_FONT_SIZES`. */
508
+ fontSizes?: number[];
509
+ /** Color cell's swatch-grid palette - defaults to `STICKY_SWATCHES`. */
510
+ swatches?: Swatch[];
511
+ /** Bring your own color picker: called instead of opening the built-in
512
+ * `vanilla-colorful` popover when the color cell's "More colors…" is
513
+ * clicked. See `ColorCellOptions.onMoreColors` for the full contract. */
514
+ onMoreColors?: ColorCellOptions["onMoreColors"];
515
+ /**
516
+ * Full control over the pill's contents, beyond `fonts`/`fontSizes`/
517
+ * `swatches`/`onMoreColors`: called with the built-in item list (color,
518
+ * font, font size, bold, show-author, delete - each with a stable `id`,
519
+ * see `ToolbarButtonItem.id`) and returns what actually renders. Add,
520
+ * remove, reorder or replace freely -
521
+ * `defaultItems.filter((i) => i.id !== "delete")` drops Delete,
522
+ * `[{ kind: "custom", build: () => new MyCell(ctx) }, ...defaultItems]`
523
+ * prepends a cell - same power as subclassing and overriding
524
+ * `getItems()`, as a plain option instead. Defaults to the identity
525
+ * function (`defaultItems` unchanged).
526
+ */
527
+ items?: (defaultItems: ToolbarItem[], ctx: ToolbarCellContext) => ToolbarItem[];
528
+ }
529
+ /** Floating style pill for a Text annotation (plain text box or sticky
530
+ * note): Color, Font family, Font size, Bold, Show author, Delete - see the
531
+ * Figma "Sticky Note Toolbar" export (alignment cell dropped for v1).
532
+ * `StickyNoteStyleToolbar` extends this with no items of its own for now.
533
+ *
534
+ * The built-in items are declarative `ToolbarItem`s (see `cells/types.ts`),
535
+ * built from `this.options` - override `fonts`/`fontSizes`/`swatches`/
536
+ * `onMoreColors` at construction to reconfigure them without subclassing. */
537
+ declare class TextStyleToolbar extends AnnotationStyleToolbar<TextStyleToolbarOptions> {
538
+ protected getItems(ctx: ToolbarCellContext): ToolbarItem[];
539
+ }
540
+
541
+ /** `fonts`/`fontSizes`/`swatches` (see `TextStyleToolbarOptions`) apply to
542
+ * both the plain-Text and sticky-note pill - `StickyNoteStyleToolbar`
543
+ * currently has no items or options of its own, it's a plain subclass. */
544
+ interface TextAnnotationToolbarOptions extends TextStyleToolbarOptions {
545
+ control: Control;
546
+ /**
547
+ * Whether a selected-but-locked (`isEditable: false`) Text annotation
548
+ * keeps the toolbar hidden, same as no selection. Defaults to `true`.
549
+ * Set to `false` to keep the toolbar open on a locked selection instead
550
+ * (e.g. to keep a lock/unlock button in `items` reachable) - see
551
+ * `attachPanelVisibility`'s `hideWhenNotEditable` for the full contract.
552
+ */
553
+ hideWhenNotEditable?: boolean;
554
+ }
555
+ /**
556
+ * Floating, per-selection style toolbar for Text annotations (plain text
557
+ * boxes and sticky notes) - the vanilla equivalent of the docked
558
+ * `AnnotationPanel`, but anchored above the selection instead of docked to
559
+ * a screen edge. Ignores non-Text selections (arrow/box/polygon/comment
560
+ * still only get the docked panel, for now).
561
+ *
562
+ * Thin by design (`constructor(options)` / `destroy()`, no other public
563
+ * surface) so a future React wrapper can host it the way
564
+ * `AnnotationPanelController` hosts `AnnotationPanel`.
565
+ */
566
+ declare class TextAnnotationToolbar {
567
+ private control;
568
+ private options;
569
+ private current;
570
+ private detachVisibility;
571
+ private handleUpdate;
572
+ constructor(options: TextAnnotationToolbarOptions);
573
+ private show;
574
+ private hide;
575
+ destroy(): void;
576
+ }
577
+
578
+ /** Sticky-note style pill. Currently just `TextStyleToolbar`'s items
579
+ * verbatim - the author-visibility toggle that used to be added here has
580
+ * moved to the base class so plain Text annotations get it too. Kept as a
581
+ * distinct (empty) subclass, rather than folded away, so `FloatingTextToolbar`
582
+ * can keep selecting it via `isStickyNote()` without change, and so any
583
+ * future sticky-note-only cell has an obvious place to go. */
584
+ declare class StickyNoteStyleToolbar extends TextStyleToolbar {
585
+ }
586
+
587
+ /** Renders a `ToolbarButtonItem` - the generic cell behind Bold, Delete,
588
+ * and the author-visibility toggle (any simple action/toggle button). */
589
+ declare class ButtonItemCell implements ToolbarCell {
590
+ private ctx;
591
+ private item;
592
+ readonly element: HTMLButtonElement;
593
+ constructor(ctx: ToolbarCellContext, item: ToolbarButtonItem);
594
+ private onClick;
595
+ update(annotation: Text): void;
596
+ destroy(): void;
597
+ }
598
+
599
+ /** Renders a `ToolbarDropdownItem` - the generic cell behind Font family
600
+ * and Font size (anything that's "pick one of a list"). */
601
+ declare class DropdownItemCell implements ToolbarCell {
602
+ private ctx;
603
+ private item;
604
+ readonly element: HTMLElement;
605
+ private dropdown;
606
+ constructor(ctx: ToolbarCellContext, item: ToolbarDropdownItem);
607
+ update(annotation: Text): void;
608
+ destroy(): void;
609
+ }
610
+
611
+ /**
612
+ * Shared, framework-agnostic configuration for the annotation style panel UI.
613
+ * Consumed by both the vanilla `AnnotationPanel` (core) and the React controllers.
614
+ */
615
+
616
+ interface BackgroundOption {
617
+ value: string;
618
+ /** Inline style for the swatch's color circle. */
619
+ style: string;
620
+ }
621
+ declare const BACKGROUNDS: BackgroundOption[];
622
+ interface FontOption {
623
+ value: string;
624
+ label: string;
625
+ /** Icon name from the shared icon set (see `ui/icons`). */
626
+ icon: IconName;
627
+ }
628
+ declare const FONTS: FontOption[];
629
+ interface ExtremityOption {
630
+ value: string;
631
+ label: string;
632
+ icon: IconName;
633
+ }
634
+ declare const EXTREMITY_OPTIONS: ExtremityOption[];
635
+ interface LineTypeOption {
636
+ value: string;
637
+ icon: IconName;
638
+ }
639
+ declare const LINE_TYPES: LineTypeOption[];
640
+ /** Default palette used to seed the recent-colors strip. */
641
+ declare const DEFAULT_RECENT_COLORS: string[];
642
+
643
+ /** Channel-wise rgba, matching the shape emitted by `vanilla-colorful`. */
644
+ interface RgbaChannels {
645
+ r: number;
646
+ g: number;
647
+ b: number;
648
+ a: number;
649
+ }
650
+ /** Serializes an rgba object (as emitted by the color picker) to a CSS string. */
651
+ declare function rgbaToString(color: RgbaChannels): string;
652
+ interface RecentColorsState {
653
+ colors: string[];
654
+ /** Index of the currently active swatch. */
655
+ activeIndex: number;
656
+ }
657
+ declare const MAX_RECENT_COLORS = 3;
658
+ declare function initialRecentColors(): RecentColorsState;
659
+ /**
660
+ * Given the current recent-colors state and a color coming from the selected
661
+ * annotation, returns the next state: if the color is already known it becomes
662
+ * active, otherwise it is unshifted to the front (capped at MAX_RECENT_COLORS).
663
+ *
664
+ * Pure — callers (React/vanilla) hold the state and apply the result.
665
+ */
666
+ declare function withColorFromAnnotation(state: RecentColorsState, color: string): RecentColorsState;
667
+
668
+ /** Channel-wise rgba color, as read/written by the picker's `color` property. */
669
+ interface RgbaColor {
670
+ r: number;
671
+ g: number;
672
+ b: number;
673
+ a: number;
674
+ }
675
+ /**
676
+ * The public surface of the `<rgba-color-picker>` element we rely on. Declared
677
+ * locally (rather than re-exporting `vanilla-colorful`'s class type) so that
678
+ * consumers don't have to resolve the package's deep `lib/entrypoints` types,
679
+ * which it does not expose via its `exports` map.
680
+ */
681
+ interface RgbaColorPicker extends HTMLElement {
682
+ color: RgbaColor;
683
+ addEventListener(type: "color-changed", listener: (event: CustomEvent<{
684
+ value: RgbaColor;
685
+ }>) => void): void;
686
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
687
+ }
688
+ /**
689
+ * Registers `<rgba-color-picker>` if it isn't already, then returns a fresh
690
+ * instance. Safe to call repeatedly and from multiple module copies.
691
+ */
692
+ declare function createRgbaColorPicker(): RgbaColorPicker;
693
+
694
+ /**
695
+ * Framework-agnostic visibility state machine for the annotation style panel.
696
+ *
697
+ * The panel should appear when a single annotation is selected — but only once
698
+ * we're sure the interaction is a click/selection and not the start of a drag
699
+ * or an in-progress drawing. This logic was previously duplicated verbatim in
700
+ * the vanilla `AnnotationPanel` constructor and the React
701
+ * `AnnotationPanelController`; it now lives here and is consumed by both.
702
+ */
703
+
704
+ interface PanelVisibilityHandlers {
705
+ /** Called when the panel should be shown for `annotation`. */
706
+ onShow: (annotation: Annotation) => void;
707
+ /** Called when the panel should be hidden. */
708
+ onHide: () => void;
709
+ /**
710
+ * Whether a selected-but-non-editable annotation (per the `isEditable`
711
+ * control option) keeps the panel hidden, same as if nothing were
712
+ * selected. Defaults to `true` - the original behavior, right for a panel
713
+ * with nothing safe to change on a locked annotation. Set to `false` for
714
+ * a panel that stays open on a locked selection instead (e.g. to keep a
715
+ * lock/unlock toggle reachable) - callers typically also grey out every
716
+ * other control in that case, since `onShow` still fires for an
717
+ * annotation nothing else in the panel can safely edit.
718
+ */
719
+ hideWhenNotEditable?: boolean;
720
+ }
721
+ /**
722
+ * The slice of `Control` this state machine relies on. Declared structurally so
723
+ * the function does not pull the full `Control` type into the `/ui` entry's
724
+ * rolled declarations (which would otherwise create a duplicate, incompatible
725
+ * `Control` identity for consumers).
726
+ */
727
+ interface PanelVisibilityControl {
728
+ on(event: string, handler: (...args: any[]) => void): unknown;
729
+ off(event: string, handler: (...args: any[]) => void): unknown;
730
+ once(event: string, handler: (...args: any[]) => void): unknown;
731
+ getAnnotation(id: string | number): Annotation | undefined;
732
+ isDrawing(): boolean;
733
+ isAnnotationEditable(id: string | number): boolean;
734
+ }
735
+ /**
736
+ * Wires `control` events to show/hide callbacks. Returns a `detach` function
737
+ * that removes every listener it registered.
738
+ */
739
+ declare function attachPanelVisibility(control: PanelVisibilityControl, { onShow, onHide, hideWhenNotEditable }: PanelVisibilityHandlers): () => void;
740
+
741
+ export { ALL_PANEL_ANNOTATION_TYPES, AnnotationPanel, AnnotationStyleToolbar, AnnotationToolbar, BACKGROUNDS, ButtonItemCell, ColorCell, DEFAULT_PANEL_ORIENTATION, DEFAULT_PANEL_PLACEMENT, DEFAULT_RECENT_COLORS, DEFAULT_TOOLBAR_FONTS, DEFAULT_TOOLBAR_FONT_SIZES, DropdownItemCell, EXTREMITY_OPTIONS, FONTS, ICON_PATHS, LINE_TYPES, MAX_RECENT_COLORS, STICKY_SWATCHES, StickyNoteStyleToolbar, TextAnnotationToolbar, TextStyleToolbar, attachPanelVisibility, classifyPanelAnnotationType, createRgbaColorPicker, initialRecentColors, rgbaToString, svgIcon, withColorFromAnnotation };
742
+ export type { AnnotationPanelOptions, AnnotationStyleToolbarOptions, AnnotationToolbarOptions, AnnotationToolbarStyles, BackgroundOption, ColorCellOptions, DeleteMode, ExtremityOption, FontOption, IconName, LineTypeOption, PanelAnnotationType, PanelOrientation, PanelPlacement, PanelVisibilityControl, PanelVisibilityHandlers, RecentColorsState, RgbaChannels, RgbaColor, RgbaColorPicker, Swatch, TextAnnotationToolbarOptions, TextStyleToolbarOptions, ToolbarButtonItem, ToolbarCell, ToolbarCellContext, ToolbarCustomItem, ToolbarDrawingType, ToolbarDropdownItem, ToolbarDropdownOption, ToolbarItem, ToolbarSeparatorItem };