@dr2rai/raid-canvas 0.1.0 → 0.2.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/src/RaiBridge.ts CHANGED
@@ -147,6 +147,8 @@ export class RaiBridge {
147
147
  ...(targetPort !== undefined ? { targetPort } : {}),
148
148
  ...(label !== undefined ? { label } : {}),
149
149
  ...(customData.stereotype !== undefined ? { stereotype: customData.stereotype } : {}),
150
+ ...(customData.sourceCardinality !== undefined ? { sourceCardinality: customData.sourceCardinality } : {}),
151
+ ...(customData.targetCardinality !== undefined ? { targetCardinality: customData.targetCardinality } : {}),
150
152
  bendPoints,
151
153
  };
152
154
 
@@ -266,8 +268,66 @@ export class RaiBridge {
266
268
  const bendsAttr = el.getAttribute(AimSvgContract.ATTR_BENDS) ?? '';
267
269
  const bendPoints = this.parseBendPoints(bendsAttr);
268
270
 
269
- const sourcePort = el.getAttribute(AimSvgContract.ATTR_SOURCE_PORT) ?? undefined;
270
- const targetPort = el.getAttribute(AimSvgContract.ATTR_TARGET_PORT) ?? undefined;
271
+ const label =
272
+ el.getAttribute('aim-label') ??
273
+ el.querySelector('text')?.textContent?.trim() ??
274
+ undefined;
275
+
276
+ let sourcePort = el.getAttribute(AimSvgContract.ATTR_SOURCE_PORT) ?? undefined;
277
+ let targetPort = el.getAttribute(AimSvgContract.ATTR_TARGET_PORT) ?? undefined;
278
+
279
+ const sourceNode = nodes.find((n) => n.id === sourceId);
280
+ const targetNode = nodes.find((n) => n.id === targetId);
281
+
282
+ // Infer optimal orthogonal docking ports if not explicitly declared
283
+ if (sourceNode && targetNode) {
284
+ const dx =
285
+ targetNode.bounds.x +
286
+ targetNode.bounds.width / 2 -
287
+ (sourceNode.bounds.x + sourceNode.bounds.width / 2);
288
+ const dy =
289
+ targetNode.bounds.y +
290
+ targetNode.bounds.height / 2 -
291
+ (sourceNode.bounds.y + sourceNode.bounds.height / 2);
292
+
293
+ if (!sourcePort) {
294
+ sourcePort =
295
+ Math.abs(dx) >= Math.abs(dy)
296
+ ? dx >= 0
297
+ ? 'port-right'
298
+ : 'port-left'
299
+ : dy >= 0
300
+ ? 'port-bottom'
301
+ : 'port-top';
302
+ }
303
+ if (!targetPort) {
304
+ targetPort =
305
+ Math.abs(dx) >= Math.abs(dy)
306
+ ? dx >= 0
307
+ ? 'port-left'
308
+ : 'port-right'
309
+ : dy >= 0
310
+ ? 'port-top'
311
+ : 'port-bottom';
312
+ }
313
+ }
314
+
315
+ // If bendPoints contains only the 2 terminal endpoints of a straight connector,
316
+ // clear them so the orthogonal router doesn't treat the terminals as obstacle waypoints
317
+ const isTerminalOnly =
318
+ bendPoints.length === 2 &&
319
+ sourceNode &&
320
+ targetNode &&
321
+ ((Math.abs(bendPoints[0]!.y - bendPoints[1]!.y) < 2 &&
322
+ Math.abs(bendPoints[0]!.y - (sourceNode.bounds.y + sourceNode.bounds.height / 2)) < 40) ||
323
+ (Math.abs(bendPoints[0]!.x - bendPoints[1]!.x) < 2 &&
324
+ Math.abs(bendPoints[0]!.x - (sourceNode.bounds.x + sourceNode.bounds.width / 2)) < 40));
325
+ const effectiveBendPoints = isTerminalOnly ? [] : bendPoints;
326
+
327
+ const sourceCardinality =
328
+ el.getAttribute('aim-source-cardinality') ?? undefined;
329
+ const targetCardinality =
330
+ el.getAttribute('aim-target-cardinality') ?? undefined;
271
331
 
272
332
  edges.push({
273
333
  id,
@@ -276,7 +336,10 @@ export class RaiBridge {
276
336
  targetId,
277
337
  ...(sourcePort !== undefined ? { sourcePort } : {}),
278
338
  ...(targetPort !== undefined ? { targetPort } : {}),
279
- bendPoints,
339
+ ...(label !== undefined ? { label } : {}),
340
+ ...(sourceCardinality !== undefined ? { sourceCardinality } : {}),
341
+ ...(targetCardinality !== undefined ? { targetCardinality } : {}),
342
+ bendPoints: effectiveBendPoints,
280
343
  });
281
344
  }
282
345
 
@@ -370,6 +433,16 @@ export class RaiBridge {
370
433
  el.setAttribute(AimSvgContract.ATTR_BENDS, bendsString);
371
434
  el.setAttribute(AimSvgContract.ATTR_EDGE, 'true');
372
435
  el.setAttribute(AimSvgContract.ATTR_EDGE_KIND, edge.kind);
436
+
437
+ if (edge.stereotype !== undefined) {
438
+ el.setAttribute(AimSvgContract.ATTR_STEREOTYPE, edge.stereotype);
439
+ }
440
+ if (edge.sourceCardinality !== undefined) {
441
+ el.setAttribute('aim-source-cardinality', edge.sourceCardinality);
442
+ }
443
+ if (edge.targetCardinality !== undefined) {
444
+ el.setAttribute('aim-target-cardinality', edge.targetCardinality);
445
+ }
373
446
  }
374
447
  }
375
448