@uigraph/sdk 1.2.12 → 1.2.14

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.cjs CHANGED
@@ -2743,7 +2743,7 @@ function convertC4ToReactFlow(data) {
2743
2743
  sourceHandle: `source-${side.source}`,
2744
2744
  targetHandle: `target-${side.target}`,
2745
2745
  label,
2746
- type: "simplebezier",
2746
+ type: "smoothstep",
2747
2747
  style: {
2748
2748
  stroke: lineColor,
2749
2749
  strokeWidth: (_style$lineWidth = style === null || style === void 0 ? void 0 : style.lineWidth) !== null && _style$lineWidth !== void 0 ? _style$lineWidth : 1.5
@@ -2856,10 +2856,27 @@ function resolvePortalNodeType(hasImageUrl, tag) {
2856
2856
  if (tag && TAG_TO_NODE_TYPE[tag]) return TAG_TO_NODE_TYPE[tag];
2857
2857
  return "shape";
2858
2858
  }
2859
+ const COMPOUND_SHAPE_DELIMITERS = {
2860
+ circle: {
2861
+ open: "((",
2862
+ close: "))"
2863
+ },
2864
+ stadium: {
2865
+ open: "([",
2866
+ close: "])"
2867
+ },
2868
+ cylinder: {
2869
+ open: "[(",
2870
+ close: ")]"
2871
+ },
2872
+ subroutine: {
2873
+ open: "[[",
2874
+ close: "]]"
2875
+ }
2876
+ };
2859
2877
  function getNodeShape(nodeDefinition) {
2860
2878
  if (nodeDefinition.includes("{") && nodeDefinition.includes("}")) return "diamond";
2861
- if (nodeDefinition.includes("((") && nodeDefinition.includes("))")) return "circle";
2862
- if (nodeDefinition.includes("([") && nodeDefinition.includes("])")) return "stadium";
2879
+ for (const [shape, { open, close }] of Object.entries(COMPOUND_SHAPE_DELIMITERS)) if (nodeDefinition.includes(open) && nodeDefinition.includes(close)) return shape;
2863
2880
  if (nodeDefinition.includes("[") && nodeDefinition.includes("]")) return "rect";
2864
2881
  if (nodeDefinition.includes("(") && nodeDefinition.includes(")")) return "round";
2865
2882
  return "rect";
@@ -2909,15 +2926,35 @@ function parseMermaidCode(code) {
2909
2926
  }
2910
2927
  const lines = cleanCode.split("\n");
2911
2928
  const subgraphStack = [];
2929
+ const SHAPE_DELIMITER_PAIRS = [
2930
+ ["[", "]"],
2931
+ ["(", ")"],
2932
+ ["{", "}"]
2933
+ ];
2934
+ function stripWrappingShapeDelimiters(label) {
2935
+ let result = label;
2936
+ let changed = true;
2937
+ while (changed && result.length >= 2) {
2938
+ changed = false;
2939
+ for (const [open, close] of SHAPE_DELIMITER_PAIRS) if (result[0] === open && result[result.length - 1] === close) {
2940
+ const stripped = result.slice(1, -1).trim().replace(new RegExp("^\"(.*)\"$", "s"), "$1").replace(new RegExp("^'(.*)'$", "s"), "$1").trim();
2941
+ if (!stripped) break;
2942
+ result = stripped;
2943
+ changed = true;
2944
+ break;
2945
+ }
2946
+ }
2947
+ return result;
2948
+ }
2912
2949
  function enhancedCleanLabel(label) {
2913
- return label.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]*>/g, "").replace(/\\u([0-9a-fA-F]{4})/g, (match, code$1) => {
2950
+ return stripWrappingShapeDelimiters(label.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]*>/g, "").replace(/\\u([0-9a-fA-F]{4})/g, (match, code$1) => {
2914
2951
  try {
2915
2952
  return String.fromCharCode(parseInt(code$1, 16));
2916
2953
  } catch (_unused) {
2917
2954
  debugLog(`Warning: Could not parse unicode character: ${match}`);
2918
2955
  return match;
2919
2956
  }
2920
- }).replace(/\\n/g, "\n").replace(/\s*\n\s*/g, "\n").trim();
2957
+ }).replace(/\\n/g, "\n").replace(/\s*\n\s*/g, "\n").trim());
2921
2958
  }
2922
2959
  debugLog("Pre-scanning for node definitions...");
