@uigraph/sdk 1.2.12 → 1.2.13

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/index.mjs CHANGED
@@ -2737,7 +2737,7 @@ function convertC4ToReactFlow(data) {
2737
2737
  sourceHandle: `source-${side.source}`,
2738
2738
  targetHandle: `target-${side.target}`,
2739
2739
  label,
2740
- type: "simplebezier",
2740
+ type: "smoothstep",
2741
2741
  style: {
2742
2742
  stroke: lineColor,
2743
2743
  strokeWidth: (_style$lineWidth = style === null || style === void 0 ? void 0 : style.lineWidth) !== null && _style$lineWidth !== void 0 ? _style$lineWidth : 1.5
@@ -2849,10 +2849,27 @@ function resolvePortalNodeType(hasImageUrl, tag) {
2849
2849
  if (tag && TAG_TO_NODE_TYPE[tag]) return TAG_TO_NODE_TYPE[tag];
2850
2850
  return "shape";
2851
2851
  }
2852
+ const COMPOUND_SHAPE_DELIMITERS = {
2853
+ circle: {
2854
+ open: "((",
2855
+ close: "))"
2856
+ },
2857
+ stadium: {
2858
+ open: "([",
2859
+ close: "])"
2860
+ },
2861
+ cylinder: {
2862
+ open: "[(",
2863
+ close: ")]"
2864
+ },
2865
+ subroutine: {
2866
+ open: "[[",
2867
+ close: "]]"
2868
+ }
2869
+ };
2852
2870
  function getNodeShape(nodeDefinition) {
2853
2871
  if (nodeDefinition.includes("{") && nodeDefinition.includes("}")) return "diamond";
2854
- if (nodeDefinition.includes("((") && nodeDefinition.includes("))")) return "circle";
2855
- if (nodeDefinition.includes("([") && nodeDefinition.includes("])")) return "stadium";
2872
+ for (const [shape, { open, close }] of Object.entries(COMPOUND_SHAPE_DELIMITERS)) if (nodeDefinition.includes(open) && nodeDefinition.includes(close)) return shape;
2856
2873
  if (nodeDefinition.includes("[") && nodeDefinition.includes("]")) return "rect";
2857
2874
  if (nodeDefinition.includes("(") && nodeDefinition.includes(")")) return "round";
2858
2875
  return "rect";
@@ -2902,15 +2919,35 @@ function parseMermaidCode(code) {
2902
2919
  }
2903
2920
  const lines = cleanCode.split("\n");
2904
2921
  const subgraphStack = [];
2922
+ const SHAPE_DELIMITER_PAIRS = [
2923
+ ["[", "]"],
2924
+ ["(", ")"],
2925
+ ["{", "}"]
2926
+ ];
2927
+ function stripWrappingShapeDelimiters(label) {
2928
+ let result = label;
2929
+ let changed = true;
2930
+ while (changed && result.length >= 2) {
2931
+ changed = false;
2932
+ for (const [open, close] of SHAPE_DELIMITER_PAIRS) if (result[0] === open && result[result.length - 1] === close) {
2933
+ const stripped = result.slice(1, -1).trim().replace(new RegExp("^\"(.*)\"$", "s"), "$1").replace(new RegExp("^'(.*)'$", "s"), "$1").trim();
2934
+ if (!stripped) break;
2935
+ result = stripped;
2936
+ changed = true;
2937
+ break;
2938
+ }
2939
+ }
2940
+ return result;
2941
+ }
2905
2942
  function enhancedCleanLabel(label) {
2906
- return label.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]*>/g, "").replace(/\\u([0-9a-fA-F]{4})/g, (match, code$1) => {
2943
+ return stripWrappingShapeDelimiters(label.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]*>/g, "").replace(/\\u([0-9a-fA-F]{4})/g, (match, code$1) => {
2907
2944
  try {
2908
2945
  return String.fromCharCode(parseInt(code$1, 16));
2909
2946
  } catch (_unused) {
2910
2947
  debugLog(`Warning: Could not parse unicode character: ${match}`);
2911
2948
  return match;
2912
2949
  }
2913
- }).replace(/\\n/g, "\n").replace(/\s*\n\s*/g, "\n").trim();
2950
+ }).replace(/\\n/g, "\n").replace(/\s*\n\s*/g, "\n").trim());
2914
2951
  }
2915
2952
  debugLog("Pre-scanning for node definitions...");
