orcasvn-react-diagrams 0.2.0 → 0.2.2

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.
Files changed (133) hide show
  1. package/README.md +22 -1
  2. package/ai/api-contract.json +57 -5
  3. package/ai/invariants.json +5 -3
  4. package/ai/manifest.json +1 -1
  5. package/dist/cjs/examples.js +11775 -0
  6. package/dist/cjs/index.js +3889 -1112
  7. package/dist/cjs/types/api/createDiagramEditor.d.ts +7 -2
  8. package/dist/cjs/types/api/types.d.ts +178 -0
  9. package/dist/cjs/types/displaybox/demos/DeletionEventsDemoTab.d.ts +3 -0
  10. package/dist/cjs/types/displaybox/demos/ShapeHoverControlsDemoTab.d.ts +3 -0
  11. package/dist/cjs/types/displaybox/demos/TextLayoutDemoTab.d.ts +3 -0
  12. package/dist/cjs/types/displaybox/demos/deletionEventsDemo.d.ts +2 -0
  13. package/dist/cjs/types/displaybox/demos/rotatedCreationDemo.d.ts +2 -0
  14. package/dist/cjs/types/displaybox/demos/roundedRectRadiusDemo.d.ts +2 -0
  15. package/dist/cjs/types/displaybox/demos/shapeBorderMovementDemo.d.ts +2 -0
  16. package/dist/cjs/types/displaybox/demos/shapeHoverControlsDemo.d.ts +10 -0
  17. package/dist/cjs/types/displaybox/demos/textDemo.d.ts +4 -0
  18. package/dist/cjs/types/displaybox/useDemoEditor.d.ts +5 -2
  19. package/dist/cjs/types/engine/AutoLayoutService.d.ts +24 -0
  20. package/dist/cjs/types/engine/DiagramEngine.d.ts +32 -14
  21. package/dist/cjs/types/engine/EngineCommands.d.ts +4 -1
  22. package/dist/cjs/types/engine/LinkRoutingService.d.ts +35 -0
  23. package/dist/cjs/types/engine/MutationPipeline.d.ts +23 -0
  24. package/dist/cjs/types/engine/TextLayoutService.d.ts +40 -0
  25. package/dist/cjs/types/examples/index.d.ts +2 -0
  26. package/dist/cjs/types/measure/textStyleDefaults.d.ts +9 -0
  27. package/dist/cjs/types/models/DiagramModel.d.ts +1 -0
  28. package/dist/cjs/types/models/ElementModel.d.ts +1 -0
  29. package/dist/cjs/types/models/PortModel.d.ts +3 -0
  30. package/dist/cjs/types/models/TextModel.d.ts +8 -0
  31. package/dist/cjs/types/renderer/RenderTypes.d.ts +34 -1
  32. package/dist/cjs/types/renderer/konva/KonvaHitTester.d.ts +1 -1
  33. package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +53 -3
  34. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +18 -1
  35. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +49 -2
  36. package/dist/cjs/types/shapes/BuiltInShapes.d.ts +107 -0
  37. package/dist/cjs/types/shapes/__tests__/BuiltInShapes.test.d.ts +1 -0
  38. package/dist/cjs/types/shapes/index.d.ts +1 -0
  39. package/dist/cjs/types/utils/__tests__/borderGeometry.test.d.ts +1 -0
  40. package/dist/cjs/types/utils/borderGeometry.d.ts +6 -0
  41. package/dist/cjs/types/utils/geometry.d.ts +22 -0
  42. package/dist/esm/examples.js +11767 -0
  43. package/dist/esm/examples.js.map +1 -0
  44. package/dist/esm/index.js +3890 -1113
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/types/api/createDiagramEditor.d.ts +7 -2
  47. package/dist/esm/types/api/types.d.ts +178 -0
  48. package/dist/esm/types/displaybox/demos/DeletionEventsDemoTab.d.ts +3 -0
  49. package/dist/esm/types/displaybox/demos/ShapeHoverControlsDemoTab.d.ts +3 -0
  50. package/dist/esm/types/displaybox/demos/TextLayoutDemoTab.d.ts +3 -0
  51. package/dist/esm/types/displaybox/demos/deletionEventsDemo.d.ts +2 -0
  52. package/dist/esm/types/displaybox/demos/rotatedCreationDemo.d.ts +2 -0
  53. package/dist/esm/types/displaybox/demos/roundedRectRadiusDemo.d.ts +2 -0
  54. package/dist/esm/types/displaybox/demos/shapeBorderMovementDemo.d.ts +2 -0
  55. package/dist/esm/types/displaybox/demos/shapeHoverControlsDemo.d.ts +10 -0
  56. package/dist/esm/types/displaybox/demos/textDemo.d.ts +4 -0
  57. package/dist/esm/types/displaybox/useDemoEditor.d.ts +5 -2
  58. package/dist/esm/types/engine/AutoLayoutService.d.ts +24 -0
  59. package/dist/esm/types/engine/DiagramEngine.d.ts +32 -14
  60. package/dist/esm/types/engine/EngineCommands.d.ts +4 -1
  61. package/dist/esm/types/engine/LinkRoutingService.d.ts +35 -0
  62. package/dist/esm/types/engine/MutationPipeline.d.ts +23 -0
  63. package/dist/esm/types/engine/TextLayoutService.d.ts +40 -0
  64. package/dist/esm/types/examples/index.d.ts +2 -0
  65. package/dist/esm/types/measure/textStyleDefaults.d.ts +9 -0
  66. package/dist/esm/types/models/DiagramModel.d.ts +1 -0
  67. package/dist/esm/types/models/ElementModel.d.ts +1 -0
  68. package/dist/esm/types/models/PortModel.d.ts +3 -0
  69. package/dist/esm/types/models/TextModel.d.ts +8 -0
  70. package/dist/esm/types/renderer/RenderTypes.d.ts +34 -1
  71. package/dist/esm/types/renderer/konva/KonvaHitTester.d.ts +1 -1
  72. package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +53 -3
  73. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +18 -1
  74. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +49 -2
  75. package/dist/esm/types/shapes/BuiltInShapes.d.ts +107 -0
  76. package/dist/esm/types/shapes/__tests__/BuiltInShapes.test.d.ts +1 -0
  77. package/dist/esm/types/shapes/index.d.ts +1 -0
  78. package/dist/esm/types/utils/__tests__/borderGeometry.test.d.ts +1 -0
  79. package/dist/esm/types/utils/borderGeometry.d.ts +6 -0
  80. package/dist/esm/types/utils/geometry.d.ts +22 -0
  81. package/dist/examples.d.ts +532 -0
  82. package/dist/index.d.ts +233 -2
  83. package/docs/API_CONTRACT.md +59 -3
  84. package/docs/ARCHITECTURE.md +1 -0
  85. package/docs/CAPABILITIES.md +3 -1
  86. package/docs/COMMANDS_EVENTS.md +5 -0
  87. package/docs/DOCUMENTATION_WORKFLOW.md +6 -8
  88. package/docs/INTEGRATION_PLAYBOOK.md +2 -0
  89. package/docs/PORTING_CHECKLIST.md +1 -0
  90. package/docs/STATE_INVARIANTS.md +4 -0
  91. package/package.json +20 -10
  92. package/src/displaybox/demos/AutoLayoutDemoTab.tsx +501 -0
  93. package/src/displaybox/demos/DeletionEventsDemoTab.tsx +147 -0
  94. package/src/displaybox/demos/EngineEventsDemoTab.tsx +151 -0
  95. package/src/displaybox/demos/EventHandlersDemoTab.tsx +110 -0
  96. package/src/displaybox/demos/ExternalDragDropDemoTab.tsx +261 -0
  97. package/src/displaybox/demos/LinkCancelDemoTab.tsx +238 -0
  98. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +30 -0
  99. package/src/displaybox/demos/ShapeHoverControlsDemoTab.tsx +558 -0
  100. package/src/displaybox/demos/SimpleDemo.tsx +73 -0
  101. package/src/displaybox/demos/SvgPathDemoTab.tsx +327 -0
  102. package/src/displaybox/demos/TextLayoutDemoTab.tsx +386 -0
  103. package/src/displaybox/demos/autoLayoutDemo.ts +111 -0
  104. package/src/displaybox/demos/basicDemo.ts +131 -0
  105. package/src/displaybox/demos/childConstraintsDemo.ts +65 -0
  106. package/src/displaybox/demos/customDemo.ts +59 -0
  107. package/src/displaybox/demos/deletionEventsDemo.ts +91 -0
  108. package/src/displaybox/demos/engineEventsDemo.ts +64 -0
  109. package/src/displaybox/demos/eventHandlersDemo.ts +41 -0
  110. package/src/displaybox/demos/externalDragDropDemo.ts +28 -0
  111. package/src/displaybox/demos/gridOverlayDemo.ts +50 -0
  112. package/src/displaybox/demos/index.tsx +217 -0
  113. package/src/displaybox/demos/linkBendHandlesDemo.ts +143 -0
  114. package/src/displaybox/demos/linkCancelDemo.ts +56 -0
  115. package/src/displaybox/demos/linkPortCreationDemo.ts +46 -0
  116. package/src/displaybox/demos/multiLevelTreeDemo.ts +120 -0
  117. package/src/displaybox/demos/multipleElementsDemo.ts +62 -0
  118. package/src/displaybox/demos/nestedDemo.ts +78 -0
  119. package/src/displaybox/demos/obstacleRoutingDemo.ts +176 -0
  120. package/src/displaybox/demos/portBorderDemo.ts +98 -0
  121. package/src/displaybox/demos/portConstraintsDemo.ts +175 -0
  122. package/src/displaybox/demos/rotatedCreationDemo.ts +185 -0
  123. package/src/displaybox/demos/roundedRectRadiusDemo.ts +93 -0
  124. package/src/displaybox/demos/routingDemo.ts +57 -0
  125. package/src/displaybox/demos/selectionDemo.ts +49 -0
  126. package/src/displaybox/demos/shapeBorderMovementDemo.ts +126 -0
  127. package/src/displaybox/demos/shapeGalleryDemo.ts +73 -0
  128. package/src/displaybox/demos/shapeHoverControlsDemo.ts +172 -0
  129. package/src/displaybox/demos/shared.ts +161 -0
  130. package/src/displaybox/demos/svgPathDemo.ts +71 -0
  131. package/src/displaybox/demos/textDemo.ts +62 -0
  132. package/src/displaybox/types.ts +66 -0
  133. package/src/examples/index.ts +21 -0
