@linkurious/ogma-annotations 2.1.2 → 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,1423 +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
- * Connector-line behavior when the attachment point moves (default: "rigid").
351
- * - "rigid": the comment translates by the same offset as the moved
352
- * attachment point — the arrow keeps its length/angle, whole callout moves.
353
- * - "elastic": the comment stays put; the arrow re-anchors to the nearest
354
- * point on the comment box, so the line can stretch/rotate.
355
- */
356
- connectorMode?: "rigid" | "elastic";
357
- }
358
-
359
- /**
360
- * Main controller class for managing annotations.
361
- * It manages rendering and editing of annotations.
362
- */
363
- declare class Control extends default_2<FeatureEvents> {
364
- private ogma;
365
- private store;
366
- private renderers;
367
- private interactions;
368
- private editor;
369
- private links;
370
- private index;
371
- private drawing;
372
- private snapping;
373
- private selectionManager;
374
- private historyManager;
375
- private updateManager;
376
- private commentManager;
377
- constructor(ogma: Ogma, options?: Partial<ControllerOptions>);
378
- private initializeRenderers;
379
- private setupEvents;
380
- private onRotate;
381
- private onZoom;
382
- private onLayout;
383
- /**
384
- * Set the options for the controller
385
- * @param options new Options
386
- * @returns the updated options
387
- */
388
- setOptions(options?: Partial<ControllerOptions>): {
389
- showSendButton: boolean;
390
- showEditButton: boolean;
391
- sendButtonIcon: string;
392
- editButtonIcon: string;
393
- minArrowHeight: number;
394
- maxArrowHeight: number;
395
- detectMargin: number;
396
- magnetRadius: number;
397
- magnetHandleRadius: number;
398
- textPlaceholder: string;
399
- };
400
- /**
401
- * Add an annotation to the controller
402
- * @param annotation The annotation to add
403
- */
404
- add(annotation: Annotation | AnnotationCollection): this;
405
- /**
406
- * Remove an annotation or an array of annotations from the controller
407
- * @param annotation The annotation(s) to remove
408
- */
409
- remove(annotation: Annotation | AnnotationCollection): this;
410
- /**
411
- * Undo the last change
412
- * @returns true if undo was successful, false if no changes to undo
413
- */
414
- undo(): boolean;
415
- /**
416
- * Redo the last undone change
417
- * @returns true if redo was successful, false if no changes to redo
418
- */
419
- redo(): boolean;
420
- /**
421
- * Check if there are changes to undo
422
- * @returns true if undo is possible
423
- */
424
- canUndo(): boolean;
425
- /**
426
- * Check if there are changes to redo
427
- * @returns true if redo is possible
428
- */
429
- canRedo(): boolean;
430
- /**
431
- * Clear the undo/redo history
432
- */
433
- clearHistory(): void;
434
- /**
435
- * Get all annotations in the controller
436
- * @returns A FeatureCollection containing all annotations
437
- */
438
- getAnnotations(): AnnotationCollection;
439
- /**
440
- * Select one or more annotations by id
441
- * @param annotations The id(s) of the annotation(s) to select
442
- * @returns this for chaining
443
- */
444
- select(annotations: Id | Id[]): this;
445
- /**
446
- * Unselect one or more annotations, or all if no ids provided
447
- * @param annotations The id(s) of the annotation(s) to unselect, or undefined to unselect all
448
- * @returns this for chaining
449
- */
450
- unselect(annotations?: Id | Id[]): this;
451
- /**
452
- * Cancel the current drawing operation
453
- * @returns this for chaining
454
- */
455
- cancelDrawing(): this;
456
- /**
457
- * Enable arrow drawing mode - the recommended way to add arrows.
458
- *
459
- * Call this method when the user clicks an "Add Arrow" button. The control will:
460
- * 1. Wait for the next mousedown event
461
- * 2. Create an arrow at that position with the specified style
462
- * 3. Start the interactive drawing process
463
- * 4. Clean up automatically when done
464
- *
465
- * **This is the recommended API for 99% of use cases.** Only use `startArrow()`
466
- * if you need to implement custom mouse handling or positioning logic.
467
- *
468
- * @example
469
- * ```ts
470
- * addArrowButton.addEventListener('click', () => {
471
- * control.enableArrowDrawing({ strokeColor: '#3A03CF', strokeWidth: 2 });
472
- * });
473
- * ```
474
- *
475
- * @param style Arrow style options
476
- * @returns this for chaining
477
- * @see startArrow for low-level programmatic control
478
- */
479
- enableArrowDrawing(style?: Partial<Arrow["properties"]["style"]>): this;
480
- /**
481
- * Enable text drawing mode - the recommended way to add text annotations.
482
- *
483
- * Call this method when the user clicks an "Add Text" button. The control will:
484
- * 1. Wait for the next mousedown event
485
- * 2. Create a text box at that position with the specified style
486
- * 3. Start the interactive drawing/editing process
487
- * 4. Clean up automatically when done
488
- *
489
- * **This is the recommended API for 99% of use cases.** Only use `startText()`
490
- * if you need to implement custom mouse handling or positioning logic.
491
- *
492
- * @example
493
- * ```ts
494
- * addTextButton.addEventListener('click', () => {
495
- * control.enableTextDrawing({ color: '#3A03CF', fontSize: 24 });
496
- * });
497
- * ```
498
- *
499
- * @param style Text style options
500
- * @returns this for chaining
501
- * @see startText for low-level programmatic control
502
- */
503
- enableTextDrawing(style?: Partial<Text_2["properties"]["style"]>): this;
504
- /**
505
- * Enable box drawing mode - the recommended way to add boxes.
506
- *
507
- * Call this method when the user clicks an "Add Box" button. The control will:
508
- * 1. Wait for the next mousedown event
509
- * 2. Create a box at that position with the specified style
510
- * 3. Start the interactive drawing process (drag to size)
511
- * 4. Clean up automatically when done
512
- *
513
- * **This is the recommended API for 99% of use cases.** Only use `startBox()`
514
- * if you need to implement custom mouse handling or positioning logic.
515
- *
516
- * @example
517
- * ```ts
518
- * addBoxButton.addEventListener('click', () => {
519
- * control.enableBoxDrawing({ background: '#EDE6FF', borderRadius: 8 });
520
- * });
521
- * ```
522
- *
523
- * @param style Box style options
524
- * @returns this for chaining
525
- * @see startBox for low-level programmatic control
526
- */
527
- enableBoxDrawing(style?: Partial<Box["properties"]["style"]>): this;
528
- /**
529
- * Enable polygon drawing mode - the recommended way to add polygons.
530
- *
531
- * Call this method when the user clicks an "Add Polygon" button. The control will:
532
- * 1. Wait for the next mousedown event
533
- * 2. Create a polygon starting at that position with the specified style
534
- * 3. Start the interactive drawing process (click points to draw shape)
535
- * 4. Clean up automatically when done
536
- *
537
- * **This is the recommended API for 99% of use cases.** Only use `startPolygon()`
538
- * if you need to implement custom mouse handling or positioning logic.
539
- *
540
- * @example
541
- * ```ts
542
- * addPolygonButton.addEventListener('click', () => {
543
- * control.enablePolygonDrawing({ strokeColor: '#3A03CF', background: 'rgba(58, 3, 207, 0.15)' });
544
- * });
545
- * ```
546
- *
547
- * @param style Polygon style options
548
- * @returns this for chaining
549
- * @see startPolygon for low-level programmatic control
550
- */
551
- enablePolygonDrawing(style?: Partial<Polygon["properties"]["style"]>): this;
552
- /**
553
- * Enable comment drawing mode - the recommended way to add comments.
554
- *
555
- * Call this method when the user clicks an "Add Comment" button. The control will:
556
- * 1. Wait for the next mousedown event
557
- * 2. Create a comment with an arrow pointing to that position
558
- * 3. Smart positioning: automatically finds the best placement for the comment box
559
- * 4. Start the interactive editing process
560
- * 5. Clean up automatically when done
561
- *
562
- * **This is the recommended API for 99% of use cases.** Only use `startComment()`
563
- * if you need to implement custom mouse handling or positioning logic.
564
- *
565
- * @example
566
- * ```ts
567
- * addCommentButton.addEventListener('click', () => {
568
- * control.enableCommentDrawing({
569
- * commentStyle: { color: '#3A03CF', background: '#EDE6FF' },
570
- * arrowStyle: { strokeColor: '#3A03CF', head: 'halo-dot' }
571
- * });
572
- * });
573
- * ```
574
- *
575
- * @param options Drawing options including offsets and styles
576
- * @param options.offsetX Manual X offset for comment placement (overrides smart positioning)
577
- * @param options.offsetY Manual Y offset for comment placement (overrides smart positioning)
578
- * @param options.commentStyle Style options for the comment box
579
- * @param options.arrowStyle Style options for the arrow
580
- * @returns this for chaining
581
- * @see startComment for low-level programmatic control
582
- */
583
- enableCommentDrawing(options?: {
584
- offsetX?: number;
585
- offsetY?: number;
586
- commentStyle?: Partial<CommentProps>;
587
- arrowStyle?: Partial<ArrowProperties>;
588
- }): this;
589
- /**
590
- * Enable sticky note drawing mode - drops a plain, resizable text box
591
- * (empty content, "Quick note…" ghost placeholder, no connector arrow)
592
- * like a Miro sticky note, unlike `enableCommentDrawing`. It's a regular
593
- * `text` annotation, so it's placed the same interactive way as
594
- * `enableBoxDrawing`/`enableTextDrawing`: click for a default-size square,
595
- * or drag to size it - either way it keeps the usual corner/edge drag
596
- * handles to resize it afterward.
597
- *
598
- * Call this method when the user clicks an "Add sticky note" button. The
599
- * control will:
600
- * 1. Wait for the next mousedown event
601
- * 2. Create the note at that position and start the interactive
602
- * corner-drag, already selected
603
- * 3. On release: a plain click (no drag) gets a default square size, a
604
- * drag gets sized to match instead - either way it drops straight
605
- * into editing (the placeholder is just ghost text, so typing
606
- * immediately replaces it)
607
- * 4. Clean up automatically when done
608
- *
609
- * @example
610
- * ```ts
611
- * addStickyNoteButton.addEventListener('click', () => {
612
- * control.enableStickyNoteDrawing({ background: '#FFEB99' });
613
- * });
614
- * ```
615
- *
616
- * @param style Sticky note style options (merged over the sticky note defaults)
617
- * @returns this for chaining
618
- * @see startStickyNote for low-level programmatic control
619
- */
620
- enableStickyNoteDrawing(style?: Partial<Text_2["properties"]["style"]>): this;
621
- /**
622
- * Enable erase mode: every click on an annotation deletes it immediately.
623
- * Stays armed across multiple clicks until `disableEraseMode()` is called,
624
- * or another drawing tool is enabled / `cancelDrawing()` is called.
625
- *
626
- * @returns this for chaining
627
- * @see disableEraseMode to turn erase mode off
628
- */
629
- enableEraseMode(): this;
630
- /** Turn erase mode off. No-op if it isn't active. */
631
- disableEraseMode(): this;
632
- /** Whether erase mode is currently active. */
633
- isEraseModeActive(): boolean;
634
- /**
635
- * Place a pre-created annotation by moving it with the cursor.
636
- * The annotation follows the mouse until the user clicks to place it.
637
- * Press Escape to cancel.
638
- *
639
- * @param annotation The text or box annotation to place
640
- * @returns this for chaining
641
- */
642
- enablePlacement(annotation: Text_2 | Box): this;
643
- /**
644
- * **Advanced API:** Programmatically start drawing a comment at specific coordinates.
645
- *
646
- * This is a low-level method that gives you full control over the drawing process.
647
- * You must handle mouse events and create the comment object yourself.
648
- *
649
- * **For most use cases, use `enableCommentDrawing()` instead** - it handles all
650
- * mouse events and annotation creation automatically.
651
- *
652
- * Use this method only when you need:
653
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
654
- * - Programmatic placement without user interaction
655
- * - Integration with custom UI frameworks
656
- *
657
- * @example
658
- * ```ts
659
- * // Custom cursor example
660
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
661
- * ogma.events.once('mousedown', (evt) => {
662
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
663
- * const comment = createComment(x, y, 'My comment', { color: '#3A03CF' });
664
- * control.startComment(x, y, comment);
665
- * });
666
- * ```
667
- *
668
- * @param x X coordinate to start drawing
669
- * @param y Y coordinate to start drawing
670
- * @param comment The comment annotation to add
671
- * @param options Drawing options including offsets and styles
672
- * @returns this for chaining
673
- * @see enableCommentDrawing for the recommended high-level API
674
- */
675
- startComment(x: number, y: number, comment: Comment_2, options?: {
676
- offsetX?: number;
677
- offsetY?: number;
678
- commentStyle?: Partial<CommentProps>;
679
- arrowStyle?: Partial<ArrowProperties>;
680
- }): this;
681
- /**
682
- * **Advanced API:** Programmatically start drawing a sticky note at
683
- * specific coordinates - same interactive corner-drag as `startBox`.
684
- * You must handle mouse events yourself (or immediately release/complete
685
- * it via the same events `enableStickyNoteDrawing` would).
686
- *
687
- * **For most use cases, use `enableStickyNoteDrawing()` instead.**
688
- *
689
- * @param x X coordinate for the note's top-left corner
690
- * @param y Y coordinate for the note's top-left corner
691
- * @param style Sticky note style options
692
- * @returns this for chaining
693
- * @see enableStickyNoteDrawing for the recommended high-level API
694
- */
695
- startStickyNote(x: number, y: number, style?: Partial<Text_2["properties"]["style"]>): this;
696
- /**
697
- * **Advanced API:** Programmatically start drawing a box at specific coordinates.
698
- *
699
- * This is a low-level method that gives you full control over the drawing process.
700
- * You must handle mouse events and optionally create the box object yourself.
701
- *
702
- * **For most use cases, use `enableBoxDrawing()` instead** - it handles all
703
- * mouse events and annotation creation automatically.
704
- *
705
- * Use this method only when you need:
706
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
707
- * - Programmatic placement without user interaction
708
- * - Integration with custom UI frameworks
709
- *
710
- * @example
711
- * ```ts
712
- * // Custom cursor example
713
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
714
- * ogma.events.once('mousedown', (evt) => {
715
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
716
- * const box = createBox(x, y, 100, 50, { background: '#EDE6FF' });
717
- * control.startBox(x, y, box);
718
- * });
719
- * ```
720
- *
721
- * @param x X coordinate for the box origin
722
- * @param y Y coordinate for the box origin
723
- * @param box The box annotation to add (optional, will be created if not provided)
724
- * @returns this for chaining
725
- * @see enableBoxDrawing for the recommended high-level API
726
- */
727
- startBox(x: number, y: number, box?: Box): this;
728
- /**
729
- * **Advanced API:** Programmatically start drawing an arrow at specific coordinates.
730
- *
731
- * This is a low-level method that gives you full control over the drawing process.
732
- * You must handle mouse events and optionally create the arrow object yourself.
733
- *
734
- * **For most use cases, use `enableArrowDrawing()` instead** - it handles all
735
- * mouse events and annotation creation automatically.
736
- *
737
- * Use this method only when you need:
738
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
739
- * - Programmatic placement without user interaction
740
- * - Integration with custom UI frameworks
741
- *
742
- * @example
743
- * ```ts
744
- * // Custom cursor example
745
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
746
- * ogma.events.once('mousedown', (evt) => {
747
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
748
- * const arrow = createArrow(x, y, x, y, { strokeColor: '#3A03CF' });
749
- * control.startArrow(x, y, arrow);
750
- * });
751
- * ```
752
- *
753
- * @param x X coordinate for the arrow start
754
- * @param y Y coordinate for the arrow start
755
- * @param arrow The arrow annotation to add (optional, will be created if not provided)
756
- * @returns this for chaining
757
- * @see enableArrowDrawing for the recommended high-level API
758
- */
759
- startArrow(x: number, y: number, arrow?: Arrow): this;
760
- /**
761
- * **Advanced API:** Programmatically start drawing a text annotation at specific coordinates.
762
- *
763
- * This is a low-level method that gives you full control over the drawing process.
764
- * You must handle mouse events and optionally create the text object yourself.
765
- *
766
- * **For most use cases, use `enableTextDrawing()` instead** - it handles all
767
- * mouse events and annotation creation automatically.
768
- *
769
- * Use this method only when you need:
770
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
771
- * - Programmatic placement without user interaction
772
- * - Integration with custom UI frameworks
773
- *
774
- * @example
775
- * ```ts
776
- * // Custom cursor example
777
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
778
- * ogma.events.once('mousedown', (evt) => {
779
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
780
- * const text = createText(x, y, 0, 0, 'Hello', { color: '#3A03CF' });
781
- * control.startText(x, y, text);
782
- * });
783
- * ```
784
- *
785
- * @param x X coordinate for the text
786
- * @param y Y coordinate for the text
787
- * @param text The text annotation to add (optional, will be created if not provided)
788
- * @returns this for chaining
789
- * @see enableTextDrawing for the recommended high-level API
790
- */
791
- startText(x: number, y: number, text?: Text_2): this;
792
- /**
793
- * **Advanced API:** Programmatically start drawing a polygon at specific coordinates.
794
- *
795
- * This is a low-level method that gives you full control over the drawing process.
796
- * You must handle mouse events and create the polygon object yourself.
797
- *
798
- * **For most use cases, use `enablePolygonDrawing()` instead** - it handles all
799
- * mouse events and annotation creation automatically.
800
- *
801
- * Use this method only when you need:
802
- * - Custom mouse event handling (e.g., custom cursors, right-click menus)
803
- * - Programmatic placement without user interaction
804
- * - Integration with custom UI frameworks
805
- *
806
- * @example
807
- * ```ts
808
- * // Custom cursor example
809
- * ogma.setOptions({ cursor: { default: 'crosshair' } });
810
- * ogma.events.once('mousedown', (evt) => {
811
- * const { x, y } = ogma.view.screenToGraphCoordinates(evt);
812
- * const polygon = createPolygon([[[x, y]]], { strokeColor: '#3A03CF' });
813
- * control.startPolygon(x, y, polygon);
814
- * });
815
- * ```
816
- *
817
- * @param x X coordinate to start drawing
818
- * @param y Y coordinate to start drawing
819
- * @param polygon The polygon annotation to add
820
- * @returns this for chaining
821
- * @see enablePolygonDrawing for the recommended high-level API
822
- */
823
- startPolygon(x: number, y: number, polygon: Polygon): this;
824
- /**
825
- * Get the currently selected annotations as a collection
826
- * @returns A FeatureCollection of selected annotations
827
- */
828
- getSelectedAnnotations(): AnnotationCollection;
829
- /**
830
- * Get the first selected annotation (for backwards compatibility)
831
- * @returns The currently selected annotation, or null if none selected
832
- */
833
- getSelected(): Annotation | null;
834
- /**
835
- * Get a specific annotation by id
836
- * @param id The id of the annotation to retrieve
837
- * @returns The annotation with the given id, or undefined if not found
838
- */
839
- getAnnotation<T = Annotation>(id: Id): T | undefined;
840
- /**
841
- * Scale an annotation by a given factor around an origin point
842
- * @param id The id of the annotation to scale
843
- * @param scale The scale factor
844
- * @param ox Origin x coordinate
845
- * @param oy Origin y coordinate
846
- * @returns this for chaining
847
- */
848
- setScale(id: Id, scale: number, ox: number, oy: number): this;
849
- /**
850
- * Toggle a comment between collapsed and expanded mode
851
- * @param id The id of the comment to toggle
852
- * @returns this for chaining
853
- */
854
- toggleComment(id: Id): this;
855
- /**
856
- * Destroy the controller and its elements
857
- */
858
- destroy(): void;
859
- /**
860
- * Update the style of the annotation with the given id
861
- * @param id The id of the annotation to update
862
- * @param style The new style
863
- */
864
- updateStyle<A extends Annotation>(id: Id, style: A["properties"]["style"]): this;
865
- /**
866
- * Update an annotation with partial updates
867
- *
868
- * This method allows you to update any properties of an annotation, including
869
- * geometry, properties, and style. Updates are merged with existing data.
870
- *
871
- * @param annotation Partial annotation object with id and properties to update
872
- * @returns this for chaining
873
- *
874
- * @example
875
- * ```ts
876
- * // Update arrow geometry
877
- * controller.update({
878
- * id: arrowId,
879
- * geometry: {
880
- * type: 'LineString',
881
- * coordinates: [[0, 0], [200, 200]]
882
- * }
883
- * });
884
- *
885
- * // Update text content and position
886
- * controller.update({
887
- * id: textId,
888
- * geometry: {
889
- * type: 'Point',
890
- * coordinates: [100, 100]
891
- * },
892
- * properties: {
893
- * content: 'Updated text'
894
- * }
895
- * });
896
- *
897
- * // Update style only (prefer updateStyle for style-only updates)
898
- * controller.update({
899
- * id: boxId,
900
- * properties: {
901
- * style: {
902
- * background: '#ff0000'
903
- * }
904
- * }
905
- * });
906
- * ```
907
- */
908
- update<A extends Annotation>(annotation: DeepPartial<A> & {
909
- id: Id;
910
- }): this;
911
- /**
912
- * Attach an arrow to a node at the specified side
913
- * @param arrowId
914
- * @param targetNode
915
- * @param side
916
- */
917
- link(arrowId: Id, targetNode: Node_2, side: Side): this;
918
- /**
919
- * Attach an arrow to an annotation at the specified side
920
- * @param arrowId
921
- * @param target
922
- * @param side
923
- */
924
- link(arrowId: Id, target: Id, side: Side): this;
925
- isDrawing(): boolean;
926
- }
927
-
928
- /**
929
- * Options for the annotations control
930
- */
931
- declare type ControllerOptions = {
932
- /**
933
- * The radius in which arrows are attracted
934
- */
935
- magnetRadius: number;
936
- /**
937
- * The margin in which the Texts are detected when looking for magnet points
938
- */
939
- detectMargin: number;
940
- /**
941
- * Display size of the magnet point
942
- */
943
- magnetHandleRadius: number;
944
- /**
945
- * Placeholder for the text input
946
- */
947
- textPlaceholder: string;
948
- /**
949
- * Show send button in text editor
950
- */
951
- showSendButton: boolean;
952
- /**
953
- * Show edit button in text editor
954
- */
955
- showEditButton: boolean;
956
- /**
957
- * SVG icon for the send button in text editor
958
- * Should be a complete SVG string (e.g., '<svg>...</svg>')
959
- */
960
- sendButtonIcon: string;
961
- /**
962
- * SVG icon for the edit button in text editor
963
- * Should be a complete SVG string (e.g., '<svg>...</svg>')
964
- */
965
- editButtonIcon: string;
966
- /**
967
- * Minimum height of the arrow in units
968
- */
969
- minArrowHeight: number;
970
- /**
971
- * Maximum height of the arrow in units
972
- */
973
- maxArrowHeight: number;
974
- };
975
-
976
- /**
977
- * Registers `<rgba-color-picker>` if it isn't already, then returns a fresh
978
- * instance. Safe to call repeatedly and from multiple module copies.
979
- */
980
- export declare function createRgbaColorPicker(): RgbaColorPicker;
981
-
982
- declare type DeepPartial<T> = {
983
- [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
984
- };
985
-
986
- export declare const DEFAULT_PANEL_ORIENTATION: PanelOrientation;
987
-
988
- /** Preserves the panel's original vertically-centered, right-docked look. */
989
- export declare const DEFAULT_PANEL_PLACEMENT: PanelPlacement;
990
-
991
- /** Default palette used to seed the recent-colors strip. */
992
- export declare const DEFAULT_RECENT_COLORS: string[];
993
-
994
- /**
995
- * Which deletion control(s) the toolbar shows - `"erase"` (default) deletes
996
- * on click via the erase tool; `"select"` selects an annotation then deletes
997
- * it with the trash button; `"both"` shows both, since they serve slightly
998
- * different workflows.
999
- */
1000
- export declare type DeleteMode = "select" | "erase" | "both";
1001
-
1002
- /** Event related to a single annotation feature */
1003
- declare interface DragEvent_2 {
1004
- /** Annotation ID involved in the event */
1005
- id: Id;
1006
- /** Current mouse position in pixel coordinates during the drag */
1007
- position: {
1008
- x: number;
1009
- y: number;
1010
- };
1011
- }
1012
-
1013
- /** Arrow snapped at parametric position t (0–1) along an edge path. */
1014
- declare type EdgeMagnet = {
1015
- type: "edge";
1016
- t: number;
1017
- };
1018
-
1019
- declare const EVT_ADD = "add";
1020
-
1021
- declare const EVT_CANCEL_DRAWING = "cancelDrawing";
1022
-
1023
- declare const EVT_CLICK = "click";
1024
-
1025
- declare const EVT_COMPLETE_DRAWING = "completeDrawing";
1026
-
1027
- declare const EVT_DRAG_END = "dragend";
1028
-
1029
- declare const EVT_DRAG_START = "dragstart";
1030
-
1031
- declare const EVT_HISTORY = "history";
1032
-
1033
- declare const EVT_LINK = "link";
1034
-
1035
- declare const EVT_REMOVE = "remove";
1036
-
1037
- declare const EVT_SELECT = "select";
1038
-
1039
- declare const EVT_UNSELECT = "unselect";
1040
-
1041
- declare const EVT_UPDATE = "update";
1042
-
1043
- /**
1044
- * Serialized link stored inside arrow.properties.link.
1045
- * Uses plain { x, y } for backward compatibility with saved annotations.
1046
- * Converted to the internal Magnet type by Links.add().
1047
- */
1048
- declare type ExportedLink = {
1049
- id: Id;
1050
- side: Side;
1051
- type: TargetType;
1052
- magnet?: Point;
1053
- };
1054
-
1055
- /** Extremity types for arrow annotations. */
1056
- declare type Extremity = "none" | "arrow" | "arrow-plain" | "dot" | "halo-dot";
1057
-
1058
- export declare const EXTREMITY_OPTIONS: ExtremityOption[];
1059
-
1060
- export declare interface ExtremityOption {
1061
- value: string;
1062
- label: string;
1063
- icon: IconName;
1064
- }
1065
-
1066
- /** Event related to a single annotation feature */
1067
- declare interface FeatureEvent {
1068
- /** Annotation ID involved in the event */
1069
- id: Id;
1070
- }
1071
-
1072
- declare type FeatureEvents = {
1073
- /**
1074
- * Event trigerred when selecting an annotation
1075
- * @param evt The annotation selected
1076
- */
1077
- [EVT_SELECT]: (evt: FeaturesEvent) => void;
1078
- /**
1079
- * Event trigerred when unselecting an annotation
1080
- * @param evt The annotation unselected
1081
- */
1082
- [EVT_UNSELECT]: (evt: FeaturesEvent) => void;
1083
- /**
1084
- * Event trigerred when removing an annotation
1085
- * @param evt The annotation removed
1086
- */
1087
- [EVT_REMOVE]: (evt: FeatureEvent) => void;
1088
- /**
1089
- * Event trigerred when adding an annotation
1090
- * @param evt The annotation added
1091
- */
1092
- [EVT_ADD]: (evt: FeatureEvent) => void;
1093
- /**
1094
- * Event trigerred when canceling drawing mode
1095
- */
1096
- [EVT_CANCEL_DRAWING]: () => void;
1097
- /**
1098
- * Event trigerred when completing a drawing operation
1099
- * @param evt Contains the ID of the completed annotation
1100
- */
1101
- [EVT_COMPLETE_DRAWING]: (evt: FeatureEvent) => void;
1102
- /**
1103
- * Event trigerred when updating an annotation.
1104
- * This fires after any modification including drag operations, style changes, scaling, etc.
1105
- * @param evt The updated annotation with all changes applied
1106
- */
1107
- [EVT_UPDATE]: (evt: Annotation) => void;
1108
- /**
1109
- * Event trigerred when linking an arrow to a node or annotation
1110
- * @param evt Contains the arrow and link details
1111
- */
1112
- [EVT_LINK]: (evt: {
1113
- arrow: Arrow;
1114
- link: Link;
1115
- }) => void;
1116
- /**
1117
- * Event trigerred when history state changes (after undo/redo operations)
1118
- * @param evt Contains boolean flags for undo/redo availability
1119
- */
1120
- [EVT_HISTORY]: (evt: HistoryEvent) => void;
1121
- /**
1122
- * Event triggered when a drag operation starts on an annotation
1123
- */
1124
- [EVT_DRAG_START]: (evt: DragEvent_2) => void;
1125
- /**
1126
- * Event triggered when a drag operation ends on an annotation
1127
- */
1128
- [EVT_DRAG_END]: (evt: DragEvent_2) => void;
1129
- /**
1130
- * Event triggered when a click completes on an annotation (mouseup without drag)
1131
- */
1132
- [EVT_CLICK]: (evt: ClickEvent) => void;
1133
- };
1134
-
1135
- /** Event related to multiple annotation features */
1136
- declare interface FeaturesEvent {
1137
- /** Annotation IDs involved in the event */
1138
- ids: Id[];
1139
- }
1140
-
1141
- export declare interface FontOption {
1142
- value: string;
1143
- label: string;
1144
- /** Icon name from the shared icon set (see `ui/icons`). */
1145
- icon: IconName;
1146
- }
1147
-
1148
- export declare const FONTS: FontOption[];
1149
-
1150
- /**
1151
- * Hex color string in format #RGB or #RRGGBB
1152
- * @example "#fff" | "#ffffff" | "#F0A" | "#FF00AA"
1153
- */
1154
- declare type HexColor = `#${string}`;
1155
-
1156
- /** History stack change event */
1157
- declare interface HistoryEvent {
1158
- /** Indicates if undo operation is available */
1159
- canUndo: boolean;
1160
- /** Indicates if redo operation is available */
1161
- canRedo: boolean;
1162
- }
1163
-
1164
- /** Inner SVG markup for each icon (paths only; no <svg> wrapper). */
1165
- export declare const ICON_PATHS: Record<IconName, string>;
1166
-
1167
- /**
1168
- * Inline SVG icon set shared by the vanilla and React UI.
1169
- *
1170
- * Each entry is the *inner* markup of a 24x24 lucide icon (stroke-based,
1171
- * `currentColor`). The vanilla panel wraps these with `svgIcon()`; the React
1172
- * package wraps the same paths in a small `<Icon>` component. Keeping the path
1173
- * data here means consumers need no icon font and no `lucide-react` runtime
1174
- * dependency.
1175
- *
1176
- * Source: lucide.dev (ISC license).
1177
- */
1178
- 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";
1179
-
1180
- /** Unique identifier type for annotations */
1181
- declare type Id = string | number;
1182
-
1183
- export declare function initialRecentColors(): RecentColorsState;
1184
-
1185
- export declare const LINE_TYPES: LineTypeOption[];
1186
-
1187
- export declare interface LineTypeOption {
1188
- value: string;
1189
- icon: IconName;
1190
- }
1191
-
1192
- /** Link between an arrow and a text or node */
1193
- declare interface Link {
1194
- /** arrow attached to the text or node */
1195
- arrow: Id;
1196
- /** id of the text the arrow is attached to */
1197
- id: Id;
1198
- /** On which end the arrow is tighten to the text */
1199
- side: Side;
1200
- /** id of the text or node the arrow is attached to */
1201
- target: Id;
1202
- /** Text or node */
1203
- targetType: TargetType;
1204
- /** Typed snap point — semantics depend on targetType, see Magnet union. */
1205
- magnet: Magnet;
1206
- }
1207
-
1208
- declare type Magnet = NodeMagnet | EdgeMagnet | BoxMagnet | PolygonMagnet;
1209
-
1210
- export declare const MAX_RECENT_COLORS = 3;
1211
-
1212
- /** Arrow snapped to the center or perimeter of a node. */
1213
- declare type NodeMagnet = {
1214
- type: "node";
1215
- center: boolean;
1216
- };
1217
-
1218
- /** Whether panel sections stack top-to-bottom or run left-to-right. */
1219
- export declare type PanelOrientation = "vertical" | "horizontal";
1220
-
1221
- /**
1222
- * Placement and orientation options shared by the vanilla `AnnotationPanel`
1223
- * and the React panel/controller. Both are applied as `data-placement` /
1224
- * `data-orientation` attributes on the panel root, consumed by the rules in
1225
- * `ui/styles.css` — no inline layout styles are set from JS.
1226
- */
1227
- /** Which screen edge/corner the panel docks to. */
1228
- export declare type PanelPlacement = "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right";
1229
-
1230
- /**
1231
- * The slice of `Control` this state machine relies on. Declared structurally so
1232
- * the function does not pull the full `Control` type into the `/ui` entry's
1233
- * rolled declarations (which would otherwise create a duplicate, incompatible
1234
- * `Control` identity for consumers).
1235
- */
1236
- export declare interface PanelVisibilityControl {
1237
- on(event: string, handler: (...args: any[]) => void): unknown;
1238
- off(event: string, handler: (...args: any[]) => void): unknown;
1239
- once(event: string, handler: (...args: any[]) => void): unknown;
1240
- getAnnotation(id: string | number): Annotation | undefined;
1241
- isDrawing(): boolean;
1242
- }
1243
-
1244
- export declare interface PanelVisibilityHandlers {
1245
- /** Called when the panel should be shown for `annotation`. */
1246
- onShow: (annotation: Annotation) => void;
1247
- /** Called when the panel should be hidden. */
1248
- onHide: () => void;
1249
- }
1250
-
1251
- /** 2D coordinate */
1252
- declare type Point = {
1253
- x: number;
1254
- y: number;
1255
- };
1256
-
1257
- /**
1258
- * Polygon placed on the graph, use it to highlight areas
1259
- */
1260
- declare interface Polygon extends AnnotationFeature<Polygon_2, PolygonProperties> {
1261
- }
1262
-
1263
- /**
1264
- * Arrow snapped to a polygon annotation.
1265
- * rx/ry are 0–1 fractions of the polygon's bounding box from its top-left corner.
1266
- */
1267
- declare type PolygonMagnet = {
1268
- type: "polygon";
1269
- rx: number;
1270
- ry: number;
1271
- };
1272
-
1273
- declare interface PolygonProperties extends AnnotationProps {
1274
- type: "polygon";
1275
- style?: PolygonStyle;
1276
- }
1277
-
1278
- declare interface PolygonStyle extends BoxStyle {
1279
- }
1280
-
1281
- export declare interface RecentColorsState {
1282
- colors: string[];
1283
- /** Index of the currently active swatch. */
1284
- activeIndex: number;
1285
- }
1286
-
1287
- /** Channel-wise rgba, matching the shape emitted by `vanilla-colorful`. */
1288
- export declare interface RgbaChannels {
1289
- r: number;
1290
- g: number;
1291
- b: number;
1292
- a: number;
1293
- }
1294
-
1295
- /** Channel-wise rgba color, as read/written by the picker's `color` property. */
1296
- export declare interface RgbaColor {
1297
- r: number;
1298
- g: number;
1299
- b: number;
1300
- a: number;
1301
- }
1302
-
1303
- /**
1304
- * RGBA color string in format rgba(r, g, b, a)
1305
- * @example "rgba(255, 0, 0, 1)" | "rgba(128, 128, 128, 0.5)"
1306
- */
1307
- declare type RgbaColor_2 = `rgba(${number}, ${number}, ${number}, ${number})` | `rgba(${number},${number},${number},${number})`;
1308
-
1309
- /**
1310
- * The public surface of the `<rgba-color-picker>` element we rely on. Declared
1311
- * locally (rather than re-exporting `vanilla-colorful`'s class type) so that
1312
- * consumers don't have to resolve the package's deep `lib/entrypoints` types,
1313
- * which it does not expose via its `exports` map.
1314
- */
1315
- export declare interface RgbaColorPicker extends HTMLElement {
1316
- color: RgbaColor;
1317
- addEventListener(type: "color-changed", listener: (event: CustomEvent<{
1318
- value: RgbaColor;
1319
- }>) => void): void;
1320
- addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
1321
- }
1322
-
1323
- /** Serializes an rgba object (as emitted by the color picker) to a CSS string. */
1324
- export declare function rgbaToString(color: RgbaChannels): string;
1325
-
1326
- /**
1327
- * RGB color string in format rgb(r, g, b)
1328
- * @example "rgb(255, 0, 0)" | "rgb(128, 128, 128)"
1329
- */
1330
- declare type RgbColor = `rgb(${number}, ${number}, ${number})` | `rgb(${number},${number},${number})`;
1331
-
1332
- declare type Side = typeof SIDE_START | typeof SIDE_END;
1333
-
1334
- declare const SIDE_END: "end";
1335
-
1336
- declare const SIDE_START: "start";
1337
-
1338
- /** Stroke style options for annotations */
1339
- declare type StrokeOptions = {
1340
- /** Type of stroke: plain, dashed, or none */
1341
- strokeType?: StrokeType;
1342
- /** Stroke color: #f00, yellow... */
1343
- strokeColor?: Color;
1344
- /** Stroke width */
1345
- strokeWidth?: number;
1346
- };
1347
-
1348
- /** Stroke types available for annotations */
1349
- declare type StrokeType = "plain" | "dashed" | "none";
1350
-
1351
- /**
1352
- * Returns a complete `<svg>` string for the named icon, ready to drop into
1353
- * `innerHTML`. Used by the vanilla panel.
1354
- */
1355
- export declare function svgIcon(name: IconName, size?: number): string;
1356
-
1357
- /** @private */
1358
- declare const TARGET_TYPES: {
1359
- TEXT: "text";
1360
- NODE: "node";
1361
- BOX: "box";
1362
- COMMENT: "comment";
1363
- POLYGON: "polygon";
1364
- ANNOTATION: "annotation";
1365
- EDGE: "edge";
1366
- };
1367
-
1368
- declare type TargetType = (typeof TARGET_TYPES)[keyof typeof TARGET_TYPES];
1369
-
1370
- /**
1371
- * Text annotation feature, represents a text box at a specific position
1372
- */
1373
- declare interface Text_2 extends AnnotationFeature<Point_2, TextProperties> {
1374
- }
1375
-
1376
- declare interface TextProperties extends Omit<BoxProperties, "type"> {
1377
- type: "text";
1378
- /**text to display*/
1379
- content: string;
1380
- /** Width of the text box */
1381
- width: number;
1382
- /** Height of the text box */
1383
- height: number;
1384
- style?: TextStyle;
1385
- }
1386
-
1387
- declare interface TextStyle extends BoxStyle {
1388
- /** Helvetica, sans-serif... */
1389
- font?: string;
1390
- /** Font size, in pixels */
1391
- fontSize?: number | string;
1392
- /** text color: #f00, yellow...*/
1393
- color?: Color;
1394
- /** background color: empty for transparent #f00, yellow...*/
1395
- background?: Color;
1396
- /** padding around the text */
1397
- padding?: number;
1398
- /** Text box border radius */
1399
- borderRadius?: number;
1400
- /** When true, text maintains constant size regardless of zoom level */
1401
- fixedSize?: boolean;
1402
- /**
1403
- * Ghost text shown (via the textarea's native `placeholder` attribute)
1404
- * while `content` is empty - disappears the instant the user types, no
1405
- * selection/focus tricks needed. Overrides the global
1406
- * `ControllerOptions.textPlaceholder` for this annotation.
1407
- */
1408
- placeholder?: string;
1409
- }
1410
-
1411
- /** One of the toolbar's drawing tools - see `enabledTypes`. */
1412
- export declare type ToolbarDrawingType = "arrow" | "comment" | "sticky-note" | "box" | "text" | "polygon";
1413
-
1414
- /**
1415
- * Given the current recent-colors state and a color coming from the selected
1416
- * annotation, returns the next state: if the color is already known it becomes
1417
- * active, otherwise it is unshifted to the front (capped at MAX_RECENT_COLORS).
1418
- *
1419
- * Pure — callers (React/vanilla) hold the state and apply the result.
1420
- */
1421
- export declare function withColorFromAnnotation(state: RecentColorsState, color: string): RecentColorsState;
1422
-
1423
- 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 };