2916
2953
  lines.forEach((line, lineIndex) => {
@@ -2956,11 +2993,13 @@ function parseMermaidCode(code) {
2956
2993
  if (shapeDef) {
2957
2994
  const shape = getNodeShape(fullDef);
2958
2995
  let rawLabel = nodeId;
2959
- const labelContentMatch = shapeDef.match(new RegExp("^[\\[\\(\\{](.*)[\\]\\)\\}]$", "s"));
2960
- if (labelContentMatch) {
2961
- rawLabel = labelContentMatch[1];
2962
- rawLabel = rawLabel.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
2996
+ const compoundDelimiters = COMPOUND_SHAPE_DELIMITERS[shape];
2997
+ if (compoundDelimiters && shapeDef.startsWith(compoundDelimiters.open) && shapeDef.endsWith(compoundDelimiters.close)) rawLabel = shapeDef.slice(compoundDelimiters.open.length, shapeDef.length - compoundDelimiters.close.length);
2998
+ else {
2999
+ const labelContentMatch = shapeDef.match(new RegExp("^[\\[\\(\\{](.*)[\\]\\)\\}]$", "s"));
3000
+ if (labelContentMatch) rawLabel = labelContentMatch[1];
2963
3001
  }
3002
+ rawLabel = rawLabel.replace(new RegExp("^\"(.*)\"$", "s"), "$1").replace(new RegExp("^'(.*)'$", "s"), "$1");
2964
3003
  const label = enhancedCleanLabel(rawLabel);
2965
3004
  nodeDefinitions.set(nodeId, {
2966
3005
  label,
@@ -3117,7 +3156,16 @@ function parseMermaidCode(code) {
3117
3156
  const openChar = openCharMatch[1];
3118
3157
  const openPos = idx + rest.indexOf(openChar);
3119
3158
  const closeChar = openChar === "[" ? "]" : openChar === "(" ? ")" : "}";
3120
- const closePos = str.indexOf(closeChar, openPos + 1);
3159
+ let depth = 0;
3160
+ let closePos = -1;
3161
+ for (let pos = openPos; pos < str.length; pos++) if (str[pos] === openChar) depth++;
3162
+ else if (str[pos] === closeChar) {
3163
+ depth--;
3164
+ if (depth === 0) {
3165
+ closePos = pos;
3166
+ break;
3167
+ }
3168
+ }
3121
3169
  if (closePos !== -1) return {
3122
3170
  id,
3123
3171
  full: str.slice(startIndex + idMatch[0].search(/\S/), closePos + 1).trim(),
@@ -3130,101 +3178,106 @@ function parseMermaidCode(code) {
3130
3178
  endIndex: idx
3131
3179
  };
3132
3180
  }
3181
+ const arrowHeads = [
3182
+ "-.->",
3183
+ "-->",
3184
+ "==>",
3185
+ "->>",
3186
+ "<->",
3187
+ "-<>",
3188
+ "<-",
3189
+ "->"
3190
+ ];
3133
3191
  function parseEdge(str) {
3134
3192
  try {
3193
+ const hops = [];
3135
3194
  let i$2 = 0;
3136
- const src = extractToken(str, i$2);
3195
+ let src = extractToken(str, i$2);
3137
3196
  if (!src) return null;
3138
3197
  i$2 = src.endIndex;
3139
- while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3140
- const arrowHeads = [
3141
- "-.->",
3142
- "-->",
3143
- "==>",
3144
- "->>",
3145
- "<->",
3146
- "-<>",
3147
- "<-",
3148
- "->"
3149
- ];
3150
- let foundArrowIndex = -1;
3151
- let foundArrow = "";
3152
- for (const ah of arrowHeads) {
3153
- const idx = str.indexOf(ah, i$2);
3154
- if (idx !== -1 && (foundArrowIndex === -1 || idx < foundArrowIndex)) {
3155
- foundArrowIndex = idx;
3156
- foundArrow = ah;
3157
- }
3158
- }
3159
- let op = null;
3160
- let edgeLabel = "";
3161
- if (foundArrowIndex !== -1) {
3162
- const between = str.slice(i$2, foundArrowIndex);
3163
- const prePipeMatch = between.match(/\|(.*?)\|/);
3164
- if (prePipeMatch) edgeLabel = prePipeMatch[1];
3165
- else {
3166
- const inline = between.replace(/^\s*[\-\.=:\~]+\s*/g, "").replace(/\s*[\-\.=:\~]+\s*$/g, "").trim();
3167
- if (inline) edgeLabel = inline;
3168
- }
3169
- op = foundArrow;
3170
- i$2 = foundArrowIndex + foundArrow.length;
3198
+ while (true) {
3171
3199
  while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3172
- if (str[i$2] === "|") {
3173
- const next = str.indexOf("|", i$2 + 1);
3174
- if (next !== -1) {
3175
- edgeLabel = str.slice(i$2 + 1, next);
3176
- i$2 = next + 1;
3200
+ let foundArrowIndex = -1;
3201
+ let foundArrow = "";
3202
+ for (const ah of arrowHeads) {
3203
+ const idx = str.indexOf(ah, i$2);
3204
+ if (idx !== -1 && (foundArrowIndex === -1 || idx < foundArrowIndex)) {
3205
+ foundArrowIndex = idx;
3206
+ foundArrow = ah;
3177
3207
  }
3178
3208
  }
3179
- } else {
3180
- for (const o of [
3181
- "---",
3182
- "-.-",
3183
- "::",
3184
- ":-:",
3185
- "...",
3186
- "~",
3187
- "==="
3188
- ].sort((a, b) => b.length - a.length)) if (str.startsWith(o, i$2)) {
3189
- op = o;
3190
- i$2 += o.length;
3191
- break;
3192
- }
3193
- if (!op) return null;
3194
- while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3195
- if (str[i$2] === "|") {
3196
- const next = str.indexOf("|", i$2 + 1);
3197
- if (next !== -1) {
3198
- edgeLabel = str.slice(i$2 + 1, next);
3199
- i$2 = next + 1;
3209
+ let op = null;
3210
+ let edgeLabel = "";
3211
+ if (foundArrowIndex !== -1) {
3212
+ const between = str.slice(i$2, foundArrowIndex);
3213
+ const prePipeMatch = between.match(/\|(.*?)\|/);
3214
+ if (prePipeMatch) edgeLabel = prePipeMatch[1];
3215
+ else {
3216
+ const inline = between.replace(/^\s*[\-\.=:\~]+\s*/g, "").replace(/\s*[\-\.=:\~]+\s*$/g, "").trim();
3217
+ if (inline) edgeLabel = inline;
3200
3218
  }
3219
+ op = foundArrow;
3220
+ i$2 = foundArrowIndex + foundArrow.length;
3221
+ while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3222
+ if (str[i$2] === "|") {
3223
+ const next = str.indexOf("|", i$2 + 1);
3224
+ if (next !== -1) {
3225
+ edgeLabel = str.slice(i$2 + 1, next);
3226
+ i$2 = next + 1;
3227
+ }
3228
+ }
3229
+ } else if (hops.length === 0) {
3230
+ for (const o of [
3231
+ "---",
3232
+ "-.-",
3233
+ "::",
3234
+ ":-:",
3235
+ "...",
3236
+ "~",
3237
+ "==="
3238
+ ].sort((a, b) => b.length - a.length)) if (str.startsWith(o, i$2)) {
3239
+ op = o;
3240
+ i$2 += o.length;
3241
+ break;
3242
+ }
3243
+ if (!op) return null;
3244
+ while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3245
+ if (str[i$2] === "|") {
3246
+ const next = str.indexOf("|", i$2 + 1);
3247
+ if (next !== -1) {
3248
+ edgeLabel = str.slice(i$2 + 1, next);
3249
+ i$2 = next + 1;
3250
+ }
3251
+ }
3252
+ } else break;
3253
+ while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3254
+ const tgt = extractToken(str, i$2);
3255
+ if (!tgt) break;
3256
+ const trailing = str.slice(tgt.endIndex).trimStart();
3257
+ if (!edgeLabel && !trailing.startsWith(":::") && !arrowHeads.some((ah) => trailing.includes(ah))) {
3258
+ const colonLabelMatch = trailing.match(/^:\s*(.+)$/);
3259
+ if (colonLabelMatch) edgeLabel = colonLabelMatch[1].trim();
3201
3260
  }
3261
+ const isReverseArrow = op === "<-";
3262
+ hops.push({
3263
+ sourceId: isReverseArrow ? tgt.id : src.id,
3264
+ sourceFull: isReverseArrow ? tgt.full : src.full,
3265
+ targetId: isReverseArrow ? src.id : tgt.id,
3266
+ targetFull: isReverseArrow ? src.full : tgt.full,
3267
+ edgeType: isReverseArrow ? "->" : op,
3268
+ edgeLabel
3269
+ });
3270
+ src = tgt;
3271
+ i$2 = tgt.endIndex;
3202
3272
  }
3203
- while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3204
- const tgt = extractToken(str, i$2);
3205
- if (!tgt) return null;
3206
- const trailing = str.slice(tgt.endIndex).trimStart();
3207
- if (!edgeLabel && !trailing.startsWith(":::")) {
3208
- const colonLabelMatch = trailing.match(/^:\s*(.+)$/);
3209
- if (colonLabelMatch) edgeLabel = colonLabelMatch[1].trim();
3210
- }
3211
- const isReverseArrow = op === "<-";
3212
- return {
3213
- sourceId: isReverseArrow ? tgt.id : src.id,
3214
- sourceFull: isReverseArrow ? tgt.full : src.full,
3215
- targetId: isReverseArrow ? src.id : tgt.id,
3216
- targetFull: isReverseArrow ? src.full : tgt.full,
3217
- edgeType: isReverseArrow ? "->" : op,
3218
- edgeLabel
3219
- };
3273
+ return hops.length > 0 ? hops : null;
3220
3274
  } catch (_unused2) {
3221
3275
  return null;
3222
3276
  }
3223
3277
  }
3224
- const parsedEdge = parseEdge(line);
3225
- if (!parsedEdge) debugLog(`Line "${line}" did not match edge pattern - checking for standalone nodes`);
3226
- if (parsedEdge) try {
3227
- const { sourceId, targetId, edgeType, edgeLabel } = parsedEdge;
3278
+ const parsedEdges = parseEdge(line);
3279
+ if (!parsedEdges) debugLog(`Line "${line}" did not match edge pattern - checking for standalone nodes`);
3280
+ if (parsedEdges) for (const { sourceId, targetId, edgeType, edgeLabel } of parsedEdges) try {
3228
3281
  debugLog(`Found edge: ${sourceId} ${edgeType} ${targetId} with label: "${edgeLabel}" in context: ${currentSubgraph || "global"}`);
3229
3282
  const isSourceSubgraph = subgraphMap.has(sourceId);
3230
3283
  const isTargetSubgraph = subgraphMap.has(targetId);
@@ -3244,10 +3297,11 @@ function parseMermaidCode(code) {
3244
3297
  createOrGetNode(targetId, targetSubgraph);
3245
3298
  }
3246
3299
  }
3300
+ const unquotedEdgeLabel = edgeLabel.replace(new RegExp("^\"(.*)\"$", "s"), "$1").replace(new RegExp("^'(.*)'$", "s"), "$1");
3247
3301
  edges.push({
3248
3302
  source: sourceId,
3249
3303
  target: targetId,
3250
- label: enhancedCleanLabel(edgeLabel),
3304
+ label: enhancedCleanLabel(unquotedEdgeLabel),
3251
3305
  type: edgeType,
3252
3306
  isSourceSubgraph,
3253
3307
  isTargetSubgraph
@@ -3303,7 +3357,9 @@ const MERMAID_TO_PORTAL_SHAPE = {
3303
3357
  round: "rounded-rect",
3304
3358
  stadium: "terminator",
3305
3359
  circle: "ellipse",
3306
- diamond: "diamond"
3360
+ diamond: "diamond",
3361
+ cylinder: "cylinder",
3362
+ subroutine: "subroutine"
3307
3363
  };
3308
3364
  const SUBGRAPH_HEADER_HEIGHT = LAYOUT_SPACING.SUBGRAPH_HEADER_HEIGHT;
3309
3365
  const SUBGRAPH_PADDING = LAYOUT_SPACING.SUBGRAPH_PADDING;
@@ -3340,7 +3396,8 @@ function calculateNodeSize(label, shape, isImageNode = false) {
3340
3396
  } catch (_unused) {}
3341
3397
  return text.length * 8;
3342
3398
  }
3343
- const baseWidth = Math.max(...lines.map((line) => Math.ceil(measureLineWidth(line)))) + 30;
3399
+ const maxLineWidth = Math.max(...lines.map((line) => Math.ceil(measureLineWidth(line))));
3400
+ const baseWidth = maxLineWidth + 30;
3344
3401
  const baseHeight = lines.length * 18 + 20;
3345
3402
  const width = Math.max(80, baseWidth + 30);
3346
3403
  const height = Math.max(40, baseHeight + 20);
@@ -3355,6 +3412,27 @@ function calculateNodeSize(label, shape, isImageNode = false) {
3355
3412
  height: size
3356
3413
  };
3357
3414
  }
3415
+ if (shape === "stadium") {
3416
+ const sideInset = Math.max(12, height / 2) * 2;
3417
+ return {
3418
+ width: Math.max(width, maxLineWidth + sideInset + 20),
3419
+ height
3420
+ };
3421
+ }
3422
+ if (shape === "cylinder") {
3423
+ const capInset = Math.max(12, Math.min(height * .18, width * .5) + 8) * 2;
3424
+ return {
3425
+ width,
3426
+ height: Math.max(height, lines.length * 18 + capInset + 10)
3427
+ };
3428
+ }
3429
+ if (shape === "subroutine") {
3430
+ const sideInset = Math.max(24, width * .2);
3431
+ return {
3432
+ width: Math.max(width, maxLineWidth + sideInset + 20),
3433
+ height
3434
+ };
3435
+ }
3358
3436
  return {
3359
3437
  width,
3360
3438
  height
@@ -3623,11 +3701,12 @@ function validateNodeSpacing(nodePositions, subgraphId) {
3623
3701
  if (!hasOverlap) debugLog(`✓ Node spacing validated for subgraph ${subgraphId} - no overlaps detected`);
3624
3702
  }
3625
3703
  function layoutMetaGraph(nodes, edges, subgraphLayouts, direction) {
3704
+ const hasTopLevelSubgraphs = Array.from(subgraphLayouts.values()).some((layout) => !layout.parentId);
3626
3705
  const g = new dagre.graphlib.Graph();
3627
3706
  g.setGraph({
3628
3707
  rankdir: direction,
3629
- nodesep: CONTAINER_SEPARATION_HORIZONTAL,
3630
- ranksep: CONTAINER_SEPARATION_VERTICAL,
3708
+ nodesep: hasTopLevelSubgraphs ? CONTAINER_SEPARATION_HORIZONTAL : NODE_SEPARATION_HORIZONTAL,
3709
+ ranksep: hasTopLevelSubgraphs ? CONTAINER_SEPARATION_VERTICAL : NODE_SEPARATION_VERTICAL,
3631
3710
  marginx: META_GRAPH_MARGIN,
3632
3711
  marginy: META_GRAPH_MARGIN,
3633
3712
  ranker: DAGRE_RANKER
@@ -3872,7 +3951,8 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
3872
3951
  ];
3873
3952
  return subgraphColors[index % subgraphColors.length];
3874
3953
  }
3875
- processSubgraphsInHierarchicalOrder(subgraphs).forEach((subgraph, index) => {
3954
+ const orderedSubgraphs = processSubgraphsInHierarchicalOrder(subgraphs);
3955
+ orderedSubgraphs.forEach((subgraph, index) => {
3876
3956
  const layout = subgraphLayouts.get(subgraph.id);
3877
3957
  const position = subgraphPositions.get(subgraph.id);
3878
3958
  if (layout && position) {
@@ -4067,11 +4147,42 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
4067
4147
  zIndex: 1
4068
4148
  });
4069
4149
  });
4150
+ const nodeSubgraphById = new Map(nodes.map((n) => [n.id, n.subgraph]));
4151
+ const meaningfulEdges = edges.filter((edge) => {
4152
+ if (edge.isTargetSubgraph && nodeSubgraphById.get(edge.source) === edge.target) {
4153
+ debugLog(`Dropping edge ${edge.source} -> ${edge.target}: source already lives inside this subgraph`);
4154
+ return false;
4155
+ }
4156
+ if (edge.isSourceSubgraph && nodeSubgraphById.get(edge.target) === edge.source) {
4157
+ debugLog(`Dropping edge ${edge.source} -> ${edge.target}: target already lives inside this subgraph`);
4158
+ return false;
4159
+ }
4160
+ return true;
4161
+ });
4162
+ const nodeAxisPosition = /* @__PURE__ */ new Map();
4163
+ orderedSubgraphs.forEach((subgraph) => {
4164
+ const position = subgraphPositions.get(subgraph.id);
4165
+ if (position) nodeAxisPosition.set(`subgraph-${subgraph.id}`, position);
4166
+ });
4167
+ nodes.forEach((node) => {
4168
+ if (node.subgraph) {
4169
+ const subgraphLayout = subgraphLayouts.get(node.subgraph);
4170
+ const subgraphPosition = subgraphPositions.get(node.subgraph);
4171
+ const nodeLayout = subgraphLayout === null || subgraphLayout === void 0 ? void 0 : subgraphLayout.nodes.get(node.id);
4172
+ if (nodeLayout && subgraphPosition) nodeAxisPosition.set(node.id, {
4173
+ x: subgraphPosition.x + nodeLayout.x,
4174
+ y: subgraphPosition.y + nodeLayout.y
4175
+ });
4176
+ } else {
4177
+ const standalonePos = standalonePositions.get(node.id);
4178
+ if (standalonePos) nodeAxisPosition.set(node.id, standalonePos);
4179
+ }
4180
+ });
4070
4181
  return {
4071
4182
  nodes: reactFlowNodes,
4072
- edges: edges.map((edge, index) => {
4183
+ edges: meaningfulEdges.map((edge, index) => {
4073
4184
  const edgeStyle = { strokeWidth: 2.5 };
4074
- const edgeType = "default";
4185
+ const edgeType = "smoothstep";
4075
4186
  switch (edge.type) {
4076
4187
  case "-->":
4077
4188
  case "->": break;
@@ -4088,6 +4199,22 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
4088
4199
  }
4089
4200
  const sourceId = edge.isSourceSubgraph ? `subgraph-${edge.source}` : edge.source;
4090
4201
  const targetId = edge.isTargetSubgraph ? `subgraph-${edge.target}` : edge.target;
4202
+ const isHorizontalLayout = direction === "LR" || direction === "RL";
4203
+ let sourceHandle = isHorizontalLayout ? "source-right" : "source-bottom";
4204
+ let targetHandle = isHorizontalLayout ? "target-left" : "target-top";
4205
+ const sourcePoint = nodeAxisPosition.get(sourceId);
4206
+ const targetPoint = nodeAxisPosition.get(targetId);
4207
+ if (sourcePoint && targetPoint) {
4208
+ if (isHorizontalLayout) {
4209
+ if (targetPoint.x < sourcePoint.x) {
4210
+ sourceHandle = "source-left";
4211
+ targetHandle = "target-right";
4212
+ }
4213
+ } else if (targetPoint.y < sourcePoint.y) {
4214
+ sourceHandle = "source-top";
4215
+ targetHandle = "target-bottom";
4216
+ }
4217
+ }
4091
4218
  return {
4092
4219
  id: `edge-${edge.source}-${edge.target}-${index}`,
4093
4220
  source: sourceId,
@@ -4107,8 +4234,8 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
4107
4234
  width: 20,
4108
4235
  height: 20
4109
4236
  },
4110
- sourceHandle: direction === "LR" || direction === "RL" ? "source-right" : "source-bottom",
4111
- targetHandle: direction === "LR" || direction === "RL" ? "target-left" : "target-top",
4237
+ sourceHandle,
4238
+ targetHandle,
4112
4239
  zIndex: 0
4113
4240
  };
4114
4241
  })
@@ -6895,4 +7022,494 @@ function convertUiGraphToMermaid(input, options) {
6895
7022
  context
6896
7023
  };
6897
7024
  }
6898
- export { AstToSqlGenerator, AstToUiConverter, BasicDetailsSchema, C4_COLORS, C4_LAYOUT, ComponentInputType, DialectIdSchema, DocumentCollectionSchema, DocumentIndexSchema, DynamoAttributeSchema, DynamoEditorSchema, DynamoGsiSchema, EditorSupportedDialectSchema, JsonEditorSchema, MongoCollectionSchema, MongoEditorSchema, MongoIndexFieldSchema, MongoIndexSchema, MongoNestedFieldSchema, NestedFieldSchema, NoSqlFieldSchema, SEQUENCE_LAYOUT, SqlToAstParser, buildMetaData, computeDiagramSyncHash, contextSchema, convertC4MermaidToReactFlow, convertC4ToReactFlow, convertDynamoSchemaToAst, convertJsonSchemaToAst, convertMermaidToReactFlow, convertMermaidToReactFlowWithContext, convertMongoSchemaToAst, convertNoSQLToAst, convertReactFlowToC4Mermaid, convertReactFlowToC4UiGraph, convertReactFlowToSequenceMermaid, convertReactFlowToSequenceUiGraph, convertUiGraphToMermaid, estimateSequenceMessageBoxSize, flattenMetaData, generateTableNodeId, generateUUID, getC4ElementColors, isC4Diagram, isC4ReactFlowDiagram, isSequenceDiagram, parseC4Diagram, sanitizeMermaidLabels, syncBaseData };
7025
+ const SHAPE_IDS = [
7026
+ "rectangle",
7027
+ "rounded-rect",
7028
+ "ellipse",
7029
+ "diamond",
7030
+ "triangle",
7031
+ "parallelogram",
7032
+ "trapezoid",
7033
+ "hexagon",
7034
+ "document",
7035
+ "cylinder",
7036
+ "delay",
7037
+ "off-page-connector",
7038
+ "display",
7039
+ "collate",
7040
+ "sort",
7041
+ "terminator",
7042
+ "or",
7043
+ "database",
7044
+ "multiple-documents",
7045
+ "subroutine",
7046
+ "manual-input",
7047
+ "summing-junction",
7048
+ "internal-storage"
7049
+ ];
7050
+ const STROKE_STYLES = [
7051
+ "solid",
7052
+ "dashed",
7053
+ "dotted"
7054
+ ];
7055
+ function clampNum(value, min, max) {
7056
+ if (value === void 0 || !Number.isFinite(value)) return void 0;
7057
+ return Math.min(max, Math.max(min, value));
7058
+ }
7059
+ function dropUndefined(input) {
7060
+ const output = {};
7061
+ for (const [key, value] of Object.entries(input)) {
7062
+ if (value === void 0) continue;
7063
+ output[key] = value;
7064
+ }
7065
+ return output;
7066
+ }
7067
+ function mapShapeStylePatch(patch) {
7068
+ return dropUndefined({
7069
+ fill: patch.fill,
7070
+ stroke: patch.stroke,
7071
+ strokeWidth: clampNum(patch.strokeWidth, 0, 10),
7072
+ strokeStyle: patch.strokeStyle,
7073
+ cornerRadius: clampNum(patch.cornerRadius, 0, 64),
7074
+ textColor: patch.textColor,
7075
+ textFontSize: clampNum(patch.textFontSize, 8, 48),
7076
+ shape: patch.shape
7077
+ });
7078
+ }
7079
+ function mapCloudStylePatch(patch) {
7080
+ return dropUndefined({
7081
+ fill: patch.fill,
7082
+ stroke: patch.stroke,
7083
+ strokeWidth: clampNum(patch.strokeWidth, 0, 10),
7084
+ strokeStyle: patch.strokeStyle
7085
+ });
7086
+ }
7087
+ function mapTextStylePatch(patch) {
7088
+ return dropUndefined({
7089
+ fill: patch.fill,
7090
+ stroke: patch.stroke,
7091
+ strokeWidth: clampNum(patch.strokeWidth, 0, 10),
7092
+ strokeStyle: patch.strokeStyle,
7093
+ borderRadius: clampNum(patch.cornerRadius, 0, 64),
7094
+ color: patch.textColor,
7095
+ fontSize: clampNum(patch.textFontSize, 8, 48)
7096
+ });
7097
+ }
7098
+ function mapGroupStylePatch(patch) {
7099
+ return dropUndefined({
7100
+ backgroundColor: patch.fill,
7101
+ borderColor: patch.stroke
7102
+ });
7103
+ }
7104
+ function mapC4StylePatch(patch) {
7105
+ return dropUndefined({
7106
+ fill: patch.fill,
7107
+ stroke: patch.stroke,
7108
+ fontColor: patch.textColor
7109
+ });
7110
+ }
7111
+ function mapC4BoundaryStylePatch(patch) {
7112
+ return dropUndefined({
7113
+ backgroundColor: patch.fill,
7114
+ borderColor: patch.stroke,
7115
+ fontColor: patch.textColor
7116
+ });
7117
+ }
7118
+ function mapSequenceParticipantStylePatch(patch) {
7119
+ return dropUndefined({
7120
+ color: patch.stroke,
7121
+ textColor: patch.textColor
7122
+ });
7123
+ }
7124
+ const NODE_TYPE_REGISTRY = {
7125
+ shape: {
7126
+ tag: "shape",
7127
+ diagramTypes: ["flowchart"],
7128
+ isContainer: false,
7129
+ geometryEditable: true,
7130
+ shapes: SHAPE_IDS,
7131
+ mapStylePatch: mapShapeStylePatch
7132
+ },
7133
+ default: {
7134
+ tag: "default",
7135
+ diagramTypes: ["flowchart"],
7136
+ isContainer: false,
7137
+ geometryEditable: true,
7138
+ shapes: SHAPE_IDS,
7139
+ mapStylePatch: mapShapeStylePatch
7140
+ },
7141
+ cloud: {
7142
+ tag: "cloud",
7143
+ diagramTypes: ["flowchart"],
7144
+ isContainer: false,
7145
+ geometryEditable: true,
7146
+ mapStylePatch: mapCloudStylePatch
7147
+ },
7148
+ text: {
7149
+ tag: "text",
7150
+ diagramTypes: ["flowchart"],
7151
+ isContainer: false,
7152
+ geometryEditable: true,
7153
+ mapStylePatch: mapTextStylePatch
7154
+ },
7155
+ group: {
7156
+ tag: "group",
7157
+ diagramTypes: ["flowchart"],
7158
+ isContainer: true,
7159
+ geometryEditable: false,
7160
+ mapStylePatch: mapGroupStylePatch
7161
+ },
7162
+ c4: {
7163
+ tag: "c4",
7164
+ diagramTypes: ["c4"],
7165
+ isContainer: false,
7166
+ geometryEditable: true,
7167
+ mapStylePatch: mapC4StylePatch
7168
+ },
7169
+ c4Boundary: {
7170
+ tag: "c4Boundary",
7171
+ diagramTypes: ["c4"],
7172
+ isContainer: true,
7173
+ geometryEditable: false,
7174
+ mapStylePatch: mapC4BoundaryStylePatch
7175
+ },
7176
+ sequenceParticipant: {
7177
+ tag: "sequenceParticipant",
7178
+ diagramTypes: ["sequence"],
7179
+ isContainer: false,
7180
+ geometryEditable: false,
7181
+ mapStylePatch: mapSequenceParticipantStylePatch
7182
+ }
7183
+ };
7184
+ function getNodeTypeSpec(type) {
7185
+ if (type === void 0) return void 0;
7186
+ return NODE_TYPE_REGISTRY[type];
7187
+ }
7188
+ function buildNodeStyleDataPatch(type, patch) {
7189
+ var _getNodeTypeSpec$mapS, _getNodeTypeSpec;
7190
+ return (_getNodeTypeSpec$mapS = (_getNodeTypeSpec = getNodeTypeSpec(type)) === null || _getNodeTypeSpec === void 0 ? void 0 : _getNodeTypeSpec.mapStylePatch(patch)) !== null && _getNodeTypeSpec$mapS !== void 0 ? _getNodeTypeSpec$mapS : {};
7191
+ }
7192
+ function isGeometryEditableNodeType(type) {
7193
+ var _getNodeTypeSpec$geom, _getNodeTypeSpec2;
7194
+ return (_getNodeTypeSpec$geom = (_getNodeTypeSpec2 = getNodeTypeSpec(type)) === null || _getNodeTypeSpec2 === void 0 ? void 0 : _getNodeTypeSpec2.geometryEditable) !== null && _getNodeTypeSpec$geom !== void 0 ? _getNodeTypeSpec$geom : false;
7195
+ }
7196
+ const THEME_ROLES = [
7197
+ "boundary",
7198
+ "client",
7199
+ "service",
7200
+ "data",
7201
+ "messaging",
7202
+ "neutral"
7203
+ ];
7204
+ const DEFAULT_THEME_ID = "professional";
7205
+ const THEME_REGISTRY = {
7206
+ professional: {
7207
+ id: "professional",
7208
+ label: "Professional",
7209
+ description: "Calm slate-navy palette, the default look",
7210
+ mode: "dark",
7211
+ promptHint: "clean, modern, professional, calm, corporate, default, no specific style requested",
7212
+ nodeText: "#F8FAFC",
7213
+ canvasText: "#93B4E8",
7214
+ roles: {
7215
+ boundary: {
7216
+ fill: "#171B26",
7217
+ stroke: "#3D4759"
7218
+ },
7219
+ client: {
7220
+ fill: "#1C2336",
7221
+ stroke: "#3D5EA8"
7222
+ },
7223
+ service: {
7224
+ fill: "#1C2336",
7225
+ stroke: "#3D5EA8"
7226
+ },
7227
+ data: {
7228
+ fill: "#18242F",
7229
+ stroke: "#3D8A7A"
7230
+ },
7231
+ messaging: {
7232
+ fill: "#241F30",
7233
+ stroke: "#6B5CA8"
7234
+ },
7235
+ neutral: {
7236
+ fill: "#1C2336",
7237
+ stroke: "#3D5EA8"
7238
+ }
7239
+ },
7240
+ edge: {
7241
+ stroke: "#828DA3",
7242
+ strokeWidth: 1.5,
7243
+ labelBackground: "#1E293B",
7244
+ labelText: "#E2E8F0",
7245
+ emphasizedStroke: "#5B9BFF",
7246
+ emphasizedStrokeWidth: 3
7247
+ }
7248
+ },
7249
+ "midnight-bold": {
7250
+ id: "midnight-bold",
7251
+ label: "Midnight Bold",
7252
+ description: "Dark background, saturated high-contrast accents",
7253
+ mode: "dark",
7254
+ promptHint: "dark, bold, saturated, high contrast, vivid, striking",
7255
+ nodeText: "#F8FAFC",
7256
+ canvasText: "#7FB2FF",
7257
+ roles: {
7258
+ boundary: {
7259
+ fill: "#12151F",
7260
+ stroke: "#3D4759"
7261
+ },
7262
+ client: {
7263
+ fill: "#1C2336",
7264
+ stroke: "#5B9BFF"
7265
+ },
7266
+ service: {
7267
+ fill: "#1C2336",
7268
+ stroke: "#38BDF8"
7269
+ },
7270
+ data: {
7271
+ fill: "#0F2D3A",
7272
+ stroke: "#2AD4B3"
7273
+ },
7274
+ messaging: {
7275
+ fill: "#2A2410",
7276
+ stroke: "#E8B93E"
7277
+ },
7278
+ neutral: {
7279
+ fill: "#1C2336",
7280
+ stroke: "#5B9BFF"
7281
+ }
7282
+ },
7283
+ edge: {
7284
+ stroke: "#5B9BFF",
7285
+ strokeWidth: 2.5,
7286
+ labelBackground: "#1E293B",
7287
+ labelText: "#E2E8F0",
7288
+ emphasizedStroke: "#5B9BFF",
7289
+ emphasizedStrokeWidth: 4
7290
+ }
7291
+ },
7292
+ "soft-pastel": {
7293
+ id: "soft-pastel",
7294
+ label: "Soft Pastel",
7295
+ description: "Dark cards, soft muted pastel-hued borders",
7296
+ mode: "dark",
7297
+ promptHint: "soft, pastel, gentle, muted, airy, whimsical",
7298
+ nodeText: "#F1F5F9",
7299
+ canvasText: "#A8C8E8",
7300
+ roles: {
7301
+ boundary: {
7302
+ fill: "#171B24",
7303
+ stroke: "#7C93B8"
7304
+ },
7305
+ client: {
7306
+ fill: "#1A2233",
7307
+ stroke: "#8FD9C4"
7308
+ },
7309
+ service: {
7310
+ fill: "#1A2233",
7311
+ stroke: "#93B8F0"
7312
+ },
7313
+ data: {
7314
+ fill: "#1F1A2E",
7315
+ stroke: "#E3A98C"
7316
+ },
7317
+ messaging: {
7318
+ fill: "#241F14",
7319
+ stroke: "#E8D48A"
7320
+ },
7321
+ neutral: {
7322
+ fill: "#1C2029",
7323
+ stroke: "#A8B3C4"
7324
+ }
7325
+ },
7326
+ edge: {
7327
+ stroke: "#8FA3C4",
7328
+ strokeWidth: 2,
7329
+ labelBackground: "#1E293B",
7330
+ labelText: "#E8EDF5",
7331
+ emphasizedStroke: "#8FD9C4",
7332
+ emphasizedStrokeWidth: 3.5
7333
+ }
7334
+ },
7335
+ monochrome: {
7336
+ id: "monochrome",
7337
+ label: "Monochrome",
7338
+ description: "Grayscale, roles differ by shade rather than hue",
7339
+ mode: "dark",
7340
+ promptHint: "monochrome, grayscale, black and white, minimal, neutral",
7341
+ nodeText: "#F3F4F6",
7342
+ canvasText: "#D1D5DB",
7343
+ roles: {
7344
+ boundary: {
7345
+ fill: "#17191D",
7346
+ stroke: "#4B5563"
7347
+ },
7348
+ client: {
7349
+ fill: "#23262B",
7350
+ stroke: "#9CA3AF"
7351
+ },
7352
+ service: {
7353
+ fill: "#1D2024",
7354
+ stroke: "#6B7280"
7355
+ },
7356
+ data: {
7357
+ fill: "#26292E",
7358
+ stroke: "#D1D5DB"
7359
+ },
7360
+ messaging: {
7361
+ fill: "#202225",
7362
+ stroke: "#4B5563"
7363
+ },
7364
+ neutral: {
7365
+ fill: "#1F2226",
7366
+ stroke: "#6B7280"
7367
+ }
7368
+ },
7369
+ edge: {
7370
+ stroke: "#9CA3AF",
7371
+ strokeWidth: 2,
7372
+ labelBackground: "#111318",
7373
+ labelText: "#E5E7EB",
7374
+ emphasizedStroke: "#F3F4F6",
7375
+ emphasizedStrokeWidth: 3.5
7376
+ }
7377
+ },
7378
+ "high-contrast": {
7379
+ id: "high-contrast",
7380
+ label: "High Contrast",
7381
+ description: "Maximum-contrast palette for accessibility or emphasis",
7382
+ mode: "dark",
7383
+ promptHint: "high contrast, accessible, accessibility, maximum contrast, bright neon",
7384
+ nodeText: "#000000",
7385
+ canvasText: "#FFFFFF",
7386
+ roles: {
7387
+ boundary: {
7388
+ fill: "#000000",
7389
+ stroke: "#FFFFFF"
7390
+ },
7391
+ client: {
7392
+ fill: "#FFD400",
7393
+ stroke: "#000000"
7394
+ },
7395
+ service: {
7396
+ fill: "#00E5FF",
7397
+ stroke: "#000000"
7398
+ },
7399
+ data: {
7400
+ fill: "#00FF85",
7401
+ stroke: "#000000"
7402
+ },
7403
+ messaging: {
7404
+ fill: "#FF6EC7",
7405
+ stroke: "#000000"
7406
+ },
7407
+ neutral: {
7408
+ fill: "#FFFFFF",
7409
+ stroke: "#000000"
7410
+ }
7411
+ },
7412
+ edge: {
7413
+ stroke: "#FFFFFF",
7414
+ strokeWidth: 2.5,
7415
+ labelBackground: "#000000",
7416
+ labelText: "#FFFFFF",
7417
+ emphasizedStroke: "#FFD400",
7418
+ emphasizedStrokeWidth: 4.5
7419
+ }
7420
+ },
7421
+ blueprint: {
7422
+ id: "blueprint",
7423
+ label: "Blueprint",
7424
+ description: "Monochrome technical blueprint — one blue hue, thin uniform lines, near-transparent fills",
7425
+ mode: "dark",
7426
+ promptHint: "blueprint, technical, monochrome blue, schematic, precise, no decorative color, engineering drawing",
7427
+ nodeText: "#CFE3F5",
7428
+ canvasText: "#5B8FBF",
7429
+ roles: {
7430
+ boundary: {
7431
+ fill: "#0B1929",
7432
+ stroke: "#3D6A94"
7433
+ },
7434
+ client: {
7435
+ fill: "#0E1D30",
7436
+ stroke: "#5B8FBF"
7437
+ },
7438
+ service: {
7439
+ fill: "#0E1D30",
7440
+ stroke: "#4A7BAA"
7441
+ },
7442
+ data: {
7443
+ fill: "#0E1D30",
7444
+ stroke: "#6FA8D6"
7445
+ },
7446
+ messaging: {
7447
+ fill: "#0E1D30",
7448
+ stroke: "#3D6A94"
7449
+ },
7450
+ neutral: {
7451
+ fill: "#0E1D30",
7452
+ stroke: "#4A7BAA"
7453
+ }
7454
+ },
7455
+ edge: {
7456
+ stroke: "#3D6A94",
7457
+ strokeWidth: 1,
7458
+ labelBackground: "#0B1929",
7459
+ labelText: "#CFE3F5",
7460
+ emphasizedStroke: "#6FA8D6",
7461
+ emphasizedStrokeWidth: 2
7462
+ }
7463
+ },
7464
+ ocean: {
7465
+ id: "ocean",
7466
+ label: "Ocean",
7467
+ description: "Cool dark blues and teals",
7468
+ mode: "dark",
7469
+ promptHint: "ocean, cool, blue, teal, calm water, deep sea",
7470
+ nodeText: "#EAF6FA",
7471
+ canvasText: "#7FD9EE",
7472
+ roles: {
7473
+ boundary: {
7474
+ fill: "#0B1F2A",
7475
+ stroke: "#2E5A6E"
7476
+ },
7477
+ client: {
7478
+ fill: "#0F2E3D",
7479
+ stroke: "#4FB8D6"
7480
+ },
7481
+ service: {
7482
+ fill: "#0F3A3A",
7483
+ stroke: "#35C7B0"
7484
+ },
7485
+ data: {
7486
+ fill: "#10293F",
7487
+ stroke: "#3B7DD8"
7488
+ },
7489
+ messaging: {
7490
+ fill: "#172B3D",
7491
+ stroke: "#6E93C7"
7492
+ },
7493
+ neutral: {
7494
+ fill: "#10222C",
7495
+ stroke: "#4FB8D6"
7496
+ }
7497
+ },
7498
+ edge: {
7499
+ stroke: "#4FB8D6",
7500
+ strokeWidth: 2,
7501
+ labelBackground: "#0B1F2A",
7502
+ labelText: "#DCEEF5",
7503
+ emphasizedStroke: "#35C7B0",
7504
+ emphasizedStrokeWidth: 3.5
7505
+ }
7506
+ }
7507
+ };
7508
+ function getTheme(themeId) {
7509
+ if (themeId && THEME_REGISTRY[themeId]) return THEME_REGISTRY[themeId];
7510
+ return THEME_REGISTRY[DEFAULT_THEME_ID];
7511
+ }
7512
+ function buildThemeCatalogPromptContext() {
7513
+ return Object.values(THEME_REGISTRY).map((t) => `- "${t.id}" (${t.mode}): ${t.description}. Matches requests like: ${t.promptHint}.`).join("\n");
7514
+ }
7515
+ export { AstToSqlGenerator, AstToUiConverter, BasicDetailsSchema, C4_COLORS, C4_LAYOUT, ComponentInputType, DEFAULT_THEME_ID, DialectIdSchema, DocumentCollectionSchema, DocumentIndexSchema, DynamoAttributeSchema, DynamoEditorSchema, DynamoGsiSchema, EditorSupportedDialectSchema, JsonEditorSchema, MongoCollectionSchema, MongoEditorSchema, MongoIndexFieldSchema, MongoIndexSchema, MongoNestedFieldSchema, NODE_TYPE_REGISTRY, NestedFieldSchema, NoSqlFieldSchema, SEQUENCE_LAYOUT, SHAPE_IDS, STROKE_STYLES, SqlToAstParser, THEME_REGISTRY, THEME_ROLES, buildMetaData, buildNodeStyleDataPatch, buildThemeCatalogPromptContext, computeDiagramSyncHash, contextSchema, convertC4MermaidToReactFlow, convertC4ToReactFlow, convertDynamoSchemaToAst, convertJsonSchemaToAst, convertMermaidToReactFlow, convertMermaidToReactFlowWithContext, convertMongoSchemaToAst, convertNoSQLToAst, convertReactFlowToC4Mermaid, convertReactFlowToC4UiGraph, convertReactFlowToSequenceMermaid, convertReactFlowToSequenceUiGraph, convertUiGraphToMermaid, estimateSequenceMessageBoxSize, flattenMetaData, generateTableNodeId, generateUUID, getC4ElementColors, getNodeTypeSpec, getTheme, isC4Diagram, isC4ReactFlowDiagram, isGeometryEditableNodeType, isSequenceDiagram, parseC4Diagram, sanitizeMermaidLabels, syncBaseData };