@dr2rai/raid-canvas 0.2.0 → 0.3.1
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 +10 -3
- package/dist/RaiBridge.d.ts.map +1 -1
- package/dist/RaiBridge.js +288 -16
- package/dist/RaiBridge.js.map +1 -1
- package/dist/RaidCanvas.d.ts +10 -2
- package/dist/RaidCanvas.d.ts.map +1 -1
- package/dist/RaidCanvas.js +169 -27
- package/dist/RaidCanvas.js.map +1 -1
- package/dist/X6Shapes.d.ts +15 -1
- package/dist/X6Shapes.d.ts.map +1 -1
- package/dist/X6Shapes.js +196 -22
- 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 +23 -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 +332 -19
- package/src/RaidCanvas.tsx +183 -25
- package/src/X6Shapes.ts +204 -22
- package/src/index.ts +3 -0
- package/src/styles/aoaim-theme.css +21 -7
- package/src/types.ts +29 -1
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,
|
|
@@ -36,6 +37,74 @@ export const CascaisPalette = {
|
|
|
36
37
|
TextSecondary: '#4B5563',
|
|
37
38
|
} as const;
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Formats and wraps node label text for AOAIM entities.
|
|
42
|
+
* Automatically wraps on whitespace when exceeding target length,
|
|
43
|
+
* and treats `<wbr>` / `<wbr/>` tags and hyphens as soft word-break opportunities
|
|
44
|
+
* within long unbroken words or strings.
|
|
45
|
+
*/
|
|
46
|
+
export function wrapAimText(rawText: string, maxLineLength: number = 18): string {
|
|
47
|
+
if (!rawText) return '';
|
|
48
|
+
|
|
49
|
+
const lines = rawText.split('\n');
|
|
50
|
+
const resultLines: string[] = [];
|
|
51
|
+
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
if (!line) {
|
|
54
|
+
resultLines.push('');
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Replace <wbr> / <wbr/> with zero-width break marker \u200B,
|
|
59
|
+
// and allow breaking after hyphens within words
|
|
60
|
+
const normalized = line
|
|
61
|
+
.replace(/<wbr\s*\/?>/gi, '\u200B')
|
|
62
|
+
.replace(/-(?=[a-zA-Z0-9])/g, '-\u200B');
|
|
63
|
+
|
|
64
|
+
const spaceWords = normalized.split(/\s+/).filter(Boolean);
|
|
65
|
+
if (spaceWords.length === 0) continue;
|
|
66
|
+
|
|
67
|
+
let currentLine = '';
|
|
68
|
+
|
|
69
|
+
for (let wordIdx = 0; wordIdx < spaceWords.length; wordIdx++) {
|
|
70
|
+
const spaceWord = spaceWords[wordIdx]!;
|
|
71
|
+
const chunks = spaceWord.split('\u200B').filter(Boolean);
|
|
72
|
+
|
|
73
|
+
for (let chunkIdx = 0; chunkIdx < chunks.length; chunkIdx++) {
|
|
74
|
+
const chunk = chunks[chunkIdx]!;
|
|
75
|
+
const isFirstChunkOfWord = chunkIdx === 0;
|
|
76
|
+
|
|
77
|
+
if (!currentLine) {
|
|
78
|
+
currentLine = chunk;
|
|
79
|
+
} else if (isFirstChunkOfWord) {
|
|
80
|
+
// Break or space before a new whitespace-separated word
|
|
81
|
+
if (currentLine.length + 1 + chunk.length <= maxLineLength) {
|
|
82
|
+
currentLine += ' ' + chunk;
|
|
83
|
+
} else {
|
|
84
|
+
resultLines.push(currentLine);
|
|
85
|
+
currentLine = chunk;
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
// Soft-break opportunity within a word (<wbr> or hyphen):
|
|
89
|
+
// Glues together without space if it fits; breaks without space if it overflows
|
|
90
|
+
if (currentLine.length + chunk.length <= maxLineLength) {
|
|
91
|
+
currentLine += chunk;
|
|
92
|
+
} else {
|
|
93
|
+
resultLines.push(currentLine);
|
|
94
|
+
currentLine = chunk;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (currentLine) {
|
|
101
|
+
resultLines.push(currentLine);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return resultLines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
|
|
39
108
|
/**
|
|
40
109
|
* Port configuration generating 4 orthogonal snap anchors.
|
|
41
110
|
*/
|
|
@@ -132,7 +201,7 @@ export function registerAimShapes(): void {
|
|
|
132
201
|
ports: createOrthogonalPorts(),
|
|
133
202
|
});
|
|
134
203
|
|
|
135
|
-
// 2. AimActivityNode ('act') — Rounded rectangle with Heraldic Green border
|
|
204
|
+
// 2. AimActivityNode ('act') — Rounded rectangle with Heraldic Green border & underlined label
|
|
136
205
|
Shape.Rect.define({
|
|
137
206
|
shape: 'aim-act',
|
|
138
207
|
overwrite: true,
|
|
@@ -155,6 +224,11 @@ export function registerAimShapes(): void {
|
|
|
155
224
|
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
|
|
156
225
|
textAnchor: 'middle',
|
|
157
226
|
textVerticalAnchor: 'middle',
|
|
227
|
+
textDecoration: 'underline',
|
|
228
|
+
textWrap: {
|
|
229
|
+
width: -16,
|
|
230
|
+
breakWord: true,
|
|
231
|
+
},
|
|
158
232
|
},
|
|
159
233
|
},
|
|
160
234
|
ports: createOrthogonalPorts(),
|
|
@@ -277,34 +351,77 @@ export function registerAimShapes(): void {
|
|
|
277
351
|
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
|
|
278
352
|
textAnchor: 'middle',
|
|
279
353
|
textVerticalAnchor: 'middle',
|
|
354
|
+
textDecoration: 'underline',
|
|
355
|
+
textWrap: {
|
|
356
|
+
width: -16,
|
|
357
|
+
breakWord: true,
|
|
358
|
+
},
|
|
280
359
|
},
|
|
281
360
|
},
|
|
282
361
|
ports: createOrthogonalPorts(),
|
|
283
362
|
});
|
|
284
363
|
|
|
285
|
-
// 5. AimPersonNode ('per') — Person / Actor
|
|
364
|
+
// 5. AimPersonNode ('per') — Person / Actor glyph
|
|
286
365
|
Shape.Rect.define({
|
|
287
366
|
shape: 'aim-per',
|
|
288
367
|
overwrite: true,
|
|
289
|
-
width:
|
|
290
|
-
height:
|
|
368
|
+
width: 90,
|
|
369
|
+
height: 90,
|
|
370
|
+
markup: [
|
|
371
|
+
{
|
|
372
|
+
tagName: 'rect',
|
|
373
|
+
selector: 'body',
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
tagName: 'path',
|
|
377
|
+
selector: 'torso',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
tagName: 'circle',
|
|
381
|
+
selector: 'head',
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
tagName: 'text',
|
|
385
|
+
selector: 'label',
|
|
386
|
+
},
|
|
387
|
+
],
|
|
291
388
|
attrs: {
|
|
292
389
|
body: {
|
|
293
|
-
fill:
|
|
294
|
-
stroke:
|
|
295
|
-
strokeWidth:
|
|
296
|
-
rx: 6,
|
|
297
|
-
ry: 6,
|
|
390
|
+
fill: 'transparent',
|
|
391
|
+
stroke: 'transparent',
|
|
392
|
+
strokeWidth: 0,
|
|
298
393
|
class: 'aim-node aim-per',
|
|
299
394
|
},
|
|
395
|
+
torso: {
|
|
396
|
+
d: 'M 61 50 v -4 a 8 8 0 0 0 -8 -8 H 37 a 8 8 0 0 0 -8 8 v 4',
|
|
397
|
+
fill: 'none',
|
|
398
|
+
stroke: CascaisPalette.WarmGraphite,
|
|
399
|
+
strokeWidth: 2,
|
|
400
|
+
strokeLinecap: 'round',
|
|
401
|
+
strokeLinejoin: 'round',
|
|
402
|
+
},
|
|
403
|
+
head: {
|
|
404
|
+
cx: 45,
|
|
405
|
+
cy: 22,
|
|
406
|
+
r: 8,
|
|
407
|
+
fill: CascaisPalette.ChalkWhite,
|
|
408
|
+
stroke: CascaisPalette.WarmGraphite,
|
|
409
|
+
strokeWidth: 2,
|
|
410
|
+
},
|
|
300
411
|
label: {
|
|
301
|
-
text: '
|
|
412
|
+
text: 'Actor',
|
|
302
413
|
fill: CascaisPalette.TextPrimary,
|
|
303
414
|
fontSize: 12,
|
|
304
415
|
fontWeight: '500',
|
|
305
416
|
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
|
|
306
417
|
textAnchor: 'middle',
|
|
307
|
-
textVerticalAnchor: '
|
|
418
|
+
textVerticalAnchor: 'top',
|
|
419
|
+
refX: 0.5,
|
|
420
|
+
refY: 62,
|
|
421
|
+
textWrap: {
|
|
422
|
+
width: -10,
|
|
423
|
+
breakWord: true,
|
|
424
|
+
},
|
|
308
425
|
},
|
|
309
426
|
},
|
|
310
427
|
ports: createOrthogonalPorts(),
|
|
@@ -390,25 +507,30 @@ export function createAimNode(data: RaidNodeData): Node.Metadata {
|
|
|
390
507
|
|
|
391
508
|
// Archetype-specific customization
|
|
392
509
|
switch (data.kind) {
|
|
393
|
-
case 'uc':
|
|
510
|
+
case 'uc': {
|
|
511
|
+
const wrappedName = wrapAimText(data.displayName);
|
|
394
512
|
return {
|
|
395
513
|
...baseMetadata,
|
|
396
514
|
attrs: {
|
|
397
515
|
label: {
|
|
398
|
-
text: data.stereotype ? `${data.stereotype}\n${
|
|
516
|
+
text: data.stereotype ? `${data.stereotype}\n${wrappedName}` : wrappedName,
|
|
399
517
|
},
|
|
400
518
|
},
|
|
401
519
|
};
|
|
520
|
+
}
|
|
402
521
|
|
|
403
|
-
case 'act':
|
|
522
|
+
case 'act': {
|
|
523
|
+
const wrappedName = wrapAimText(data.displayName);
|
|
404
524
|
return {
|
|
405
525
|
...baseMetadata,
|
|
406
526
|
attrs: {
|
|
407
527
|
label: {
|
|
408
|
-
text: data.stereotype ? `${data.stereotype}\n${
|
|
528
|
+
text: data.stereotype ? `${data.stereotype}\n${wrappedName}` : wrappedName,
|
|
529
|
+
textDecoration: 'underline',
|
|
409
530
|
},
|
|
410
531
|
},
|
|
411
532
|
};
|
|
533
|
+
}
|
|
412
534
|
|
|
413
535
|
case 'cls':
|
|
414
536
|
return {
|
|
@@ -426,27 +548,35 @@ export function createAimNode(data: RaidNodeData): Node.Metadata {
|
|
|
426
548
|
},
|
|
427
549
|
};
|
|
428
550
|
|
|
429
|
-
case 'obj':
|
|
551
|
+
case 'obj': {
|
|
552
|
+
const wrappedName = wrapAimText(data.displayName);
|
|
430
553
|
return {
|
|
431
554
|
...baseMetadata,
|
|
432
555
|
attrs: {
|
|
433
556
|
label: {
|
|
434
|
-
text: data.
|
|
557
|
+
text: data.stereotype ? `${data.stereotype}\n${wrappedName}` : wrappedName,
|
|
558
|
+
textDecoration: 'underline',
|
|
435
559
|
},
|
|
436
560
|
},
|
|
437
561
|
};
|
|
562
|
+
}
|
|
438
563
|
|
|
439
564
|
case 'per': {
|
|
440
565
|
const isInitiating = data.stereotype?.toLowerCase().includes('initiates') ?? false;
|
|
566
|
+
const strokeColor = isInitiating ? CascaisPalette.NetGold : CascaisPalette.WarmGraphite;
|
|
567
|
+
const wrappedName = wrapAimText(data.displayName, 14);
|
|
568
|
+
const text = data.stereotype ? `${data.stereotype}\n${wrappedName}` : wrappedName;
|
|
441
569
|
return {
|
|
442
570
|
...baseMetadata,
|
|
443
571
|
attrs: {
|
|
444
|
-
|
|
445
|
-
stroke:
|
|
446
|
-
|
|
572
|
+
torso: {
|
|
573
|
+
stroke: strokeColor,
|
|
574
|
+
},
|
|
575
|
+
head: {
|
|
576
|
+
stroke: strokeColor,
|
|
447
577
|
},
|
|
448
578
|
label: {
|
|
449
|
-
text
|
|
579
|
+
text,
|
|
450
580
|
},
|
|
451
581
|
},
|
|
452
582
|
};
|
|
@@ -457,6 +587,34 @@ export function createAimNode(data: RaidNodeData): Node.Metadata {
|
|
|
457
587
|
}
|
|
458
588
|
}
|
|
459
589
|
|
|
590
|
+
/**
|
|
591
|
+
* Configures the router and connector for an X6 Edge based on AimRoutingMode.
|
|
592
|
+
* - 'manhattan': Obstacle-avoiding 90° orthogonal router with rounded corners (radius: 8).
|
|
593
|
+
* - 'normal': Direct straight line point-to-point connection.
|
|
594
|
+
* - 'smooth': Curved cubic bezier spline between ports.
|
|
595
|
+
*/
|
|
596
|
+
export function applyEdgeRouting(edge: Edge, routing: AimRoutingMode = 'manhattan'): void {
|
|
597
|
+
switch (routing) {
|
|
598
|
+
case 'normal':
|
|
599
|
+
edge.setRouter('normal');
|
|
600
|
+
edge.setConnector('normal');
|
|
601
|
+
break;
|
|
602
|
+
case 'smooth':
|
|
603
|
+
edge.setRouter('normal');
|
|
604
|
+
edge.setConnector('smooth');
|
|
605
|
+
break;
|
|
606
|
+
case 'manhattan':
|
|
607
|
+
default:
|
|
608
|
+
edge.setRouter('manhattan', {
|
|
609
|
+
padding: 20,
|
|
610
|
+
startDirections: ['top', 'right', 'bottom', 'left'],
|
|
611
|
+
endDirections: ['top', 'right', 'bottom', 'left'],
|
|
612
|
+
});
|
|
613
|
+
edge.setConnector('rounded', { radius: 8 });
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
460
618
|
/**
|
|
461
619
|
* Factory creating an AntV X6 Edge model from a RaidEdgeData specification.
|
|
462
620
|
*/
|
|
@@ -464,10 +622,34 @@ export function createAimEdge(data: RaidEdgeData): Edge.Metadata {
|
|
|
464
622
|
registerAimShapes();
|
|
465
623
|
|
|
466
624
|
const edgeAttrs = getEdgeStyling(data.kind);
|
|
625
|
+
const routing = data.routing ?? 'manhattan';
|
|
626
|
+
|
|
627
|
+
let routerConfig: Edge.Metadata['router'] = {
|
|
628
|
+
name: 'manhattan',
|
|
629
|
+
args: {
|
|
630
|
+
padding: 20,
|
|
631
|
+
startDirections: ['top', 'right', 'bottom', 'left'],
|
|
632
|
+
endDirections: ['top', 'right', 'bottom', 'left'],
|
|
633
|
+
},
|
|
634
|
+
};
|
|
635
|
+
let connectorConfig: Edge.Metadata['connector'] = {
|
|
636
|
+
name: 'rounded',
|
|
637
|
+
args: { radius: 8 },
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
if (routing === 'normal') {
|
|
641
|
+
routerConfig = { name: 'normal' };
|
|
642
|
+
connectorConfig = { name: 'normal' };
|
|
643
|
+
} else if (routing === 'smooth') {
|
|
644
|
+
routerConfig = { name: 'normal' };
|
|
645
|
+
connectorConfig = { name: 'smooth' };
|
|
646
|
+
}
|
|
467
647
|
|
|
468
648
|
return {
|
|
469
649
|
id: data.id,
|
|
470
650
|
shape: 'aim-edge',
|
|
651
|
+
router: routerConfig,
|
|
652
|
+
connector: connectorConfig,
|
|
471
653
|
source: {
|
|
472
654
|
cell: data.sourceId,
|
|
473
655
|
...(data.sourcePort !== undefined ? { port: data.sourcePort } : {}),
|
|
@@ -595,7 +777,7 @@ export function getDefaultNodeBounds(
|
|
|
595
777
|
case 'obj':
|
|
596
778
|
return { x, y, width: 160, height: 80 };
|
|
597
779
|
case 'per':
|
|
598
|
-
return { x, y, width:
|
|
780
|
+
return { x, y, width: 90, height: 90 };
|
|
599
781
|
default:
|
|
600
782
|
return { x, y, width: 140, height: 60 };
|
|
601
783
|
}
|
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,8 +36,10 @@ export {
|
|
|
35
36
|
configureAimGraph,
|
|
36
37
|
createAimNode,
|
|
37
38
|
createAimEdge,
|
|
39
|
+
applyEdgeRouting,
|
|
38
40
|
getDefaultNodeBounds,
|
|
39
41
|
getDefaultNodeName,
|
|
42
|
+
wrapAimText,
|
|
40
43
|
} from './X6Shapes.js';
|
|
41
44
|
|
|
42
45
|
// Anti-Entropy Semantic Connection Rules
|
|
@@ -83,6 +83,12 @@
|
|
|
83
83
|
ry: var(--aim-radius-act);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
.aim-node[aim-act] text,
|
|
87
|
+
.aim-node[aim-kind="act"] text,
|
|
88
|
+
.aim-act text {
|
|
89
|
+
text-decoration: underline;
|
|
90
|
+
}
|
|
91
|
+
|
|
86
92
|
.aim-node[aim-act]:hover,
|
|
87
93
|
.aim-node[aim-kind="act"]:hover {
|
|
88
94
|
filter: drop-shadow(0 0 6px rgba(16, 185, 129, 0.4));
|
|
@@ -115,20 +121,28 @@
|
|
|
115
121
|
stroke-width: 1.5px;
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
.aim-node[aim-obj] text,
|
|
125
|
+
.aim-node[aim-kind="obj"] text,
|
|
126
|
+
.aim-obj text {
|
|
127
|
+
text-decoration: underline;
|
|
128
|
+
}
|
|
129
|
+
|
|
118
130
|
/* 5. Person / Actor Nodes ('per') */
|
|
119
131
|
.aim-node[aim-per],
|
|
120
132
|
.aim-node[aim-kind="per"],
|
|
121
133
|
.aim-per {
|
|
122
|
-
fill:
|
|
123
|
-
stroke:
|
|
124
|
-
stroke-width: 1.5px;
|
|
125
|
-
rx: var(--aim-radius-card);
|
|
126
|
-
ry: var(--aim-radius-card);
|
|
134
|
+
fill: transparent;
|
|
135
|
+
stroke: none;
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
.aim-node[aim-per]
|
|
138
|
+
.aim-node[aim-per] path,
|
|
139
|
+
.aim-node[aim-per] circle {
|
|
140
|
+
transition: stroke 0.15s ease;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.aim-node[aim-per][aim-role="initiating"] path,
|
|
144
|
+
.aim-node[aim-per][aim-role="initiating"] circle {
|
|
130
145
|
stroke: var(--aim-net-gold);
|
|
131
|
-
stroke-width: 2px;
|
|
132
146
|
}
|
|
133
147
|
|
|
134
148
|
/* Relationship Edges */
|
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,8 +134,14 @@ 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[];
|
|
142
|
+
|
|
143
|
+
/** Precomputed or live SVG path data ('M ... L ...') for standalone vector rendering. */
|
|
144
|
+
readonly pathData?: string;
|
|
131
145
|
}
|
|
132
146
|
|
|
133
147
|
/**
|
|
@@ -149,6 +163,9 @@ export interface RaidMetamodel {
|
|
|
149
163
|
/** Relationships connecting projected nodes. */
|
|
150
164
|
readonly edges: readonly RaidEdgeData[];
|
|
151
165
|
|
|
166
|
+
/** Diagram-level edge routing mode ('manhattan', 'normal', 'smooth'). */
|
|
167
|
+
readonly routing?: AimRoutingMode;
|
|
168
|
+
|
|
152
169
|
/** Additional diagram-level metadata. */
|
|
153
170
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
154
171
|
}
|
|
@@ -172,6 +189,7 @@ export const AimSvgContract = {
|
|
|
172
189
|
ATTR_TARGET: 'aim-target',
|
|
173
190
|
ATTR_SOURCE_PORT: 'aim-source-port',
|
|
174
191
|
ATTR_TARGET_PORT: 'aim-target-port',
|
|
192
|
+
ATTR_ROUTING: 'aim-routing',
|
|
175
193
|
ATTR_BENDS: 'aim-bends',
|
|
176
194
|
|
|
177
195
|
// Selectors for DOM queries
|
|
@@ -189,6 +207,13 @@ export interface HydrationOptions {
|
|
|
189
207
|
/** If true, automatically executes Manhattan routing if bend points are missing. Default: true. */
|
|
190
208
|
readonly autoRouteEdges?: boolean;
|
|
191
209
|
|
|
210
|
+
/**
|
|
211
|
+
* If true, infers docking ports (e.g. port-right, port-left) based on node geometry when ports
|
|
212
|
+
* are omitted in the SVG. When false (default), edges bind directly to node cells without ports,
|
|
213
|
+
* enabling dynamic Manhattan center-aiming routing. Default: false.
|
|
214
|
+
*/
|
|
215
|
+
readonly inferPorts?: boolean;
|
|
216
|
+
|
|
192
217
|
/** Default fallback dimensions when width/height are unspecified in SVG. */
|
|
193
218
|
readonly defaultNodeSize?: { readonly width: number; readonly height: number };
|
|
194
219
|
}
|
|
@@ -205,4 +230,7 @@ export interface SerializationOptions {
|
|
|
205
230
|
|
|
206
231
|
/** Inject Cascais design token CSS variables into `<defs><style>`. Default: true. */
|
|
207
232
|
readonly embedStyles?: boolean;
|
|
233
|
+
|
|
234
|
+
/** Optional diagram-level routing mode to serialize onto root `<svg>` tag. */
|
|
235
|
+
readonly routingMode?: AimRoutingMode;
|
|
208
236
|
}
|