package/dist/index.d.ts CHANGED
@@ -20,6 +20,130 @@ type DiagramContainer = {
20
20
  getBoundingClientRect: () => ClientRectLike;
21
21
  };
22
22
  type MoveConstraint = 'free' | 'inside' | 'border';
23
+ type BorderSide$1 = 'left' | 'right' | 'top' | 'bottom';
24
+ type HostAnchorPreset = 'vertices' | 'cardinal';
25
+ type PortAnchor = {
26
+ id: string;
27
+ position: Point;
28
+ side?: BorderSide$1;
29
+ normal?: Point;
30
+ meta?: Record<string, unknown>;
31
+ };
32
+ type PortAnchorConstraint = {
33
+ preset: HostAnchorPreset;
34
+ fallback?: 'nearest';
35
+ };
36
+ type ResolvePortAnchorsOptions = {
37
+ preset: HostAnchorPreset;
38
+ };
39
+ type ShapeVertexTarget = {
40
+ id: string;
41
+ position: Point;
42
+ };
43
+ type ShapeEdgeTarget = {
44
+ id: string;
45
+ start: Point;
46
+ end: Point;
47
+ midpoint: Point;
48
+ };
49
+ declare enum EllipseMidPoint {
50
+ top = "top",
51
+ right = "right",
52
+ bottom = "bottom",
53
+ left = "left"
54
+ }
55
+ type ShapeEllipseMidPointTarget = {
56
+ id: EllipseMidPoint;
57
+ ellipseMidPoint: EllipseMidPoint;
58
+ position: Point;
59
+ };
60
+ type ShapeHoverGeometry = {
61
+ vertices: ShapeVertexTarget[];
62
+ edges: ShapeEdgeTarget[];
63
+ };
64
+ type HoverControlIcon = {
65
+ svgPath: string;
66
+ size?: Size;
67
+ style?: Record<string, unknown>;
68
+ };
69
+ type ShapeControlTargetKind = 'vertex' | 'edge' | 'midpoint' | 'ellipse-midpoint';
70
+ type ShapeControlVisibilityTrigger = 'element-hover' | 'target-hover';
71
+ type ShapeControlDefinition = {
72
+ id: string;
73
+ targetKind: ShapeControlTargetKind;
74
+ icon: HoverControlIcon;
75
+ targetIndices?: number[];
76
+ ellipseMidPoints?: EllipseMidPoint | EllipseMidPoint[];
77
+ allowAllTargets?: boolean;
78
+ visibilityTriggers?: ShapeControlVisibilityTrigger[];
79
+ lineStyle?: Record<string, unknown>;
80
+ tolerance?: number;
81
+ };
82
+ /**
83
+ * @deprecated Use ShapeControlDefinition with targetKind='edge'.
84
+ */
85
+ type EdgeHoverControl = {
86
+ kind: 'edge';
87
+ icon: HoverControlIcon;
88
+ lineStyle?: Record<string, unknown>;
89
+ tolerance?: number;
90
+ };
91
+ /**
92
+ * @deprecated Use ShapeControlDefinition with targetKind='vertex'.
93
+ */
94
+ type VertexHoverControl = {
95
+ kind: 'vertex';
96
+ icon: HoverControlIcon;
97
+ tolerance?: number;
98
+ };
99
+ type ElementShapeHoverControls = {
100
+ controls?: ShapeControlDefinition[];
101
+ /**
102
+ * @deprecated Legacy singleton edge control. Use controls[] instead.
103
+ */
104
+ edge?: EdgeHoverControl;
105
+ /**
106
+ * @deprecated Legacy singleton vertex control. Use controls[] instead.
107
+ */
108
+ vertex?: VertexHoverControl;
109
+ /**
110
+ * @deprecated Legacy resolution preference for singleton edge/vertex controls.
111
+ */
112
+ priority?: 'vertex-first';
113
+ };
114
+ type ElementShapeControlEventType = 'click' | 'drag-start' | 'drag-move' | 'drag-end';
115
+ type ElementShapeControlDragEvent = {
116
+ sessionId: string;
117
+ startPointer: EnginePointerInfo;
118
+ delta: Point;
119
+ };
120
+ type ElementShapeHoverControlInteractionEvent = {
121
+ eventType: ElementShapeControlEventType;
122
+ controlId: string;
123
+ elementId: string;
124
+ targetKind: ShapeControlTargetKind;
125
+ targetIndex: number;
126
+ pointer: EnginePointerInfo;
127
+ edge?: ShapeEdgeTarget;
128
+ vertex?: ShapeVertexTarget;
129
+ ellipseMidPoint?: EllipseMidPoint;
130
+ drag?: ElementShapeControlDragEvent;
131
+ };
132
+ /**
133
+ * @deprecated Use ElementShapeHoverControlInteractionEvent with eventType='click'.
134
+ */
135
+ type ElementShapeHoverControlActivationEvent = {
136
+ controlKind: 'edge' | 'vertex';
137
+ elementId: string;
138
+ targetId: string;
139
+ pointer: EnginePointerInfo;
140
+ edge?: ShapeEdgeTarget;
141
+ vertex?: ShapeVertexTarget;
142
+ };
143
+ type ElementPortMovementPolicy = {
144
+ moveMode: MoveConstraint | 'anchors';
145
+ anchorConstraint?: PortAnchorConstraint;
146
+ };
23
147
  type LinkRoutingMode = 'auto' | 'manual';
