@dr2rai/raid-canvas 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RaiBridge.d.ts.map +1 -1
- package/dist/RaiBridge.js +43 -1
- package/dist/RaiBridge.js.map +1 -1
- package/dist/RaidCanvas.d.ts +8 -2
- package/dist/RaidCanvas.d.ts.map +1 -1
- package/dist/RaidCanvas.js +134 -21
- package/dist/RaidCanvas.js.map +1 -1
- package/dist/X6Shapes.d.ts +8 -1
- package/dist/X6Shapes.d.ts.map +1 -1
- package/dist/X6Shapes.js +50 -0
- package/dist/X6Shapes.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/RaiBridge.ts +46 -2
- package/src/RaidCanvas.tsx +145 -21
- package/src/X6Shapes.ts +53 -0
- package/src/index.ts +2 -0
- package/src/types.ts +13 -1
package/src/RaidCanvas.tsx
CHANGED
|
@@ -16,12 +16,13 @@ import React, {
|
|
|
16
16
|
useCallback,
|
|
17
17
|
useImperativeHandle,
|
|
18
18
|
} from 'react';
|
|
19
|
-
import { Graph, Shape } from '@antv/x6';
|
|
19
|
+
import { Graph, Shape, Edge } from '@antv/x6';
|
|
20
20
|
import { RaiBridge } from './RaiBridge.js';
|
|
21
21
|
import {
|
|
22
22
|
registerAimShapes,
|
|
23
23
|
configureAimGraph,
|
|
24
24
|
createAimNode,
|
|
25
|
+
applyEdgeRouting,
|
|
25
26
|
CascaisPalette,
|
|
26
27
|
getDefaultNodeBounds,
|
|
27
28
|
getDefaultNodeName,
|
|
@@ -31,7 +32,7 @@ import {
|
|
|
31
32
|
getSemanticEdgeKind,
|
|
32
33
|
getSemanticEdgeStereotype,
|
|
33
34
|
} from './semanticRules.js';
|
|
34
|
-
import type { AimOntologyKind, RaidNodeData, RaidEdgeData } from './types.js';
|
|
35
|
+
import type { AimOntologyKind, AimRoutingMode, RaidNodeData, RaidEdgeData } from './types.js';
|
|
35
36
|
|
|
36
37
|
export interface RaidCanvasProps {
|
|
37
38
|
/** The raw SVG string carrying aim-* ontological attributes (preferred) */
|
|
@@ -40,6 +41,8 @@ export interface RaidCanvasProps {
|
|
|
40
41
|
svgContent?: string;
|
|
41
42
|
/** Whether the canvas allows dragging and editing, or behaves as a pan/zoom viewer */
|
|
42
43
|
readOnly?: boolean;
|
|
44
|
+
/** Default routing mode for edges ('manhattan', 'normal', 'smooth'). Default: 'manhattan' */
|
|
45
|
+
defaultRouting?: AimRoutingMode;
|
|
43
46
|
/** Optional CSS class name for the outer wrapper */
|
|
44
47
|
className?: string;
|
|
45
48
|
/** Optional inline styles for outer wrapper */
|
|
@@ -72,8 +75,12 @@ export interface RaidCanvasHandle {
|
|
|
72
75
|
) => string;
|
|
73
76
|
/** Update properties of an existing node (label, stereotype, dimensions, etc.) */
|
|
74
77
|
updateNode: (id: string, updates: Partial<RaidNodeData>) => void;
|
|
75
|
-
/** Update properties of an existing edge (kind, label, stereotype) */
|
|
78
|
+
/** Update properties of an existing edge (kind, label, stereotype, routing, ports) */
|
|
76
79
|
updateEdge: (id: string, updates: Partial<RaidEdgeData>) => void;
|
|
80
|
+
/** Set routing mode across the canvas or for new edges */
|
|
81
|
+
setRoutingMode: (mode: AimRoutingMode, applyToAllEdges?: boolean) => void;
|
|
82
|
+
/** Get the current active canvas routing mode */
|
|
83
|
+
getRoutingMode: () => AimRoutingMode;
|
|
77
84
|
/** Delete currently selected cell(s) */
|
|
78
85
|
deleteSelection: () => void;
|
|
79
86
|
/** Clear all cells on the canvas */
|
|
@@ -100,6 +107,7 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
100
107
|
svg,
|
|
101
108
|
svgContent,
|
|
102
109
|
readOnly = false,
|
|
110
|
+
defaultRouting = 'manhattan',
|
|
103
111
|
className,
|
|
104
112
|
style,
|
|
105
113
|
onChange,
|
|
@@ -118,6 +126,9 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
118
126
|
const isHydratingRef = useRef<boolean>(false);
|
|
119
127
|
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
120
128
|
const selectedCellIdRef = useRef<string | null>(null);
|
|
129
|
+
const clearEdgeToolsRef = useRef<(() => void) | null>(null);
|
|
130
|
+
const routingModeRef = useRef<AimRoutingMode>(defaultRouting);
|
|
131
|
+
routingModeRef.current = defaultRouting;
|
|
121
132
|
|
|
122
133
|
const [zoomLevel, setZoomLevel] = useState<number>(100);
|
|
123
134
|
const [isDragOver, setIsDragOver] = useState<boolean>(false);
|
|
@@ -302,6 +313,24 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
302
313
|
const nextData = { ...currentData, ...updates };
|
|
303
314
|
edge.setData(nextData);
|
|
304
315
|
|
|
316
|
+
if (updates.routing !== undefined) {
|
|
317
|
+
applyEdgeRouting(edge, updates.routing);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (updates.sourcePort !== undefined) {
|
|
321
|
+
const currentSource = edge.getSource() as { cell?: string };
|
|
322
|
+
if (currentSource.cell) {
|
|
323
|
+
edge.setSource({ cell: currentSource.cell, port: updates.sourcePort });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (updates.targetPort !== undefined) {
|
|
328
|
+
const currentTarget = edge.getTarget() as { cell?: string };
|
|
329
|
+
if (currentTarget.cell) {
|
|
330
|
+
edge.setTarget({ cell: currentTarget.cell, port: updates.targetPort });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
305
334
|
if (updates.label !== undefined || updates.stereotype !== undefined) {
|
|
306
335
|
const text = updates.label ?? updates.stereotype ?? '';
|
|
307
336
|
edge.setLabels(
|
|
@@ -324,12 +353,25 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
324
353
|
|
|
325
354
|
triggerDebouncedChange();
|
|
326
355
|
},
|
|
356
|
+
setRoutingMode: (mode, applyToAll = true) => {
|
|
357
|
+
routingModeRef.current = mode;
|
|
358
|
+
if (applyToAll && graphRef.current) {
|
|
359
|
+
for (const edge of graphRef.current.getEdges()) {
|
|
360
|
+
applyEdgeRouting(edge, mode);
|
|
361
|
+
const currentData = (edge.getData() ?? {}) as RaidEdgeData;
|
|
362
|
+
edge.setData({ ...currentData, routing: mode });
|
|
363
|
+
}
|
|
364
|
+
triggerDebouncedChange();
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
getRoutingMode: () => routingModeRef.current,
|
|
327
368
|
deleteSelection: () => {
|
|
328
369
|
const graph = graphRef.current;
|
|
329
370
|
if (!graph || readOnly) return;
|
|
330
371
|
if (selectedCellIdRef.current) {
|
|
331
372
|
const cell = graph.getCellById(selectedCellIdRef.current);
|
|
332
373
|
if (cell) {
|
|
374
|
+
clearEdgeToolsRef.current?.();
|
|
333
375
|
graph.removeCell(cell);
|
|
334
376
|
selectedCellIdRef.current = null;
|
|
335
377
|
onSelectRef.current?.(null);
|
|
@@ -455,9 +497,11 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
455
497
|
return validateSemanticConnection(sourceKind, targetKind);
|
|
456
498
|
},
|
|
457
499
|
createEdge() {
|
|
458
|
-
|
|
500
|
+
const edge = new Shape.Edge({
|
|
459
501
|
shape: 'aim-edge',
|
|
460
502
|
});
|
|
503
|
+
applyEdgeRouting(edge, routingModeRef.current);
|
|
504
|
+
return edge;
|
|
461
505
|
},
|
|
462
506
|
},
|
|
463
507
|
});
|
|
@@ -465,9 +509,66 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
465
509
|
configureAimGraph(graph);
|
|
466
510
|
graphRef.current = graph;
|
|
467
511
|
|
|
468
|
-
|
|
512
|
+
let activeToolEdge: Edge | null = null;
|
|
513
|
+
|
|
514
|
+
const clearEdgeTools = () => {
|
|
515
|
+
if (activeToolEdge) {
|
|
516
|
+
try {
|
|
517
|
+
activeToolEdge.removeTools();
|
|
518
|
+
} catch {
|
|
519
|
+
// Ignore if cell was already removed
|
|
520
|
+
}
|
|
521
|
+
activeToolEdge = null;
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
clearEdgeToolsRef.current = clearEdgeTools;
|
|
525
|
+
|
|
526
|
+
const setEdgeTools = (edge: Edge) => {
|
|
527
|
+
clearEdgeTools();
|
|
528
|
+
if (readOnly) return;
|
|
529
|
+
activeToolEdge = edge;
|
|
530
|
+
try {
|
|
531
|
+
edge.addTools([
|
|
532
|
+
{
|
|
533
|
+
name: 'source-arrowhead',
|
|
534
|
+
args: {
|
|
535
|
+
attrs: {
|
|
536
|
+
fill: CascaisPalette.NetGold,
|
|
537
|
+
stroke: '#FFFFFF',
|
|
538
|
+
strokeWidth: 2,
|
|
539
|
+
cursor: 'grab',
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: 'target-arrowhead',
|
|
545
|
+
args: {
|
|
546
|
+
attrs: {
|
|
547
|
+
fill: CascaisPalette.NetGold,
|
|
548
|
+
stroke: '#FFFFFF',
|
|
549
|
+
strokeWidth: 2,
|
|
550
|
+
cursor: 'grab',
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: 'vertices',
|
|
556
|
+
args: {
|
|
557
|
+
attrs: {
|
|
558
|
+
fill: CascaisPalette.WarmGraphite,
|
|
559
|
+
stroke: '#FFFFFF',
|
|
560
|
+
strokeWidth: 1.5,
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
]);
|
|
565
|
+
} catch {
|
|
566
|
+
// Fallback if tools fail to attach
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// Handle edge connections and re-connections with automatic semantic wiring
|
|
469
571
|
graph.on('edge:connected', ({ edge, isNew }) => {
|
|
470
|
-
if (!isNew) return;
|
|
471
572
|
const sourceCell = edge.getSourceCell();
|
|
472
573
|
const targetCell = edge.getTargetCell();
|
|
473
574
|
if (sourceCell?.isNode() && targetCell?.isNode()) {
|
|
@@ -479,33 +580,44 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
479
580
|
) as AimOntologyKind;
|
|
480
581
|
|
|
481
582
|
const edgeKind = getSemanticEdgeKind(sourceKind, targetKind);
|
|
482
|
-
const
|
|
583
|
+
const currentData = (edge.getData() ?? {}) as Partial<RaidEdgeData>;
|
|
584
|
+
const stereotype = currentData.stereotype ?? getSemanticEdgeStereotype(sourceKind, targetKind);
|
|
483
585
|
|
|
484
586
|
edge.setData({
|
|
587
|
+
...currentData,
|
|
485
588
|
id: edge.id,
|
|
486
589
|
kind: edgeKind,
|
|
487
590
|
sourceId: sourceCell.id,
|
|
488
591
|
targetId: targetCell.id,
|
|
489
592
|
sourcePort: edge.getSourcePortId(),
|
|
490
593
|
targetPort: edge.getTargetPortId(),
|
|
491
|
-
label: stereotype,
|
|
594
|
+
label: currentData.label ?? stereotype,
|
|
492
595
|
stereotype,
|
|
493
|
-
|
|
596
|
+
routing: currentData.routing ?? routingModeRef.current,
|
|
597
|
+
bendPoints: currentData.bendPoints ?? [],
|
|
494
598
|
});
|
|
495
599
|
|
|
496
|
-
if (
|
|
497
|
-
edge.
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
600
|
+
if (isNew) {
|
|
601
|
+
applyEdgeRouting(edge, routingModeRef.current);
|
|
602
|
+
if (stereotype) {
|
|
603
|
+
edge.setLabels([
|
|
604
|
+
{
|
|
605
|
+
attrs: {
|
|
606
|
+
text: {
|
|
607
|
+
text: stereotype,
|
|
608
|
+
fill: CascaisPalette.TextSecondary,
|
|
609
|
+
fontSize: 11,
|
|
610
|
+
},
|
|
504
611
|
},
|
|
612
|
+
position: 0.5,
|
|
505
613
|
},
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
614
|
+
]);
|
|
615
|
+
}
|
|
616
|
+
} else {
|
|
617
|
+
// Re-anchored: if edge was selected, re-attach tools
|
|
618
|
+
if (selectedCellIdRef.current === edge.id) {
|
|
619
|
+
setEdgeTools(edge);
|
|
620
|
+
}
|
|
509
621
|
}
|
|
510
622
|
}
|
|
511
623
|
triggerDebouncedChange();
|
|
@@ -515,8 +627,15 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
515
627
|
graph.on('node:change:position', triggerDebouncedChange);
|
|
516
628
|
graph.on('node:change:size', triggerDebouncedChange);
|
|
517
629
|
graph.on('edge:change:vertices', triggerDebouncedChange);
|
|
630
|
+
graph.on('edge:change:source', triggerDebouncedChange);
|
|
631
|
+
graph.on('edge:change:target', triggerDebouncedChange);
|
|
518
632
|
graph.on('cell:added', triggerDebouncedChange);
|
|
519
|
-
graph.on('cell:removed',
|
|
633
|
+
graph.on('cell:removed', ({ cell }) => {
|
|
634
|
+
if (cell === activeToolEdge) {
|
|
635
|
+
activeToolEdge = null;
|
|
636
|
+
}
|
|
637
|
+
triggerDebouncedChange();
|
|
638
|
+
});
|
|
520
639
|
|
|
521
640
|
// Zoom listener to keep toolbar indicator accurate
|
|
522
641
|
graph.on('scale', () => {
|
|
@@ -529,6 +648,7 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
529
648
|
selectedCellIdRef.current = id;
|
|
530
649
|
|
|
531
650
|
if (cell.isNode()) {
|
|
651
|
+
clearEdgeTools();
|
|
532
652
|
const data = (cell.getData() ?? {}) as Partial<RaidNodeData>;
|
|
533
653
|
const kind = (data.kind ?? 'act') as AimOntologyKind;
|
|
534
654
|
const label =
|
|
@@ -538,6 +658,7 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
538
658
|
id;
|
|
539
659
|
onSelectRef.current?.({ id, kind, label });
|
|
540
660
|
} else if (cell.isEdge()) {
|
|
661
|
+
setEdgeTools(cell);
|
|
541
662
|
const data = (cell.getData() ?? {}) as Partial<RaidEdgeData>;
|
|
542
663
|
const label =
|
|
543
664
|
data.label ??
|
|
@@ -550,6 +671,7 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
550
671
|
});
|
|
551
672
|
|
|
552
673
|
graph.on('blank:click', () => {
|
|
674
|
+
clearEdgeTools();
|
|
553
675
|
selectedCellIdRef.current = null;
|
|
554
676
|
onSelectRef.current?.(null);
|
|
555
677
|
onSelectionChangeRef.current?.([]);
|
|
@@ -572,6 +694,7 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
572
694
|
const cell = graphRef.current.getCellById(selectedCellIdRef.current);
|
|
573
695
|
if (cell) {
|
|
574
696
|
e.preventDefault();
|
|
697
|
+
clearEdgeTools();
|
|
575
698
|
graphRef.current.removeCell(cell);
|
|
576
699
|
selectedCellIdRef.current = null;
|
|
577
700
|
onSelectRef.current?.(null);
|
|
@@ -617,6 +740,7 @@ export const RaidCanvas = React.forwardRef<RaidCanvasHandle, RaidCanvasProps>(
|
|
|
617
740
|
}
|
|
618
741
|
window.removeEventListener('keydown', handleKeyDown);
|
|
619
742
|
resizeObserver.disconnect();
|
|
743
|
+
clearEdgeToolsRef.current = null;
|
|
620
744
|
graph.dispose();
|
|
621
745
|
graphRef.current = null;
|
|
622
746
|
};
|
package/src/X6Shapes.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { Graph, Shape, Node, Edge } from '@antv/x6';
|
|
|
13
13
|
import type {
|
|
14
14
|
AimOntologyKind,
|
|
15
15
|
AimEdgeKind,
|
|
16
|
+
AimRoutingMode,
|
|
16
17
|
RaidNodeData,
|
|
17
18
|
RaidEdgeData,
|
|
18
19
|
OrthogonalPortId,
|
|
@@ -457,6 +458,34 @@ export function createAimNode(data: RaidNodeData): Node.Metadata {
|
|
|
457
458
|
}
|
|
458
459
|
}
|
|
459
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Configures the router and connector for an X6 Edge based on AimRoutingMode.
|
|
463
|
+
* - 'manhattan': Obstacle-avoiding 90° orthogonal router with rounded corners (radius: 8).
|
|
464
|
+
* - 'normal': Direct straight line point-to-point connection.
|
|
465
|
+
* - 'smooth': Curved cubic bezier spline between ports.
|
|
466
|
+
*/
|
|
467
|
+
export function applyEdgeRouting(edge: Edge, routing: AimRoutingMode = 'manhattan'): void {
|
|
468
|
+
switch (routing) {
|
|
469
|
+
case 'normal':
|
|
470
|
+
edge.setRouter('normal');
|
|
471
|
+
edge.setConnector('normal');
|
|
472
|
+
break;
|
|
473
|
+
case 'smooth':
|
|
474
|
+
edge.setRouter('normal');
|
|
475
|
+
edge.setConnector('smooth');
|
|
476
|
+
break;
|
|
477
|
+
case 'manhattan':
|
|
478
|
+
default:
|
|
479
|
+
edge.setRouter('manhattan', {
|
|
480
|
+
padding: 20,
|
|
481
|
+
startDirections: ['top', 'right', 'bottom', 'left'],
|
|
482
|
+
endDirections: ['top', 'right', 'bottom', 'left'],
|
|
483
|
+
});
|
|
484
|
+
edge.setConnector('rounded', { radius: 8 });
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
460
489
|
/**
|
|
461
490
|
* Factory creating an AntV X6 Edge model from a RaidEdgeData specification.
|
|
462
491
|
*/
|
|
@@ -464,10 +493,34 @@ export function createAimEdge(data: RaidEdgeData): Edge.Metadata {
|
|
|
464
493
|
registerAimShapes();
|
|
465
494
|
|
|
466
495
|
const edgeAttrs = getEdgeStyling(data.kind);
|
|
496
|
+
const routing = data.routing ?? 'manhattan';
|
|
497
|
+
|
|
498
|
+
let routerConfig: Edge.Metadata['router'] = {
|
|
499
|
+
name: 'manhattan',
|
|
500
|
+
args: {
|
|
501
|
+
padding: 20,
|
|
502
|
+
startDirections: ['top', 'right', 'bottom', 'left'],
|
|
503
|
+
endDirections: ['top', 'right', 'bottom', 'left'],
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
let connectorConfig: Edge.Metadata['connector'] = {
|
|
507
|
+
name: 'rounded',
|
|
508
|
+
args: { radius: 8 },
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
if (routing === 'normal') {
|
|
512
|
+
routerConfig = { name: 'normal' };
|
|
513
|
+
connectorConfig = { name: 'normal' };
|
|
514
|
+
} else if (routing === 'smooth') {
|
|
515
|
+
routerConfig = { name: 'normal' };
|
|
516
|
+
connectorConfig = { name: 'smooth' };
|
|
517
|
+
}
|
|
467
518
|
|
|
468
519
|
return {
|
|
469
520
|
id: data.id,
|
|
470
521
|
shape: 'aim-edge',
|
|
522
|
+
router: routerConfig,
|
|
523
|
+
connector: connectorConfig,
|
|
471
524
|
source: {
|
|
472
525
|
cell: data.sourceId,
|
|
473
526
|
...(data.sourcePort !== undefined ? { port: data.sourcePort } : {}),
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
export {
|
|
16
16
|
AimSvgContract,
|
|
17
17
|
type AimOntologyKind,
|
|
18
|
+
type AimRoutingMode,
|
|
18
19
|
type AimEdgeKind,
|
|
19
20
|
type Point,
|
|
20
21
|
type SvgBendPoint,
|
|
@@ -35,6 +36,7 @@ export {
|
|
|
35
36
|
configureAimGraph,
|
|
36
37
|
createAimNode,
|
|
37
38
|
createAimEdge,
|
|
39
|
+
applyEdgeRouting,
|
|
38
40
|
getDefaultNodeBounds,
|
|
39
41
|
getDefaultNodeName,
|
|
40
42
|
} from './X6Shapes.js';
|
package/src/types.ts
CHANGED
|
@@ -16,7 +16,15 @@
|
|
|
16
16
|
* - 'obj' : Object / Instance (runtime instance card with underlined title)
|
|
17
17
|
* - 'per' : Person / Actor (Initiating or Defined role stick-figure/card)
|
|
18
18
|
*/
|
|
19
|
-
export type AimOntologyKind = '
|
|
19
|
+
export type AimOntologyKind = 'act' | 'uc' | 'cls' | 'obj' | 'per';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Routing strategy for diagram edges.
|
|
23
|
+
* - 'manhattan': Obstacle-avoiding 90° orthogonal routing with rounded corners (default).
|
|
24
|
+
* - 'normal': Direct straight line point-to-point connection.
|
|
25
|
+
* - 'smooth': Curved cubic bezier spline between ports.
|
|
26
|
+
*/
|
|
27
|
+
export type AimRoutingMode = 'manhattan' | 'normal' | 'smooth';
|
|
20
28
|
|
|
21
29
|
/**
|
|
22
30
|
* Ontological relationship classifications in AOAIM.
|
|
@@ -126,6 +134,9 @@ export interface RaidEdgeData {
|
|
|
126
134
|
/** Multiplicity / Cardinality at the target end (e.g., '0..1', '*'). */
|
|
127
135
|
readonly targetCardinality?: string;
|
|
128
136
|
|
|
137
|
+
/** Routing strategy for this edge ('manhattan', 'normal', 'smooth'). */
|
|
138
|
+
readonly routing?: AimRoutingMode;
|
|
139
|
+
|
|
129
140
|
/** User-editable or router-computed Manhattan bend points. */
|
|
130
141
|
readonly bendPoints: readonly SvgBendPoint[];
|
|
131
142
|
}
|
|
@@ -172,6 +183,7 @@ export const AimSvgContract = {
|
|
|
172
183
|
ATTR_TARGET: 'aim-target',
|
|
173
184
|
ATTR_SOURCE_PORT: 'aim-source-port',
|
|
174
185
|
ATTR_TARGET_PORT: 'aim-target-port',
|
|
186
|
+
ATTR_ROUTING: 'aim-routing',
|
|
175
187
|
ATTR_BENDS: 'aim-bends',
|
|
176
188
|
|
|
177
189
|
// Selectors for DOM queries
|