2923
2960
  lines.forEach((line, lineIndex) => {
@@ -2963,11 +3000,13 @@ function parseMermaidCode(code) {
2963
3000
  if (shapeDef) {
2964
3001
  const shape = getNodeShape(fullDef);
2965
3002
  let rawLabel = nodeId;
2966
- const labelContentMatch = shapeDef.match(new RegExp("^[\\[\\(\\{](.*)[\\]\\)\\}]$", "s"));
2967
- if (labelContentMatch) {
2968
- rawLabel = labelContentMatch[1];
2969
- rawLabel = rawLabel.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
3003
+ const compoundDelimiters = COMPOUND_SHAPE_DELIMITERS[shape];
3004
+ if (compoundDelimiters && shapeDef.startsWith(compoundDelimiters.open) && shapeDef.endsWith(compoundDelimiters.close)) rawLabel = shapeDef.slice(compoundDelimiters.open.length, shapeDef.length - compoundDelimiters.close.length);
3005
+ else {
3006
+ const labelContentMatch = shapeDef.match(new RegExp("^[\\[\\(\\{](.*)[\\]\\)\\}]$", "s"));
3007
+ if (labelContentMatch) rawLabel = labelContentMatch[1];
2970
3008
  }
3009
+ rawLabel = rawLabel.replace(new RegExp("^\"(.*)\"$", "s"), "$1").replace(new RegExp("^'(.*)'$", "s"), "$1");
2971
3010
  const label = enhancedCleanLabel(rawLabel);
2972
3011
  nodeDefinitions.set(nodeId, {
2973
3012
  label,
@@ -3124,7 +3163,16 @@ function parseMermaidCode(code) {
3124
3163
  const openChar = openCharMatch[1];
3125
3164
  const openPos = idx + rest.indexOf(openChar);
3126
3165
  const closeChar = openChar === "[" ? "]" : openChar === "(" ? ")" : "}";
3127
- const closePos = str.indexOf(closeChar, openPos + 1);
3166
+ let depth = 0;
3167
+ let closePos = -1;
3168
+ for (let pos = openPos; pos < str.length; pos++) if (str[pos] === openChar) depth++;
3169
+ else if (str[pos] === closeChar) {
3170
+ depth--;
3171
+ if (depth === 0) {
3172
+ closePos = pos;
3173
+ break;
3174
+ }
3175
+ }
3128
3176
  if (closePos !== -1) return {
3129
3177
  id,
3130
3178
  full: str.slice(startIndex + idMatch[0].search(/\S/), closePos + 1).trim(),
@@ -3137,101 +3185,106 @@ function parseMermaidCode(code) {
3137
3185
  endIndex: idx
3138
3186
  };
3139
3187
  }
3188
+ const arrowHeads = [
3189
+ "-.->",
3190
+ "-->",
3191
+ "==>",
3192
+ "->>",
3193
+ "<->",
3194
+ "-<>",
3195
+ "<-",
3196
+ "->"
3197
+ ];
3140
3198
  function parseEdge(str) {
3141
3199
  try {
3200
+ const hops = [];
3142
3201
  let i$2 = 0;
3143
- const src = extractToken(str, i$2);
3202
+ let src = extractToken(str, i$2);
3144
3203
  if (!src) return null;
3145
3204
  i$2 = src.endIndex;
3146
- while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3147
- const arrowHeads = [
3148
- "-.->",
3149
- "-->",
3150
- "==>",
3151
- "->>",
3152
- "<->",
3153
- "-<>",
3154
- "<-",
3155
- "->"
3156
- ];
3157
- let foundArrowIndex = -1;
3158
- let foundArrow = "";
3159
- for (const ah of arrowHeads) {
3160
- const idx = str.indexOf(ah, i$2);
3161
- if (idx !== -1 && (foundArrowIndex === -1 || idx < foundArrowIndex)) {
3162
- foundArrowIndex = idx;
3163
- foundArrow = ah;
3164
- }
3165
- }
3166
- let op = null;
3167
- let edgeLabel = "";
3168
- if (foundArrowIndex !== -1) {
3169
- const between = str.slice(i$2, foundArrowIndex);
3170
- const prePipeMatch = between.match(/\|(.*?)\|/);
3171
- if (prePipeMatch) edgeLabel = prePipeMatch[1];
3172
- else {
3173
- const inline = between.replace(/^\s*[\-\.=:\~]+\s*/g, "").replace(/\s*[\-\.=:\~]+\s*$/g, "").trim();
3174
- if (inline) edgeLabel = inline;
3175
- }
3176
- op = foundArrow;
3177
- i$2 = foundArrowIndex + foundArrow.length;
3205
+ while (true) {
3178
3206
  while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3179
- if (str[i$2] === "|") {
3180
- const next = str.indexOf("|", i$2 + 1);
3181
- if (next !== -1) {
3182
- edgeLabel = str.slice(i$2 + 1, next);
3183
- i$2 = next + 1;
3207
+ let foundArrowIndex = -1;
3208
+ let foundArrow = "";
3209
+ for (const ah of arrowHeads) {
3210
+ const idx = str.indexOf(ah, i$2);
3211
+ if (idx !== -1 && (foundArrowIndex === -1 || idx < foundArrowIndex)) {
3212
+ foundArrowIndex = idx;
3213
+ foundArrow = ah;
3184
3214
  }
3185
3215
  }
3186
- } else {
3187
- for (const o of [
3188
- "---",
3189
- "-.-",
3190
- "::",
3191
- ":-:",
3192
- "...",
3193
- "~",
3194
- "==="
3195
- ].sort((a, b) => b.length - a.length)) if (str.startsWith(o, i$2)) {
3196
- op = o;
3197
- i$2 += o.length;
3198
- break;
3199
- }
3200
- if (!op) return null;
3201
- while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3202
- if (str[i$2] === "|") {
3203
- const next = str.indexOf("|", i$2 + 1);
3204
- if (next !== -1) {
3205
- edgeLabel = str.slice(i$2 + 1, next);
3206
- i$2 = next + 1;
3216
+ let op = null;
3217
+ let edgeLabel = "";
3218
+ if (foundArrowIndex !== -1) {
3219
+ const between = str.slice(i$2, foundArrowIndex);
3220
+ const prePipeMatch = between.match(/\|(.*?)\|/);
3221
+ if (prePipeMatch) edgeLabel = prePipeMatch[1];
3222
+ else {
3223
+ const inline = between.replace(/^\s*[\-\.=:\~]+\s*/g, "").replace(/\s*[\-\.=:\~]+\s*$/g, "").trim();
3224
+ if (inline) edgeLabel = inline;
3207
3225
  }
3226
+ op = foundArrow;
3227
+ i$2 = foundArrowIndex + foundArrow.length;
3228
+ while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3229
+ if (str[i$2] === "|") {
3230
+ const next = str.indexOf("|", i$2 + 1);
3231
+ if (next !== -1) {
3232
+ edgeLabel = str.slice(i$2 + 1, next);
3233
+ i$2 = next + 1;
3234
+ }
3235
+ }
3236
+ } else if (hops.length === 0) {
3237
+ for (const o of [
3238
+ "---",
3239
+ "-.-",
3240
+ "::",
3241
+ ":-:",
3242
+ "...",
3243
+ "~",
3244
+ "==="
3245
+ ].sort((a, b) => b.length - a.length)) if (str.startsWith(o, i$2)) {
3246
+ op = o;
3247
+ i$2 += o.length;
3248
+ break;
3249
+ }
3250
+ if (!op) return null;
3251
+ while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3252
+ if (str[i$2] === "|") {
3253
+ const next = str.indexOf("|", i$2 + 1);
3254
+ if (next !== -1) {
3255
+ edgeLabel = str.slice(i$2 + 1, next);
3256
+ i$2 = next + 1;
3257
+ }
3258
+ }
3259
+ } else break;
3260
+ while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3261
+ const tgt = extractToken(str, i$2);
3262
+ if (!tgt) break;
3263
+ const trailing = str.slice(tgt.endIndex).trimStart();
3264
+ if (!edgeLabel && !trailing.startsWith(":::") && !arrowHeads.some((ah) => trailing.includes(ah))) {
3265
+ const colonLabelMatch = trailing.match(/^:\s*(.+)$/);
3266
+ if (colonLabelMatch) edgeLabel = colonLabelMatch[1].trim();
3208
3267
  }
3268
+ const isReverseArrow = op === "<-";
3269
+ hops.push({
3270
+ sourceId: isReverseArrow ? tgt.id : src.id,
3271
+ sourceFull: isReverseArrow ? tgt.full : src.full,
3272
+ targetId: isReverseArrow ? src.id : tgt.id,
3273
+ targetFull: isReverseArrow ? src.full : tgt.full,
3274
+ edgeType: isReverseArrow ? "->" : op,
3275
+ edgeLabel
3276
+ });
3277
+ src = tgt;
3278
+ i$2 = tgt.endIndex;
3209
3279
  }
3210
- while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
3211
- const tgt = extractToken(str, i$2);
3212
- if (!tgt) return null;
3213
- const trailing = str.slice(tgt.endIndex).trimStart();
3214
- if (!edgeLabel && !trailing.startsWith(":::")) {
3215
- const colonLabelMatch = trailing.match(/^:\s*(.+)$/);
3216
- if (colonLabelMatch) edgeLabel = colonLabelMatch[1].trim();
3217
- }
3218
- const isReverseArrow = op === "<-";
3219
- return {
3220
- sourceId: isReverseArrow ? tgt.id : src.id,
3221
- sourceFull: isReverseArrow ? tgt.full : src.full,
3222
- targetId: isReverseArrow ? src.id : tgt.id,
3223
- targetFull: isReverseArrow ? src.full : tgt.full,
3224
- edgeType: isReverseArrow ? "->" : op,
3225
- edgeLabel
3226
- };
3280
+ return hops.length > 0 ? hops : null;
3227
3281
  } catch (_unused2) {
3228
3282
  return null;
3229
3283
  }
3230
3284
  }
3231
- const parsedEdge = parseEdge(line);
3232
- if (!parsedEdge) debugLog(`Line "${line}" did not match edge pattern - checking for standalone nodes`);
3233
- if (parsedEdge) try {
3234
- const { sourceId, targetId, edgeType, edgeLabel } = parsedEdge;
3285
+ const parsedEdges = parseEdge(line);
3286
+ if (!parsedEdges) debugLog(`Line "${line}" did not match edge pattern - checking for standalone nodes`);
3287
+ if (parsedEdges) for (const { sourceId, targetId, edgeType, edgeLabel } of parsedEdges) try {
3235
3288
  debugLog(`Found edge: ${sourceId} ${edgeType} ${targetId} with label: "${edgeLabel}" in context: ${currentSubgraph || "global"}`);
3236
3289
  const isSourceSubgraph = subgraphMap.has(sourceId);
3237
3290
  const isTargetSubgraph = subgraphMap.has(targetId);
@@ -3251,10 +3304,11 @@ function parseMermaidCode(code) {
3251
3304
  createOrGetNode(targetId, targetSubgraph);
3252
3305
  }
3253
3306
  }
3307
+ const unquotedEdgeLabel = edgeLabel.replace(new RegExp("^\"(.*)\"$", "s"), "$1").replace(new RegExp("^'(.*)'$", "s"), "$1");
3254
3308
  edges.push({
3255
3309
  source: sourceId,
3256
3310
  target: targetId,
3257
- label: enhancedCleanLabel(edgeLabel),
3311
+ label: enhancedCleanLabel(unquotedEdgeLabel),
3258
3312
  type: edgeType,
3259
3313
  isSourceSubgraph,
3260
3314
  isTargetSubgraph
@@ -3310,7 +3364,9 @@ const MERMAID_TO_PORTAL_SHAPE = {
3310
3364
  round: "rounded-rect",
3311
3365
  stadium: "terminator",
3312
3366
  circle: "ellipse",
3313
- diamond: "diamond"
3367
+ diamond: "diamond",
3368
+ cylinder: "cylinder",
3369
+ subroutine: "subroutine"
3314
3370
  };
3315
3371
  const SUBGRAPH_HEADER_HEIGHT = LAYOUT_SPACING.SUBGRAPH_HEADER_HEIGHT;
3316
3372
  const SUBGRAPH_PADDING = LAYOUT_SPACING.SUBGRAPH_PADDING;
@@ -3347,7 +3403,8 @@ function calculateNodeSize(label, shape, isImageNode = false) {
3347
3403
  } catch (_unused) {}
3348
3404
  return text.length * 8;
3349
3405
  }
3350
- const baseWidth = Math.max(...lines.map((line) => Math.ceil(measureLineWidth(line)))) + 30;
3406
+ const maxLineWidth = Math.max(...lines.map((line) => Math.ceil(measureLineWidth(line))));
3407
+ const baseWidth = maxLineWidth + 30;
3351
3408
  const baseHeight = lines.length * 18 + 20;
3352
3409
  const width = Math.max(80, baseWidth + 30);
3353
3410
  const height = Math.max(40, baseHeight + 20);
@@ -3362,6 +3419,27 @@ function calculateNodeSize(label, shape, isImageNode = false) {
3362
3419
  height: size
3363
3420
  };
3364
3421
  }
3422
+ if (shape === "stadium") {
3423
+ const sideInset = Math.max(12, height / 2) * 2;
3424
+ return {
3425
+ width: Math.max(width, maxLineWidth + sideInset + 20),
3426
+ height
3427
+ };
3428
+ }
3429
+ if (shape === "cylinder") {
3430
+ const capInset = Math.max(12, Math.min(height * .18, width * .5) + 8) * 2;
3431
+ return {
3432
+ width,
3433
+ height: Math.max(height, lines.length * 18 + capInset + 10)
3434
+ };
3435
+ }
3436
+ if (shape === "subroutine") {
3437
+ const sideInset = Math.max(24, width * .2);
3438
+ return {
3439
+ width: Math.max(width, maxLineWidth + sideInset + 20),
3440
+ height
3441
+ };
3442
+ }
3365
3443
  return {
3366
3444
  width,
3367
3445
  height
@@ -3630,11 +3708,12 @@ function validateNodeSpacing(nodePositions, subgraphId) {
3630
3708
  if (!hasOverlap) debugLog(`✓ Node spacing validated for subgraph ${subgraphId} - no overlaps detected`);
3631
3709
  }
3632
3710
  function layoutMetaGraph(nodes, edges, subgraphLayouts, direction) {
3711
+ const hasTopLevelSubgraphs = Array.from(subgraphLayouts.values()).some((layout) => !layout.parentId);
3633
3712
  const g = new dagre.default.graphlib.Graph();
3634
3713
  g.setGraph({
3635
3714
  rankdir: direction,
3636
- nodesep: CONTAINER_SEPARATION_HORIZONTAL,
3637
- ranksep: CONTAINER_SEPARATION_VERTICAL,
3715
+ nodesep: hasTopLevelSubgraphs ? CONTAINER_SEPARATION_HORIZONTAL : NODE_SEPARATION_HORIZONTAL,
3716
+ ranksep: hasTopLevelSubgraphs ? CONTAINER_SEPARATION_VERTICAL : NODE_SEPARATION_VERTICAL,
3638
3717
  marginx: META_GRAPH_MARGIN,
3639
3718
  marginy: META_GRAPH_MARGIN,
3640
3719
  ranker: DAGRE_RANKER
@@ -3879,7 +3958,8 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
3879
3958
  ];
3880
3959
  return subgraphColors[index % subgraphColors.length];
3881
3960
  }
3882
- processSubgraphsInHierarchicalOrder(subgraphs).forEach((subgraph, index) => {
3961
+ const orderedSubgraphs = processSubgraphsInHierarchicalOrder(subgraphs);
3962
+ orderedSubgraphs.forEach((subgraph, index) => {
3883
3963
  const layout = subgraphLayouts.get(subgraph.id);
3884
3964
  const position = subgraphPositions.get(subgraph.id);
3885
3965
  if (layout && position) {
@@ -4074,11 +4154,42 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
4074
4154
  zIndex: 1
4075
4155
  });
4076
4156
  });
4157
+ const nodeSubgraphById = new Map(nodes.map((n) => [n.id, n.subgraph]));
4158
+ const meaningfulEdges = edges.filter((edge) => {
4159
+ if (edge.isTargetSubgraph && nodeSubgraphById.get(edge.source) === edge.target) {
4160
+ debugLog(`Dropping edge ${edge.source} -> ${edge.target}: source already lives inside this subgraph`);
4161
+ return false;
4162
+ }
4163
+ if (edge.isSourceSubgraph && nodeSubgraphById.get(edge.target) === edge.source) {
4164
+ debugLog(`Dropping edge ${edge.source} -> ${edge.target}: target already lives inside this subgraph`);
4165
+ return false;
4166
+ }
4167
+ return true;
4168
+ });
4169
+ const nodeAxisPosition = /* @__PURE__ */ new Map();
4170
+ orderedSubgraphs.forEach((subgraph) => {
4171
+ const position = subgraphPositions.get(subgraph.id);
4172
+ if (position) nodeAxisPosition.set(`subgraph-${subgraph.id}`, position);
4173
+ });
4174
+ nodes.forEach((node) => {
4175
+ if (node.subgraph) {
4176
+ const subgraphLayout = subgraphLayouts.get(node.subgraph);
4177
+ const subgraphPosition = subgraphPositions.get(node.subgraph);
4178
+ const nodeLayout = subgraphLayout === null || subgraphLayout === void 0 ? void 0 : subgraphLayout.nodes.get(node.id);
4179
+ if (nodeLayout && subgraphPosition) nodeAxisPosition.set(node.id, {
4180
+ x: subgraphPosition.x + nodeLayout.x,
4181
+ y: subgraphPosition.y + nodeLayout.y
4182
+ });
4183
+ } else {
4184
+ const standalonePos = standalonePositions.get(node.id);
4185
+ if (standalonePos) nodeAxisPosition.set(node.id, standalonePos);
4186
+ }
4187
+ });
4077
4188
  return {
4078
4189
  nodes: reactFlowNodes,
4079
- edges: edges.map((edge, index) => {
4190
+ edges: meaningfulEdges.map((edge, index) => {
4080
4191
  const edgeStyle = { strokeWidth: 2.5 };
4081
- const edgeType = "default";
4192
+ const edgeType = "smoothstep";
4082
4193
  switch (edge.type) {
4083
4194
  case "-->":
4084
4195
  case "->": break;
@@ -4095,6 +4206,22 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
4095
4206
  }
4096
4207
  const sourceId = edge.isSourceSubgraph ? `subgraph-${edge.source}` : edge.source;
4097
4208
  const targetId = edge.isTargetSubgraph ? `subgraph-${edge.target}` : edge.target;
4209
+ const isHorizontalLayout = direction === "LR" || direction === "RL";
4210
+ let sourceHandle = isHorizontalLayout ? "source-right" : "source-bottom";
4211
+ let targetHandle = isHorizontalLayout ? "target-left" : "target-top";
4212
+ const sourcePoint = nodeAxisPosition.get(sourceId);
4213
+ const targetPoint = nodeAxisPosition.get(targetId);
4214
+ if (sourcePoint && targetPoint) {
4215
+ if (isHorizontalLayout) {
4216
+ if (targetPoint.x < sourcePoint.x) {
4217
+ sourceHandle = "source-left";
4218
+ targetHandle = "target-right";
4219
+ }
4220
+ } else if (targetPoint.y < sourcePoint.y) {
4221
+ sourceHandle = "source-top";
4222
+ targetHandle = "target-bottom";
4223
+ }
4224
+ }
4098
4225
  return {
4099
4226
  id: `edge-${edge.source}-${edge.target}-${index}`,
4100
4227
  source: sourceId,
@@ -4114,14 +4241,14 @@ function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgr
4114
4241
  width: 20,
4115
4242
  height: 20
4116
4243
  },
4117
- sourceHandle: direction === "LR" || direction === "RL" ? "source-right" : "source-bottom",
4118
- targetHandle: direction === "LR" || direction === "RL" ? "target-left" : "target-top",
4244
+ sourceHandle,
4245
+ targetHandle,
4119
4246
  zIndex: 0
4120
4247
  };
4121
4248
  })
4122
4249
  };
4123
4250
  }
4124
- function layoutGraph(nodes, edges, subgraphs, direction) {
4251
+ function layoutFlowchart(nodes, edges, subgraphs, direction) {
4125
4252
  debugLog("Starting graph layout with direction:", direction);
4126
4253
  debugLog(`Input: ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs`);
4127
4254
  const subgraphLayouts = layoutSubgraphs(nodes, edges, subgraphs, direction);
@@ -4145,7 +4272,7 @@ function _convertFlowChartToReactFlow() {
4145
4272
  };
4146
4273
  }
4147
4274
  debugLog(`Parsed ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs`);
4148
- const graphedResult = layoutGraph(nodes, edges, subgraphs, direction);
4275
+ const graphedResult = layoutFlowchart(nodes, edges, subgraphs, direction);
4149
4276
  return {
4150
4277
  nodes: graphedResult.nodes.map((node) => _objectSpread2(_objectSpread2({}, node), {}, { data: _objectSpread2(_objectSpread2({}, node.data), {}, { source: "mermaid" }) })),
4151
4278
  edges: graphedResult.edges.map((edge) => _objectSpread2(_objectSpread2({}, edge), {}, { data: _objectSpread2(_objectSpread2({}, edge.data), {}, { source: "mermaid" }) }))
@@ -6902,12 +7029,503 @@ function convertUiGraphToMermaid(input, options) {
6902
7029
  context
6903
7030
  };
6904
7031
  }
7032
+ const SHAPE_IDS = [
7033
+ "rectangle",
7034
+ "rounded-rect",
7035
+ "ellipse",
7036
+ "diamond",
7037
+ "triangle",
7038
+ "parallelogram",
7039
+ "trapezoid",
7040
+ "hexagon",
7041
+ "document",
7042
+ "cylinder",
7043
+ "delay",
7044
+ "off-page-connector",
7045
+ "display",
7046
+ "collate",
7047
+ "sort",
7048
+ "terminator",
7049
+ "or",
7050
+ "database",
7051
+ "multiple-documents",
7052
+ "subroutine",
7053
+ "manual-input",
7054
+ "summing-junction",
7055
+ "internal-storage"
7056
+ ];
7057
+ const STROKE_STYLES = [
7058
+ "solid",
7059
+ "dashed",
7060
+ "dotted"
7061
+ ];
7062
+ function clampNum(value, min, max) {
7063
+ if (value === void 0 || !Number.isFinite(value)) return void 0;
7064
+ return Math.min(max, Math.max(min, value));
7065
+ }
7066
+ function dropUndefined(input) {
7067
+ const output = {};
7068
+ for (const [key, value] of Object.entries(input)) {
7069
+ if (value === void 0) continue;
7070
+ output[key] = value;
7071
+ }
7072
+ return output;
7073
+ }
7074
+ function mapShapeStylePatch(patch) {
7075
+ return dropUndefined({
7076
+ fill: patch.fill,
7077
+ stroke: patch.stroke,
7078
+ strokeWidth: clampNum(patch.strokeWidth, 0, 10),
7079
+ strokeStyle: patch.strokeStyle,
7080
+ cornerRadius: clampNum(patch.cornerRadius, 0, 64),
7081
+ textColor: patch.textColor,
7082
+ textFontSize: clampNum(patch.textFontSize, 8, 48),
7083
+ shape: patch.shape
7084
+ });
7085
+ }
7086
+ function mapCloudStylePatch(patch) {
7087
+ return dropUndefined({
7088
+ fill: patch.fill,
7089
+ stroke: patch.stroke,
7090
+ strokeWidth: clampNum(patch.strokeWidth, 0, 10),
7091
+ strokeStyle: patch.strokeStyle
7092
+ });
7093
+ }
7094
+ function mapTextStylePatch(patch) {
7095
+ return dropUndefined({
7096
+ fill: patch.fill,
7097
+ stroke: patch.stroke,
7098
+ strokeWidth: clampNum(patch.strokeWidth, 0, 10),
7099
+ strokeStyle: patch.strokeStyle,
7100
+ borderRadius: clampNum(patch.cornerRadius, 0, 64),
7101
+ color: patch.textColor,
7102
+ fontSize: clampNum(patch.textFontSize, 8, 48)
7103
+ });
7104
+ }
7105
+ function mapGroupStylePatch(patch) {
7106
+ return dropUndefined({
7107
+ backgroundColor: patch.fill,
7108
+ borderColor: patch.stroke
7109
+ });
7110
+ }
7111
+ function mapC4StylePatch(patch) {
7112
+ return dropUndefined({
7113
+ fill: patch.fill,
7114
+ stroke: patch.stroke,
7115
+ fontColor: patch.textColor
7116
+ });
7117
+ }
7118
+ function mapC4BoundaryStylePatch(patch) {
7119
+ return dropUndefined({
7120
+ backgroundColor: patch.fill,
7121
+ borderColor: patch.stroke,
7122
+ fontColor: patch.textColor
7123
+ });
7124
+ }
7125
+ function mapSequenceParticipantStylePatch(patch) {
7126
+ return dropUndefined({
7127
+ color: patch.stroke,
7128
+ textColor: patch.textColor
7129
+ });
7130
+ }
7131
+ const NODE_TYPE_REGISTRY = {
7132
+ shape: {
7133
+ tag: "shape",
7134
+ diagramTypes: ["flowchart"],
7135
+ isContainer: false,
7136
+ geometryEditable: true,
7137
+ shapes: SHAPE_IDS,
7138
+ mapStylePatch: mapShapeStylePatch
7139
+ },
7140
+ default: {
7141
+ tag: "default",
7142
+ diagramTypes: ["flowchart"],
7143
+ isContainer: false,
7144
+ geometryEditable: true,
7145
+ shapes: SHAPE_IDS,
7146
+ mapStylePatch: mapShapeStylePatch
7147
+ },
7148
+ cloud: {
7149
+ tag: "cloud",
7150
+ diagramTypes: ["flowchart"],
7151
+ isContainer: false,
7152
+ geometryEditable: true,
7153
+ mapStylePatch: mapCloudStylePatch
7154
+ },
7155
+ text: {
7156
+ tag: "text",
7157
+ diagramTypes: ["flowchart"],
7158
+ isContainer: false,
7159
+ geometryEditable: true,
7160
+ mapStylePatch: mapTextStylePatch
7161
+ },
7162
+ group: {
7163
+ tag: "group",
7164
+ diagramTypes: ["flowchart"],
7165
+ isContainer: true,
7166
+ geometryEditable: false,
7167
+ mapStylePatch: mapGroupStylePatch
7168
+ },
7169
+ c4: {
7170
+ tag: "c4",
7171
+ diagramTypes: ["c4"],
7172
+ isContainer: false,
7173
+ geometryEditable: true,
7174
+ mapStylePatch: mapC4StylePatch
7175
+ },
7176
+ c4Boundary: {
7177
+ tag: "c4Boundary",
7178
+ diagramTypes: ["c4"],
7179
+ isContainer: true,
7180
+ geometryEditable: false,
7181
+ mapStylePatch: mapC4BoundaryStylePatch
7182
+ },
7183
+ sequenceParticipant: {
7184
+ tag: "sequenceParticipant",
7185
+ diagramTypes: ["sequence"],
7186
+ isContainer: false,
7187
+ geometryEditable: false,
7188
+ mapStylePatch: mapSequenceParticipantStylePatch
7189
+ }
7190
+ };
7191
+ function getNodeTypeSpec(type) {
7192
+ if (type === void 0) return void 0;
7193
+ return NODE_TYPE_REGISTRY[type];
7194
+ }
7195
+ function buildNodeStyleDataPatch(type, patch) {
7196
+ var _getNodeTypeSpec$mapS, _getNodeTypeSpec;
7197
+ return (_getNodeTypeSpec$mapS = (_getNodeTypeSpec = getNodeTypeSpec(type)) === null || _getNodeTypeSpec === void 0 ? void 0 : _getNodeTypeSpec.mapStylePatch(patch)) !== null && _getNodeTypeSpec$mapS !== void 0 ? _getNodeTypeSpec$mapS : {};
7198
+ }
7199
+ function isGeometryEditableNodeType(type) {
7200
+ var _getNodeTypeSpec$geom, _getNodeTypeSpec2;
7201
+ return (_getNodeTypeSpec$geom = (_getNodeTypeSpec2 = getNodeTypeSpec(type)) === null || _getNodeTypeSpec2 === void 0 ? void 0 : _getNodeTypeSpec2.geometryEditable) !== null && _getNodeTypeSpec$geom !== void 0 ? _getNodeTypeSpec$geom : false;
7202
+ }
7203
+ const THEME_ROLES = [
7204
+ "boundary",
7205
+ "client",
7206
+ "service",
7207
+ "data",
7208
+ "messaging",
7209
+ "neutral"
7210
+ ];
7211
+ const DEFAULT_THEME_ID = "professional";
7212
+ const THEME_REGISTRY = {
7213
+ professional: {
7214
+ id: "professional",
7215
+ label: "Professional",
7216
+ description: "Calm slate-navy palette, the default look",
7217
+ mode: "dark",
7218
+ promptHint: "clean, modern, professional, calm, corporate, default, no specific style requested",
7219
+ nodeText: "#F8FAFC",
7220
+ canvasText: "#93B4E8",
7221
+ roles: {
7222
+ boundary: {
7223
+ fill: "#171B26",
7224
+ stroke: "#3D4759"
7225
+ },
7226
+ client: {
7227
+ fill: "#1C2336",
7228
+ stroke: "#3D5EA8"
7229
+ },
7230
+ service: {
7231
+ fill: "#1C2336",
7232
+ stroke: "#3D5EA8"
7233
+ },
7234
+ data: {
7235
+ fill: "#18242F",
7236
+ stroke: "#3D8A7A"
7237
+ },
7238
+ messaging: {
7239
+ fill: "#241F30",
7240
+ stroke: "#6B5CA8"
7241
+ },
7242
+ neutral: {
7243
+ fill: "#1C2336",
7244
+ stroke: "#3D5EA8"
7245
+ }
7246
+ },
7247
+ edge: {
7248
+ stroke: "#828DA3",
7249
+ strokeWidth: 1.5,
7250
+ labelBackground: "#1E293B",
7251
+ labelText: "#E2E8F0",
7252
+ emphasizedStroke: "#5B9BFF",
7253
+ emphasizedStrokeWidth: 3
7254
+ }
7255
+ },
7256
+ "midnight-bold": {
7257
+ id: "midnight-bold",
7258
+ label: "Midnight Bold",
7259
+ description: "Dark background, saturated high-contrast accents",
7260
+ mode: "dark",
7261
+ promptHint: "dark, bold, saturated, high contrast, vivid, striking",
7262
+ nodeText: "#F8FAFC",
7263
+ canvasText: "#7FB2FF",
7264
+ roles: {
7265
+ boundary: {
7266
+ fill: "#12151F",
7267
+ stroke: "#3D4759"
7268
+ },
7269
+ client: {
7270
+ fill: "#1C2336",
7271
+ stroke: "#5B9BFF"
7272
+ },
7273
+ service: {
7274
+ fill: "#1C2336",
7275
+ stroke: "#38BDF8"
7276
+ },
7277
+ data: {
7278
+ fill: "#0F2D3A",
7279
+ stroke: "#2AD4B3"
7280
+ },
7281
+ messaging: {
7282
+ fill: "#2A2410",
7283
+ stroke: "#E8B93E"
7284
+ },
7285
+ neutral: {
7286
+ fill: "#1C2336",
7287
+ stroke: "#5B9BFF"
7288
+ }
7289
+ },
7290
+ edge: {
7291
+ stroke: "#5B9BFF",
7292
+ strokeWidth: 2.5,
7293
+ labelBackground: "#1E293B",
7294
+ labelText: "#E2E8F0",
7295
+ emphasizedStroke: "#5B9BFF",
7296
+ emphasizedStrokeWidth: 4
7297
+ }
7298
+ },
7299
+ "soft-pastel": {
7300
+ id: "soft-pastel",
7301
+ label: "Soft Pastel",
7302
+ description: "Dark cards, soft muted pastel-hued borders",
7303
+ mode: "dark",
7304
+ promptHint: "soft, pastel, gentle, muted, airy, whimsical",
7305
+ nodeText: "#F1F5F9",
7306
+ canvasText: "#A8C8E8",
7307
+ roles: {
7308
+ boundary: {
7309
+ fill: "#171B24",
7310
+ stroke: "#7C93B8"
7311
+ },
7312
+ client: {
7313
+ fill: "#1A2233",
7314
+ stroke: "#8FD9C4"
7315
+ },
7316
+ service: {
7317
+ fill: "#1A2233",
7318
+ stroke: "#93B8F0"
7319
+ },
7320
+ data: {
7321
+ fill: "#1F1A2E",
7322
+ stroke: "#E3A98C"
7323
+ },
7324
+ messaging: {
7325
+ fill: "#241F14",
7326
+ stroke: "#E8D48A"
7327
+ },
7328
+ neutral: {
7329
+ fill: "#1C2029",
7330
+ stroke: "#A8B3C4"
7331
+ }
7332
+ },
7333
+ edge: {
7334
+ stroke: "#8FA3C4",
7335
+ strokeWidth: 2,
7336
+ labelBackground: "#1E293B",
7337
+ labelText: "#E8EDF5",
7338
+ emphasizedStroke: "#8FD9C4",
7339
+ emphasizedStrokeWidth: 3.5
7340
+ }
7341
+ },
7342
+ monochrome: {
7343
+ id: "monochrome",
7344
+ label: "Monochrome",
7345
+ description: "Grayscale, roles differ by shade rather than hue",
7346
+ mode: "dark",
7347
+ promptHint: "monochrome, grayscale, black and white, minimal, neutral",
7348
+ nodeText: "#F3F4F6",
7349
+ canvasText: "#D1D5DB",
7350
+ roles: {
7351
+ boundary: {
7352
+ fill: "#17191D",
7353
+ stroke: "#4B5563"
7354
+ },
7355
+ client: {
7356
+ fill: "#23262B",
7357
+ stroke: "#9CA3AF"
7358
+ },
7359
+ service: {
7360
+ fill: "#1D2024",
7361
+ stroke: "#6B7280"
7362
+ },
7363
+ data: {
7364
+ fill: "#26292E",
7365
+ stroke: "#D1D5DB"
7366
+ },
7367
+ messaging: {
7368
+ fill: "#202225",
7369
+ stroke: "#4B5563"
7370
+ },
7371
+ neutral: {
7372
+ fill: "#1F2226",
7373
+ stroke: "#6B7280"
7374
+ }
7375
+ },
7376
+ edge: {
7377
+ stroke: "#9CA3AF",
7378
+ strokeWidth: 2,
7379
+ labelBackground: "#111318",
7380
+ labelText: "#E5E7EB",
7381
+ emphasizedStroke: "#F3F4F6",
7382
+ emphasizedStrokeWidth: 3.5
7383
+ }
7384
+ },
7385
+ "high-contrast": {
7386
+ id: "high-contrast",
7387
+ label: "High Contrast",
7388
+ description: "Maximum-contrast palette for accessibility or emphasis",
7389
+ mode: "dark",
7390
+ promptHint: "high contrast, accessible, accessibility, maximum contrast, bright neon",
7391
+ nodeText: "#000000",
7392
+ canvasText: "#FFFFFF",
7393
+ roles: {
7394
+ boundary: {
7395
+ fill: "#000000",
7396
+ stroke: "#FFFFFF"
7397
+ },
7398
+ client: {
7399
+ fill: "#FFD400",
7400
+ stroke: "#000000"
7401
+ },
7402
+ service: {
7403
+ fill: "#00E5FF",
7404
+ stroke: "#000000"
7405
+ },
7406
+ data: {
7407
+ fill: "#00FF85",
7408
+ stroke: "#000000"
7409
+ },
7410
+ messaging: {
7411
+ fill: "#FF6EC7",
7412
+ stroke: "#000000"
7413
+ },
7414
+ neutral: {
7415
+ fill: "#FFFFFF",
7416
+ stroke: "#000000"
7417
+ }
7418
+ },
7419
+ edge: {
7420
+ stroke: "#FFFFFF",
7421
+ strokeWidth: 2.5,
7422
+ labelBackground: "#000000",
7423
+ labelText: "#FFFFFF",
7424
+ emphasizedStroke: "#FFD400",
7425
+ emphasizedStrokeWidth: 4.5
7426
+ }
7427
+ },
7428
+ blueprint: {
7429
+ id: "blueprint",
7430
+ label: "Blueprint",
7431
+ description: "Monochrome technical blueprint — one blue hue, thin uniform lines, near-transparent fills",
7432
+ mode: "dark",
7433
+ promptHint: "blueprint, technical, monochrome blue, schematic, precise, no decorative color, engineering drawing",
7434
+ nodeText: "#CFE3F5",
7435
+ canvasText: "#5B8FBF",
7436
+ roles: {
7437
+ boundary: {
7438
+ fill: "#0B1929",
7439
+ stroke: "#3D6A94"
7440
+ },
7441
+ client: {
7442
+ fill: "#0E1D30",
7443
+ stroke: "#5B8FBF"
7444
+ },
7445
+ service: {
7446
+ fill: "#0E1D30",
7447
+ stroke: "#4A7BAA"
7448
+ },
7449
+ data: {
7450
+ fill: "#0E1D30",
7451
+ stroke: "#6FA8D6"
7452
+ },
7453
+ messaging: {
7454
+ fill: "#0E1D30",
7455
+ stroke: "#3D6A94"
7456
+ },
7457
+ neutral: {
7458
+ fill: "#0E1D30",
7459
+ stroke: "#4A7BAA"
7460
+ }
7461
+ },
7462
+ edge: {
7463
+ stroke: "#3D6A94",
7464
+ strokeWidth: 1,
7465
+ labelBackground: "#0B1929",
7466
+ labelText: "#CFE3F5",
7467
+ emphasizedStroke: "#6FA8D6",
7468
+ emphasizedStrokeWidth: 2
7469
+ }
7470
+ },
7471
+ ocean: {
7472
+ id: "ocean",
7473
+ label: "Ocean",
7474
+ description: "Cool dark blues and teals",
7475
+ mode: "dark",
7476
+ promptHint: "ocean, cool, blue, teal, calm water, deep sea",
7477
+ nodeText: "#EAF6FA",
7478
+ canvasText: "#7FD9EE",
7479
+ roles: {
7480
+ boundary: {
7481
+ fill: "#0B1F2A",
7482
+ stroke: "#2E5A6E"
7483
+ },
7484
+ client: {
7485
+ fill: "#0F2E3D",
7486
+ stroke: "#4FB8D6"
7487
+ },
7488
+ service: {
7489
+ fill: "#0F3A3A",
7490
+ stroke: "#35C7B0"
7491
+ },
7492
+ data: {
7493
+ fill: "#10293F",
7494
+ stroke: "#3B7DD8"
7495
+ },
7496
+ messaging: {
7497
+ fill: "#172B3D",
7498
+ stroke: "#6E93C7"
7499
+ },
7500
+ neutral: {
7501
+ fill: "#10222C",
7502
+ stroke: "#4FB8D6"
7503
+ }
7504
+ },
7505
+ edge: {
7506
+ stroke: "#4FB8D6",
7507
+ strokeWidth: 2,
7508
+ labelBackground: "#0B1F2A",
7509
+ labelText: "#DCEEF5",
7510
+ emphasizedStroke: "#35C7B0",
7511
+ emphasizedStrokeWidth: 3.5
7512
+ }
7513
+ }
7514
+ };
7515
+ function getTheme(themeId) {
7516
+ if (themeId && THEME_REGISTRY[themeId]) return THEME_REGISTRY[themeId];
7517
+ return THEME_REGISTRY[DEFAULT_THEME_ID];
7518
+ }
7519
+ function buildThemeCatalogPromptContext() {
7520
+ return Object.values(THEME_REGISTRY).map((t) => `- "${t.id}" (${t.mode}): ${t.description}. Matches requests like: ${t.promptHint}.`).join("\n");
7521
+ }
6905
7522
  exports.AstToSqlGenerator = AstToSqlGenerator;
6906
7523
  exports.AstToUiConverter = AstToUiConverter;
6907
7524
  exports.BasicDetailsSchema = BasicDetailsSchema;
6908
7525
  exports.C4_COLORS = C4_COLORS;
6909
7526
  exports.C4_LAYOUT = C4_LAYOUT;
6910
7527
  exports.ComponentInputType = require_component_type.ComponentInputType;
7528
+ exports.DEFAULT_THEME_ID = DEFAULT_THEME_ID;
6911
7529
  exports.DialectIdSchema = DialectIdSchema;
6912
7530
  exports.DocumentCollectionSchema = DocumentCollectionSchema;
6913
7531
  exports.DocumentIndexSchema = DocumentIndexSchema;
@@ -6921,11 +7539,18 @@ exports.MongoEditorSchema = MongoEditorSchema;
6921
7539
  exports.MongoIndexFieldSchema = MongoIndexFieldSchema;
6922
7540
  exports.MongoIndexSchema = MongoIndexSchema;
6923
7541
  exports.MongoNestedFieldSchema = MongoNestedFieldSchema;
7542
+ exports.NODE_TYPE_REGISTRY = NODE_TYPE_REGISTRY;
6924
7543
  exports.NestedFieldSchema = NestedFieldSchema;
6925
7544
  exports.NoSqlFieldSchema = NoSqlFieldSchema;
6926
7545
  exports.SEQUENCE_LAYOUT = SEQUENCE_LAYOUT;
7546
+ exports.SHAPE_IDS = SHAPE_IDS;
7547
+ exports.STROKE_STYLES = STROKE_STYLES;
6927
7548
  exports.SqlToAstParser = SqlToAstParser;
7549
+ exports.THEME_REGISTRY = THEME_REGISTRY;
7550
+ exports.THEME_ROLES = THEME_ROLES;
6928
7551
  exports.buildMetaData = buildMetaData;
7552
+ exports.buildNodeStyleDataPatch = buildNodeStyleDataPatch;
7553
+ exports.buildThemeCatalogPromptContext = buildThemeCatalogPromptContext;
6929
7554
  exports.computeDiagramSyncHash = computeDiagramSyncHash;
6930
7555
  exports.contextSchema = require_headless.contextSchema;
6931
7556
  exports.convertC4MermaidToReactFlow = convertC4MermaidToReactFlow;
@@ -6946,9 +7571,13 @@ exports.flattenMetaData = flattenMetaData;
6946
7571
  exports.generateTableNodeId = generateTableNodeId;
6947
7572
  exports.generateUUID = generateUUID;
6948
7573
  exports.getC4ElementColors = getC4ElementColors;
7574
+ exports.getNodeTypeSpec = getNodeTypeSpec;
7575
+ exports.getTheme = getTheme;
6949
7576
  exports.isC4Diagram = isC4Diagram;
6950
7577
  exports.isC4ReactFlowDiagram = isC4ReactFlowDiagram;
7578
+ exports.isGeometryEditableNodeType = isGeometryEditableNodeType;
6951
7579
  exports.isSequenceDiagram = isSequenceDiagram;
7580
+ exports.layoutFlowchart = layoutFlowchart;
6952
7581
  exports.parseC4Diagram = parseC4Diagram;
6953
7582
  exports.sanitizeMermaidLabels = sanitizeMermaidLabels;
6954
7583
  exports.syncBaseData = syncBaseData;