24
148
  type AnchorReference = 'top-left' | 'center';
25
149
  type RerouteLinksOptions = {
@@ -27,6 +151,11 @@ type RerouteLinksOptions = {
27
151
  };
28
152
  type ElementLayoutMode = 'manual' | 'horizontal' | 'vertical';
29
153
  type ElementLayoutAlign = 'start' | 'center' | 'end';
154
+ type ElementLayoutChildFitMainAxis = 'none' | 'distribute';
155
+ type ElementLayoutChildFitCrossAxis = 'none' | 'stretch';
156
+ type TextLayoutBoundsMode = 'owner-width' | 'owner-box' | 'fixed';
157
+ type TextLayoutWrapMode = 'none' | 'word' | 'char';
158
+ type TextLayoutOverflowMode = 'clip' | 'ellipsis-end' | 'ellipsis-middle' | 'ellipsis-start';
30
159
  type ElementLayout = {
31
160
  mode: ElementLayoutMode;
32
161
  padding?: number | {
@@ -35,6 +164,18 @@ type ElementLayout = {
35
164
  };
36
165
  gap?: number;
37
166
  align?: ElementLayoutAlign;
167
+ childFitMainAxis?: ElementLayoutChildFitMainAxis;
168
+ childFitCrossAxis?: ElementLayoutChildFitCrossAxis;
169
+ childFitMinSize?: Partial<Size>;
170
+ childFitMaxSize?: Partial<Size>;
171
+ };
172
+ type TextLayout = {
173
+ boundsMode?: TextLayoutBoundsMode;
174
+ wrap?: TextLayoutWrapMode;
175
+ overflow?: TextLayoutOverflowMode;
176
+ padding?: number;
177
+ maxLines?: number;
178
+ fixedSize?: Size;
38
179
  };
39
180
  type ElementDropEvent = {
40
181
  elementId: string;
@@ -68,6 +209,7 @@ type ElementData = {
68
209
  moveMode?: MoveConstraint;
69
210
  anchorCenter?: boolean;
70
211
  layout?: ElementLayout;
212
+ portMovement?: ElementPortMovementPolicy;
71
213
  };
72
214
  type PortData = {
73
215
  id: string;
@@ -79,6 +221,8 @@ type PortData = {
79
221
  textIds?: string[];
80
222
  moveMode?: MoveConstraint;
81
223
  anchorCenter?: boolean;
224
+ orientToHostBorder?: boolean;
225
+ currentAnchorId?: string;
82
226
  };
83
227
  type ShapeDrawContext = {
84
228
  ctx: CanvasRenderingContext2D;
@@ -100,6 +244,10 @@ type TextData = {
100
244
  size?: Size;
101
245
  style?: Record<string, unknown>;
102
246
  ownerId?: string | null;
247
+ layout?: TextLayout;
248
+ displayContent?: string;
249
+ displayOffset?: Point;
250
+ displayClipSize?: Size;
103
251
  };
104
252
  type DiagramState = {
105
253
  elements: ElementData[];
@@ -172,6 +320,26 @@ type ElementResizedEvent = {
172
320
  type ElementDeletedEvent = {
173
321
  elementId: string;
174
322
  };
323
+ type PortDeletedEvent = {
324
+ portId: string;
325
+ elementId: string;
326
+ };
327
+ type LinkDeletedEvent = {
328
+ linkId: string;
329
+ sourcePortId: string;
330
+ targetPortId: string;
331
+ };
332
+ type TextDeletedEvent = {
333
+ textId: string;
334
+ ownerId?: string | null;
335
+ };
336
+ type TextUpdatedEvent = {
337
+ textId: string;
338
+ ownerId?: string | null;
339
+ content: string;
340
+ displayContent: string;
341
+ reason: 'content' | 'layout';
342
+ };
175
343
  type ElementLinkStartedEvent = {
176
344
  sourcePortId: string;
177
345
  sourceElementId: string;
@@ -218,6 +386,10 @@ type EngineEventMap = {
218
386
  elementMoved: ElementMovedEvent;
219
387
  elementResized: ElementResizedEvent;
220
388
  elementDeleted: ElementDeletedEvent;
389
+ portDeleted: PortDeletedEvent;
390
+ linkDeleted: LinkDeletedEvent;
391
+ textDeleted: TextDeletedEvent;
392
+ textUpdated: TextUpdatedEvent;
221
393
  elementSelected: ElementSelectedEvent;
222
394
  elementLinkStarted: ElementLinkStartedEvent;
223
395
  elementLinkConnecting: ElementLinkConnectingEvent;
@@ -262,10 +434,16 @@ type DiagramEngineHandle = {
262
434
  setSnapping: (snapper: unknown) => void;
263
435
  registerShape: (shape: {
264
436
  id: string;
437
+ baseRotation?: number;
265
438
  draw?: (context: ShapeDrawContext) => void;
266
439
  createNode?: (model: ElementData | PortData) => unknown;
267
440
  svgPath?: string;
268
441
  svgSize?: Size;
442
+ projectToBorder?: (point: Point, rect: Rect) => Point;
443
+ resolveBorderSide?: (point: Point, rect: Rect) => BorderSide$1;
444
+ resolvePortAnchors?: (rect: Rect, options: ResolvePortAnchorsOptions) => PortAnchor[];
445
+ resolveHoverGeometry?: (rect: Rect) => ShapeHoverGeometry | undefined;
446
+ resolveEllipseMidPoints?: (rect: Rect) => ShapeEllipseMidPointTarget[] | undefined;
269
447
  }) => void;
270
448
  render: () => void;
271
449
  };
@@ -282,6 +460,7 @@ declare class ElementModel {
282
460
  moveMode?: MoveConstraint;
283
461
  anchorCenter?: boolean;
284
462
  layout?: ElementData['layout'];
463
+ portMovement?: ElementData['portMovement'];
285
464
  constructor(data: ElementData);
286
465
  setPosition(position: Point): void;
287
466
  setSize(size: Size): void;
@@ -326,8 +505,11 @@ declare class PortModel {
326
505
  textIds: string[];
327
506
  moveMode?: MoveConstraint;
328
507
  anchorCenter?: boolean;
508
+ orientToHostBorder: boolean;
509
+ currentAnchorId?: string;
329
510
  constructor(data: PortData);
330
511
  setPosition(position: Point): void;
512
+ setCurrentAnchorId(currentAnchorId?: string): void;
331
513
  addText(textId: string): void;
332
514
  removeText(textId: string): void;
333
515
  toData(): PortData;
@@ -340,9 +522,17 @@ declare class TextModel {
340
522
  size?: Size;
341
523
  style?: Record<string, unknown>;
342
524
  ownerId?: string | null;
525
+ layout?: TextData['layout'];
526
+ displayContent?: string;
527
+ displayOffset?: Point;
528
+ displayClipSize?: Size;
343
529
  constructor(data: TextData);
344
530
  setContent(content: string): void;
345
531
  setSize(size: Size): void;
532
+ setDisplayContent(content: string): void;
533
+ setDisplayOffset(offset: Point | undefined): void;
534
+ setDisplayClipSize(size: Size | undefined): void;
535
+ setLayout(layout: TextData['layout']): void;
346
536
  setPosition(position: Point): void;
347
537
  toData(): TextData;
348
538
  }
@@ -374,6 +564,7 @@ declare class DiagramModel {
374
564
  removeElement(id: string): void;
375
565
  addPort(data: PortData): PortModel;
376
566
  movePort(id: string, position: Point): void;
567
+ setPortCurrentAnchorId(id: string, currentAnchorId?: string): void;
377
568
  removePort(id: string): void;
378
569
  addLink(data: LinkData): LinkModel;
379
570
  updateLinkPoints(id: string, points: Point[]): void;
@@ -426,8 +617,32 @@ interface SnapStrategy {
426
617
  snap(point: Point): Point;
427
618
  }
428
619
 
620
+ type BorderSide = 'left' | 'right' | 'top' | 'bottom';
621
+
622
+ type ShapeNodeSizeUpdateContext = {
623
+ size: {
624
+ width: number;
625
+ height: number;
626
+ };
627
+ anchorCenter: boolean;
628
+ updateOffsetX: boolean;
629
+ updateOffsetY: boolean;
630
+ getNodeAttr: <T = unknown>(key: string) => T | undefined;
631
+ };
632
+ type ShapeNodeSizeUpdater = (context: ShapeNodeSizeUpdateContext) => Record<string, unknown>;
633
+ type ShapeResizeHandle = 'nw' | 'ne' | 'sw' | 'se';
634
+ type ShapeResizeNormalizeContext = {
635
+ element: ElementData;
636
+ handle: ShapeResizeHandle;
637
+ minSize: number;
638
+ proposal: {
639
+ width: number;
640
+ height: number;
641
+ };
642
+ };
429
643
  type ShapeDefinition = {
430
644
  id: string;
645
+ baseRotation?: number;
431
646
  draw?: (context: ShapeDrawContext) => void;
432
647
  createNode?: (model: ElementData | PortData) => unknown;
433
648
  svgPath?: string;
@@ -435,6 +650,16 @@ type ShapeDefinition = {
435
650
  width: number;
436
651
  height: number;
437
652
  };
653
+ sizeUpdater?: ShapeNodeSizeUpdater;
654
+ normalizeResize?: (context: ShapeResizeNormalizeContext) => {
655
+ width: number;
656
+ height: number;
657
+ };
658
+ projectToBorder?: (point: Point, rect: Rect) => Point;
659
+ resolveBorderSide?: (point: Point, rect: Rect) => BorderSide;
660
+ resolvePortAnchors?: (rect: Rect, options: ResolvePortAnchorsOptions) => PortAnchor[];
661
+ resolveHoverGeometry?: (rect: Rect) => ShapeHoverGeometry | undefined;
662
+ resolveEllipseMidPoints?: (rect: Rect) => ShapeEllipseMidPointTarget[] | undefined;
438
663
  };
439
664
  declare class ShapeRegistry {
440
665
  private shapes;
@@ -457,9 +682,11 @@ type DiagramEngineConfig = {
457
682
  };
458
683
  declare const createDiagramEngine: (config: DiagramEngineConfig) => DiagramEngineHandle;
459
684
 
685
+ type BuiltInShapeKind = 'rect' | 'circle' | 'ellipse' | 'diamond' | 'triangle' | 'pentagon' | 'hexagon';
686
+
460
687
  type SimpleShape = {
461
688
  id: string;
462
- kind: 'rect' | 'circle' | 'diamond' | 'triangle';
689
+ kind: BuiltInShapeKind;
463
690
  style?: Record<string, unknown>;
464
691
  };
465
692
  type DiagramEditorConfig = {
@@ -470,13 +697,17 @@ type DiagramEditorConfig = {
470
697
  elementShapes?: SimpleShape[];
471
698
  portShapes?: SimpleShape[];
472
699
  panKey?: 'Control' | 'Shift' | 'Alt' | 'Meta';
700
+ elementShapeHoverControls?: ElementShapeHoverControls;
701
+ onElementShapeHoverControlInteraction?: (event: ElementShapeHoverControlInteractionEvent) => void;
702
+ onElementShapeHoverControlActivated?: (event: ElementShapeHoverControlActivationEvent) => void;
473
703
  onChange?: (event: EngineChangeEvent) => void;
474
704
  onSelection?: (event: EngineSelectionEvent) => void;
475
705
  };
476
706
  type DiagramEditorHandle = DiagramEngineHandle & {
477
707
  resize: (width: number, height: number) => void;
708
+ setElementShapeHoverControls: (controls?: ElementShapeHoverControls) => void;
478
709
  destroy: () => void;
479
710
  };
480
711
  declare const createDiagramEditor: (config: DiagramEditorConfig) => DiagramEditorHandle;
481
712
 
482
- export { type AnchorReference, type ClientRectLike, type DiagramContainer, type DiagramEditorConfig, type DiagramEditorHandle, type DiagramEngineHandle, type DiagramPatch, type DiagramState, type ElementData, type ElementDeletedEvent, type ElementDropEvent, type ElementLayout, type ElementLayoutAlign, type ElementLayoutMode, type ElementLinkConnectingEvent, type ElementLinkEndedEvent, type ElementLinkStartedEvent, type ElementMovedEvent, type ElementPointerEvent, type ElementResizedEvent, type ElementSelectedEvent, type EngineChangeEvent, type EngineConfigEvent, type EngineEventMap, type EnginePointerInfo, type EngineSelectionEvent, type LinkData, type LinkRoutingMode, type MoveConstraint, type OverlayShapeConfig, type OverlayShapeHandle, type PaperClickEvent, type Point, type PortData, type PortMouseEvent, type PortMovedEvent, type PortSelectedEvent, type Rect, type RerouteLinksOptions, type ShapeDrawContext, type SimpleShape, type Size, type TextData, type TextSelectedEvent, createDiagramEditor, createDiagramEngine };
713
+ export { type AnchorReference, type BorderSide$1 as BorderSide, type ClientRectLike, type DiagramContainer, type DiagramEditorConfig, type DiagramEditorHandle, type DiagramEngineHandle, type DiagramPatch, type DiagramState, type EdgeHoverControl, type ElementData, type ElementDeletedEvent, type ElementDropEvent, type ElementLayout, type ElementLayoutAlign, type ElementLayoutChildFitCrossAxis, type ElementLayoutChildFitMainAxis, type ElementLayoutMode, type ElementLinkConnectingEvent, type ElementLinkEndedEvent, type ElementLinkStartedEvent, type ElementMovedEvent, type ElementPointerEvent, type ElementPortMovementPolicy, type ElementResizedEvent, type ElementSelectedEvent, type ElementShapeControlDragEvent, type ElementShapeControlEventType, type ElementShapeHoverControlActivationEvent, type ElementShapeHoverControlInteractionEvent, type ElementShapeHoverControls, EllipseMidPoint, type EngineChangeEvent, type EngineConfigEvent, type EngineEventMap, type EnginePointerInfo, type EngineSelectionEvent, type HostAnchorPreset, type HoverControlIcon, type LinkData, type LinkDeletedEvent, type LinkRoutingMode, type MoveConstraint, type OverlayShapeConfig, type OverlayShapeHandle, type PaperClickEvent, type Point, type PortAnchor, type PortAnchorConstraint, type PortData, type PortDeletedEvent, type PortMouseEvent, type PortMovedEvent, type PortSelectedEvent, type Rect, type RerouteLinksOptions, type ResolvePortAnchorsOptions, type ShapeControlDefinition, type ShapeControlTargetKind, type ShapeControlVisibilityTrigger, type ShapeDrawContext, type ShapeEdgeTarget, type ShapeEllipseMidPointTarget, type ShapeHoverGeometry, type ShapeVertexTarget, type SimpleShape, type Size, type TextData, type TextDeletedEvent, type TextLayout, type TextLayoutBoundsMode, type TextLayoutOverflowMode, type TextLayoutWrapMode, type TextSelectedEvent, type TextUpdatedEvent, type VertexHoverControl, createDiagramEditor, createDiagramEngine };
@@ -23,6 +23,9 @@ Primary definitions:
23
23
  - `elementShapes?: SimpleShape[]`
24
24
  - `portShapes?: SimpleShape[]`
25
25
  - `panKey?: 'Control' | 'Shift' | 'Alt' | 'Meta'` (default: `'Control'`)
26
+ - `elementShapeHoverControls?: ElementShapeHoverControls`
27
+ - `onElementShapeHoverControlInteraction?: (event: ElementShapeHoverControlInteractionEvent) => void`
28
+ - `onElementShapeHoverControlActivated?: (event: ElementShapeHoverControlActivationEvent) => void` (deprecated callback contract)
26
29
  - `onChange?: (event: EngineChangeEvent) => void`
27
30
  - `onSelection?: (event: EngineSelectionEvent) => void`
28
31
 
@@ -55,7 +58,7 @@ Defaults:
55
58
 
56
59
  ### `ElementData`
57
60
  - Required: `id`, `position`, `size`, `shapeId`
58
- - Optional: `style`, `portIds`, `textIds`, `parentId`, `moveMode`, `anchorCenter`, `layout`
61
+ - Optional: `style`, `portIds`, `textIds`, `parentId`, `moveMode`, `anchorCenter`, `layout`, `portMovement`
59
62
  - Defaults at runtime/model:
60
63
  - `portIds`: `[]`
61
64
  - `textIds`: `[]`
@@ -64,17 +67,27 @@ Defaults:
64
67
  ### `ElementLayout`
65
68
  - Required: `mode: 'manual' | 'horizontal' | 'vertical'`
66
69
  - Optional: `padding?: number | { x: number; y: number }`, `gap?: number`, `align?: 'start' | 'center' | 'end'`
70
+ - Optional fit controls: `childFitMainAxis?: 'none' | 'distribute'`, `childFitCrossAxis?: 'none' | 'stretch'`, `childFitMinSize?: Partial<Size>`, `childFitMaxSize?: Partial<Size>`
67
71
  - Layout defaults when `mode !== 'manual'`:
68
72
  - `padding`: `12` (or `{ x: 12, y: 12 }`)
69
73
  - `gap`: `12`
70
74
  - `align`: `'center'`
71
75
 
76
+ ### `ElementPortMovementPolicy`
77
+ - `moveMode: 'free' | 'inside' | 'border' | 'anchors'`
78
+ - Optional `anchorConstraint?: PortAnchorConstraint`
79
+
80
+ ### `PortAnchorConstraint`
81
+ - `preset: 'vertices' | 'cardinal'`
82
+ - Optional `fallback?: 'nearest'`
83
+
72
84
  ### `PortData`
73
85
  - Required: `id`, `elementId`, `position`
74
- - Optional: `shapeId`, `size`, `style`, `textIds`, `moveMode`, `anchorCenter`
86
+ - Optional: `shapeId`, `size`, `style`, `textIds`, `moveMode`, `anchorCenter`, `orientToHostBorder`, `currentAnchorId`
75
87
  - Defaults at runtime/model:
76
88
  - `textIds`: `[]`
77
89
  - `anchorCenter`: `true`
90
+ - `orientToHostBorder`: `true`
78
91
 
79
92
  ### `LinkData`
80
93
  - Required: `id`, `sourcePortId`, `targetPortId`, `points`
@@ -85,9 +98,31 @@ Defaults:
85
98
 
86
99
  ### `TextData`
87
100
  - Required: `id`, `content`, `position`
88
- - Optional: `size`, `style`, `ownerId`
101
+ - Optional: `size`, `style`, `ownerId`, `layout`, `displayContent`, `displayOffset`, `displayClipSize`
89
102
  - Defaults at runtime/model:
90
103
  - `ownerId`: `null`
104
+ - `displayContent`: mirrors `content` at creation
105
+
106
+ ### `TextLayout`
107
+ - Optional `boundsMode?: 'owner-width' | 'owner-box' | 'fixed'`
108
+ - Optional `wrap?: 'none' | 'word' | 'char'`
109
+ - Optional `overflow?: 'clip' | 'ellipsis-end' | 'ellipsis-middle' | 'ellipsis-start'`
110
+ - Optional `padding?: number`
111
+ - Optional `maxLines?: number`
112
+ - Optional `fixedSize?: Size`
113
+
114
+ ### Hover Control Contracts
115
+ - `ElementShapeHoverControls`:
116
+ - Preferred: `controls?: ShapeControlDefinition[]`
117
+ - Legacy deprecated: `edge`, `vertex`, `priority`
118
+ - `ShapeControlDefinition`:
119
+ - Required: `id`, `targetKind`, `icon`
120
+ - Optional: `targetIndices`, `ellipseMidPoints`, `allowAllTargets`, `visibilityTriggers`, `lineStyle`, `tolerance`
121
+ - `ShapeControlTargetKind`: `'vertex' | 'edge' | 'midpoint' | 'ellipse-midpoint'`
122
+ - `ShapeControlVisibilityTrigger`: `'element-hover' | 'target-hover'`
123
+ - `ElementShapeHoverControlInteractionEvent`:
124
+ - lifecycle: `eventType` is `'click' | 'drag-start' | 'drag-move' | 'drag-end'`
125
+ - includes `controlId`, `elementId`, `targetKind`, `targetIndex`, `pointer`, and resolved target payload (`edge`, `vertex`, or `ellipseMidPoint`) when available
91
126
 
92
127
  ## Handle Methods
93
128
 
@@ -105,8 +140,23 @@ Defaults:
105
140
 
106
141
  ### Editor Only (`DiagramEditorHandle`)
107
142
  - `resize(width, height)`
143
+ - `setElementShapeHoverControls(controls?)`
108
144
  - `destroy()`
109
145
 
146
+ ### `registerShape` Definition Contract
147
+ - Required: `id`
148
+ - Optional behavior hooks:
149
+ - `baseRotation?: number`
150
+ - `draw?(context)`
151
+ - `createNode?(model)`
152
+ - `svgPath?: string`
153
+ - `svgSize?: Size`
154
+ - `projectToBorder?(point, rect)`
155
+ - `resolveBorderSide?(point, rect)`
156
+ - `resolvePortAnchors?(rect, options)`
157
+ - `resolveHoverGeometry?(rect)`
158
+ - `resolveEllipseMidPoints?(rect)`
159
+
110
160
  ## Events (`EngineEventMap`)
111
161
 
112
162
  - `change: EngineChangeEvent`
@@ -124,6 +174,10 @@ Defaults:
124
174
  - `elementMoved: ElementMovedEvent`
125
175
  - `elementResized: ElementResizedEvent`
126
176
  - `elementDeleted: ElementDeletedEvent`
177
+ - `portDeleted: PortDeletedEvent`
178
+ - `linkDeleted: LinkDeletedEvent`
179
+ - `textDeleted: TextDeletedEvent`
180
+ - `textUpdated: TextUpdatedEvent`
127
181
  - `elementSelected: ElementSelectedEvent`
128
182
  - `elementLinkStarted: ElementLinkStartedEvent`
129
183
  - `elementLinkConnecting: ElementLinkConnectingEvent` (cancelable through `cancel()`)
@@ -137,6 +191,8 @@ Defaults:
137
191
  - `clientToWorld` uses: `world = (client - containerRect - pan) / zoom`, with zoom fallback to `1` when zoom is `0`.
138
192
  - `rerouteLinks(ids)` skips manual links unless `options.includeManual === true`.
139
193
  - `updateLinkPoints` always marks the link as manual routing.
194
+ - Text layout/overflow resolution updates `displayContent` and emits `textUpdated` when `updateText` is called.
195
+ - Deletion mutators emit cascading `portDeleted`/`linkDeleted`/`textDeleted` for removed dependent entities.
140
196
 
141
197
  ## Verification References
142
198
  - `src/api/__tests__/createDiagramEditor.test.ts`
@@ -5,6 +5,7 @@
5
5
  ## Module Boundaries
6
6
  - `src/api/`: public entry points and type contracts.
7
7
  - `src/engine/`: command execution, event bus, selection, viewport, routing/snap integration.
8
+ - `src/engine/`: `DiagramEngine` orchestration plus extracted services (`TextLayoutService`, `AutoLayoutService`, `LinkRoutingService`, `MutationPipeline`).
8
9
  - `src/models/`: in-memory state graph for elements/ports/links/texts.
9
10
  - `src/renderer/`: renderer interface and shape registry contracts.
10
11
  - `src/renderer/konva/`: Konva implementation for render + interaction overlays.
@@ -9,6 +9,8 @@
9
9
  - Manual bend editing while preserving interior bends during endpoint moves.
10
10
  - Selection, marquee selection, and viewport pan/zoom.
11
11
  - Shape registration by canvas draw, custom node factory, or SVG path metadata.
12
+ - Shape-aware host border projection and anchor-constrained port placement.
13
+ - Element shape hover controls with edge/vertex/midpoint targets and interaction callbacks.
12
14
  - Overlay shapes for external drag ghosting and previews.
13
15
  - Event-driven host integration (`change`, `selection`, pointer/link lifecycle events).
14
16
 
@@ -19,7 +21,7 @@
19
21
  - Server-side conflict resolution or persistence workflows.
20
22
 
21
23
  ## Preconditions
22
- - Runtime: Node `>=18.17 <19` for this repo toolchain.
24
+ - Runtime: Node `>=20.17.0 <21.0.0` for this repo toolchain.
23
25
  - Browser host with Konva-compatible canvas environment when using `createDiagramEditor`.
24
26
 
25
27
  ## Side Effects
@@ -23,10 +23,14 @@
23
23
  - `add/move/remove port`
24
24
  - Always `change`
25
25
  - Plus `portMoved` on effective movement
26
+ - Plus `portDeleted` on removal (including cascade removal)
26
27
  - `add/update/remove link`
27
28
  - Always `change`
29
+ - Plus `linkDeleted` on removal (including cascade removal)
28
30
  - `add/update/move/remove text`
29
31
  - Always `change`
32
+ - Plus `textUpdated` when `updateText` resolves display layout
33
+ - Plus `textDeleted` on removal (including cascade removal)
30
34
  - `setSelection/toggleSelection/deleteSelection`
31
35
  - `selection` plus derived selected entity events
32
36
  - `setViewport`
@@ -52,6 +56,7 @@
52
56
  ## Failure Modes
53
57
  - Unknown IDs do not emit entity-specific movement/selection events.
54
58
  - Canceled link creation emits `elementLinkEnded` with `cancelled=true` and no link creation.
59
+ - `textUpdated` emits only when target text exists; missing IDs remain no-op.
55
60
 
56
61
  ## Verification References
57
62
  - `src/engine/EngineCommands.ts`
@@ -59,9 +59,9 @@ When resuming later:
59
59
  5. Re-run packaging verification.
60
60
 
61
61
  ## 6. Current Status
62
- - Last updated: 2026-02-26
63
- - Last completed step: 4
64
- - Next step: 5 (model/invariant docs deep pass)
62
+ - Last updated: 2026-04-25
63
+ - Last completed step: 10
64
+ - Next step: 7 (optional additional fixture coverage for deeper nested/manual-route scenarios)
65
65
  - Owner: Codex (with repository maintainers)
66
66
 
67
67
  ## 7. Decisions Log
@@ -72,15 +72,13 @@ When resuming later:
72
72
  - 2026-02-26: Flatten docs structure by moving `docs/ai/*` into `docs/*` and moving fixtures to `docs/fixtures/*`.
73
73
 
74
74
  ## 8. Open Items
75
- - [ ] Complete step 5: extend `STATE_INVARIANTS.md` with explicit ownership/cascade examples and coordinate edge cases.
76
- - [ ] Complete step 6: tighten host adapter mapping details for non-Konva renderer integrations.
77
75
  - [ ] Complete step 7: add at least one additional fixture covering manual link reroute + nested parent movement.
78
- - [ ] Complete step 9: run `npm pack --dry-run` after final doc pass and confirm package artifact list.
79
- - [ ] Complete step 10: run full terminology/link/version QA sweep across all docs.
80
- - [ ] Complete step 11: record final completion notes in changelog/task log.
76
+ - [ ] Complete step 11: record final completion notes in the external release changelog/task log used by maintainers.
81
77
 
82
78
  ## 9. Change Log
83
79
  - 2026-02-26: Completed workflow steps 1-4 for this iteration (audit, scope freeze, entry docs update, API contract expansion).
84
80
  - 2026-02-26: Updated `README.md`, `docs/INDEX.md`, `docs/API_CONTRACT.md`, and `ai/api-contract.json`.
85
81
  - 2026-02-26: Reorganized docs so core pages are under `docs/*` and fixtures are under `docs/fixtures/*`.
86
82
  - 2026-02-26: Added cross-page navigation and section-level links across core docs for human and AI readability.
83
+ - 2026-04-25: Completed release-doc pass for `v0.2.2` including `v0.2.1` scope consolidation; refreshed API/event contracts in `docs/*` and `ai/*`.
84
+ - 2026-04-25: Ran release validations: `npm run typecheck`, `npm test -- --watchAll=false`, `npm run build`, `npm run rollup-build-lib`, and `npm pack --dry-run`.
@@ -11,8 +11,10 @@ Embed this library into another diagram host while preserving deterministic stat
11
11
  - `change` for persistence/sync
12
12
  - `selection` for host inspector panels
13
13
  - link/pointer events for UI workflows
14
+ - deletion/text lifecycle events when host needs side-effects (`portDeleted`, `linkDeleted`, `textDeleted`, `textUpdated`)
14
15
  3. Convert external pointer drops with `clientToWorld`.
15
16
  4. Use `createOverlayShape` during host drag previews.
17
+ 5. For shape controls, pass `elementShapeHoverControls` and handle `onElementShapeHoverControlInteraction`.
16
18
 
17
19
  ## Path B: Engine-Only Adapter
18
20
  1. Implement `Renderer`:
@@ -33,6 +33,7 @@
33
33
  - `paperClick`
34
34
  - `elementLinkStarted/Connecting/Ended`
35
35
  - `portMouseDown/portMouseUp`
36
+ - deletion and text lifecycle events (`portDeleted`, `linkDeleted`, `textDeleted`, `textUpdated`)
36
37
 
37
38
  ## 6. Verify Cascading Deletes
38
39
  - Deleting element should remove descendant ports, connected links, owned texts.
@@ -20,8 +20,11 @@
20
20
  ## Port
21
21
  - Port position is local to owner element.
22
22
  - Default `anchorCenter=true` in model constructor.
23
+ - Default `orientToHostBorder=true` in model constructor.
23
24
  - Moving ports can be constrained by:
24
25
  - `moveMode: inside|border`
26
+ - element-level `portMovement.moveMode: free|inside|border|anchors`
27
+ - anchor presets via `portMovement.anchorConstraint`
25
28
  - `style.moveAxis`
26
29
  - `style.moveBounds`
27
30
  - Port removal cascades to connected links and owned texts.
@@ -37,6 +40,7 @@
37
40
  - Owned text position is stored owner-relative.
38
41
  - Text with missing owner behaves as standalone world-position text.
39
42
  - Size is measured on add/update when using engine text measurer.
43
+ - Text layout can compute derived `displayContent`, `displayOffset`, and `displayClipSize`.
40
44
 
41
45
  ## Selection And View
42
46
  - `setSelection` emits:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orcasvn-react-diagrams",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "dependencies": {
5
5
  "eventemitter3": "^5.0.1",
6
6
  "flatten-js": "^0.6.9",
@@ -10,7 +10,7 @@
10
10
  "web-vitals": "^2.1.4"
11
11
  },
12
12
  "engines": {
13
- "node": ">=18.17.0 <19.0.0"
13
+ "node": ">=20.17.0 <21.0.0"
14
14
  },
15
15
  "scripts": {
16
16
  "start": "react-scripts start",
@@ -47,19 +47,27 @@
47
47
  "import": "./dist/esm/index.js",
48
48
  "require": "./dist/cjs/index.js"
49
49
  },
50
+ "./examples": {
51
+ "types": "./dist/examples.d.ts",
52
+ "import": "./dist/esm/examples.js",
53
+ "require": "./dist/cjs/examples.js"
54
+ },
55
+ "./examples-source": "./src/displaybox/demos/index.tsx",
56
+ "./examples-source/*": "./src/displaybox/demos/*",
50
57
  "./ai-manifest": "./ai/manifest.json",
51
58
  "./package.json": "./package.json"
52
59
  },
53
- "files": [
54
- "dist",
55
- "README.md",
56
- "docs",
57
- "ai"
58
- ],
60
+ "files": [
61
+ "dist",
62
+ "src/examples",
63
+ "src/displaybox/demos",
64
+ "src/displaybox/types.ts",
65
+ "README.md",
66
+ "docs",
67
+ "ai"
68
+ ],
59
69
  "types": "dist/index.d.ts",
60
70
  "devDependencies": {
61
- "@types/jest": "^27.5.2",
62
- "@types/node": "^16.18.101",
63
71
  "@rollup/plugin-commonjs": "^26.0.1",
64
72
  "@rollup/plugin-node-resolve": "^15.2.3",
65
73
  "@rollup/plugin-terser": "^0.4.4",
@@ -67,6 +75,8 @@
67
75
  "@testing-library/jest-dom": "^5.17.0",
68
76
  "@testing-library/react": "^13.4.0",
69
77
  "@testing-library/user-event": "^13.5.0",
78
+ "@types/jest": "^27.5.2",
79
+ "@types/node": "^16.18.101",
70
80
  "@types/react": "^18.3.3",
71
81
  "@types/react-dom": "^18.3.0",
72
82
  "@types/uuid": "^10.0.0",