@ktrysmt/beautiful-mermaid 1.4.2 → 1.4.3

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.js CHANGED
@@ -263,8 +263,11 @@ function measureMultilineText(text, fontSize, fontWeight) {
263
263
 
264
264
  // src/multiline-utils.ts
265
265
  function normalizeBrTags(label) {
266
- const unquoted = label.startsWith('"') && label.endsWith('"') ? label.slice(1, -1) : label;
267
- return unquoted.replace(/<br\s*\/?>/gi, "\n").replace(/\\n/g, "\n").replace(/<\/?(?:sub|sup|small|mark)\s*>/gi, "").replace(/\*\*(.+?)\*\*/g, "<b>$1</b>").replace(/(?<!\*)\*([^\s*](?:[^*]*[^\s*])?)\*(?!\*)/g, "<i>$1</i>").replace(/~~(.+?)~~/g, "<s>$1</s>");
266
+ let result = label.replace(/<br\s*\/?>/gi, "\n").replace(/\\n/g, "\n").trim();
267
+ if (result.startsWith('"') && result.endsWith('"')) {
268
+ result = result.slice(1, -1);
269
+ }
270
+ return result.replace(/<\/?(?:sub|sup|small|mark)\s*>/gi, "").replace(/\*\*(.+?)\*\*/g, "<b>$1</b>").replace(/(?<!\*)\*([^\s*](?:[^*]*[^\s*])?)\*(?!\*)/g, "<i>$1</i>").replace(/~~(.+?)~~/g, "<s>$1</s>").trim();
268
271
  }
269
272
  function escapeXml(text) {
270
273
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -336,8 +339,43 @@ ${textEl}`;
336
339
  }
337
340
 
338
341
  // src/parser.ts
342
+ function joinMultilineDefinitions(lines) {
343
+ const result = [];
344
+ let buffer = "";
345
+ let bracketDepth = 0;
346
+ let parenDepth = 0;
347
+ let inQuote = false;
348
+ for (const line of lines) {
349
+ if (bracketDepth === 0 && parenDepth === 0 && !inQuote) {
350
+ buffer = line;
351
+ } else {
352
+ if (/<br\s*\/?>\s*$/i.test(buffer)) {
353
+ buffer += line;
354
+ } else {
355
+ buffer += "<br>" + line;
356
+ }
357
+ }
358
+ for (const ch of line) {
359
+ if (ch === '"') {
360
+ inQuote = !inQuote;
361
+ } else if (!inQuote) {
362
+ if (ch === "[") bracketDepth++;
363
+ else if (ch === "]") bracketDepth = Math.max(0, bracketDepth - 1);
364
+ else if (ch === "(") parenDepth++;
365
+ else if (ch === ")") parenDepth = Math.max(0, parenDepth - 1);
366
+ }
367
+ }
368
+ if (bracketDepth === 0 && parenDepth === 0 && !inQuote) {
369
+ result.push(buffer);
370
+ buffer = "";
371
+ }
372
+ }
373
+ if (buffer) result.push(buffer);
374
+ return result;
375
+ }
339
376
  function parseMermaid(text) {
340
- const lines = text.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("%%"));
377
+ const rawLines = text.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("%%"));
378
+ const lines = joinMultilineDefinitions(rawLines);
341
379
  if (lines.length === 0) {
342
380
  throw new Error("Empty mermaid diagram");
343
381
  }
@@ -1691,7 +1729,7 @@ function isFreeInGrid(grid, c) {
1691
1729
  return !grid.has(gridKey(c));
1692
1730
  }
1693
1731
  var MAX_ITERATIONS = 5e4;
1694
- function getPath(grid, from, to) {
1732
+ function getPath(grid, from, to, congestion) {
1695
1733
  const pq = new MinHeap();
1696
1734
  pq.push({ coord: from, priority: 0 });
1697
1735
  const costSoFar = /* @__PURE__ */ new Map();
@@ -1719,7 +1757,8 @@ function getPath(grid, from, to) {
1719
1757
  if (!isFreeInGrid(grid, next) && !gridCoordEquals(next, to)) {
1720
1758
  continue;
1721
1759
  }
1722
- const newCost = currentCost + 1;
1760
+ const congestionPenalty = congestion?.get(gridKey(next)) ?? 0;
1761
+ const newCost = currentCost + 1 + congestionPenalty * 3;
1723
1762
  const nextKey = gridKey(next);
1724
1763
  const existingCost = costSoFar.get(nextKey);
1725
1764
  if (existingCost === void 0 || newCost < existingCost) {
@@ -1863,17 +1902,17 @@ function determineStartAndEndDir(edge, graphDirection) {
1863
1902
  }
1864
1903
  return [preferredDir, preferredOppositeDir, alternativeDir, alternativeOppositeDir];
1865
1904
  }
1866
- function determinePath(graph, edge) {
1905
+ function determinePath(graph, edge, congestion) {
1867
1906
  const sourceSg = getNodeSubgraph(graph, edge.from);
1868
1907
  const targetSg = getNodeSubgraph(graph, edge.to);
1869
1908
  const effectiveDir = sourceSg && sourceSg === targetSg && sourceSg.direction ? sourceSg.direction : graph.config.graphDirection;
1870
1909
  const [preferredDir, preferredOppositeDir, alternativeDir, alternativeOppositeDir] = determineStartAndEndDir(edge, effectiveDir);
1871
1910
  const prefFrom = gridCoordDirection(edge.from.gridCoord, preferredDir);
1872
1911
  const prefTo = gridCoordDirection(edge.to.gridCoord, preferredOppositeDir);
1873
- let preferredPath = getPath(graph.grid, prefFrom, prefTo);
1912
+ let preferredPath = getPath(graph.grid, prefFrom, prefTo, congestion);
1874
1913
  const altFrom = gridCoordDirection(edge.from.gridCoord, alternativeDir);
1875
1914
  const altTo = gridCoordDirection(edge.to.gridCoord, alternativeOppositeDir);
1876
- let alternativePath = getPath(graph.grid, altFrom, altTo);
1915
+ let alternativePath = getPath(graph.grid, altFrom, altTo, congestion);
1877
1916
  if (preferredPath !== null && alternativePath !== null) {
1878
1917
  preferredPath = mergePath(preferredPath);
1879
1918
  alternativePath = mergePath(alternativePath);
@@ -1904,11 +1943,41 @@ function determinePath(graph, edge) {
1904
1943
  edge.endDir = preferredOppositeDir;
1905
1944
  edge.path = [prefFrom, prefTo];
1906
1945
  }
1946
+ function pathToCells(path) {
1947
+ const cells = [];
1948
+ for (let i = 0; i < path.length - 1; i++) {
1949
+ const p1 = path[i];
1950
+ const p2 = path[i + 1];
1951
+ if (p1.x === p2.x) {
1952
+ const minY = Math.min(p1.y, p2.y);
1953
+ const maxY = Math.max(p1.y, p2.y);
1954
+ for (let y = minY; y <= maxY; y++) {
1955
+ cells.push({ x: p1.x, y });
1956
+ }
1957
+ } else {
1958
+ const minX = Math.min(p1.x, p2.x);
1959
+ const maxX = Math.max(p1.x, p2.x);
1960
+ for (let x = minX; x <= maxX; x++) {
1961
+ cells.push({ x, y: p1.y });
1962
+ }
1963
+ }
1964
+ }
1965
+ return cells;
1966
+ }
1967
+ function segmentMidpointKey(line) {
1968
+ const isVertical = line[0].x === line[1].x;
1969
+ if (isVertical) {
1970
+ const mid = Math.floor((line[0].y + line[1].y) / 2);
1971
+ return `v:${line[0].x}:${mid}`;
1972
+ } else {
1973
+ const mid = Math.floor((line[0].x + line[1].x) / 2);
1974
+ return `h:${line[0].y}:${mid}`;
1975
+ }
1976
+ }
1907
1977
  function determineLabelLine(graph, edge) {
1908
1978
  if (edge.text.length === 0) return;
1909
1979
  const lenLabel = maxLineWidth(edge.text);
1910
1980
  const pathLen = edge.path.length;
1911
- const isVerticalFlow = graph.config.graphDirection === "TD";
1912
1981
  const segments = [];
1913
1982
  for (let i = 1; i < pathLen; i++) {
1914
1983
  const p1 = edge.path[i - 1];
@@ -1918,21 +1987,26 @@ function determineLabelLine(graph, edge) {
1918
1987
  const isVertical = p1.x === p2.x;
1919
1988
  segments.push({ line, width, index: i, isVertical });
1920
1989
  }
1921
- const suitableSegments = segments.filter((s) => s.width >= lenLabel && s.index > 1);
1990
+ const used = graph.usedLabelMidpoints ?? /* @__PURE__ */ new Set();
1991
+ const middleIdx = Math.floor(pathLen / 2);
1992
+ const scoreSeg = (s) => {
1993
+ const key = segmentMidpointKey(s.line);
1994
+ let score = 0;
1995
+ if (!used.has(key)) score += 1e3;
1996
+ if (s.index > 1) score += 100;
1997
+ score -= Math.abs(s.index - middleIdx);
1998
+ return score;
1999
+ };
2000
+ const candidates = segments.filter((s) => s.width >= lenLabel);
1922
2001
  let largestLine;
1923
- if (suitableSegments.length > 0) {
1924
- suitableSegments.sort((a, b) => b.index - a.index);
1925
- largestLine = suitableSegments[0].line;
2002
+ if (candidates.length > 0) {
2003
+ candidates.sort((a, b) => scoreSeg(b) - scoreSeg(a));
2004
+ largestLine = candidates[0].line;
1926
2005
  } else {
1927
- const fallbackSegments = segments.filter((s) => s.width >= lenLabel);
1928
- if (fallbackSegments.length > 0) {
1929
- fallbackSegments.sort((a, b) => b.index - a.index);
1930
- largestLine = fallbackSegments[0].line;
1931
- } else {
1932
- segments.sort((a, b) => b.width - a.width);
1933
- largestLine = segments[0]?.line ?? [edge.path[0], edge.path[1]];
1934
- }
2006
+ segments.sort((a, b) => b.width - a.width);
2007
+ largestLine = segments[0]?.line ?? [edge.path[0], edge.path[1]];
1935
2008
  }
2009
+ used.add(segmentMidpointKey(largestLine));
1936
2010
  const minX = Math.min(largestLine[0].x, largestLine[1].x);
1937
2011
  const maxX = Math.max(largestLine[0].x, largestLine[1].x);
1938
2012
  const middleX = minX + Math.floor((maxX - minX) / 2);
@@ -3815,7 +3889,14 @@ function createMapping(graph) {
3815
3889
  const parentSg = getNodeSubgraph(graph, node);
3816
3890
  const childSg = getNodeSubgraph(graph, child);
3817
3891
  const edgeDir = parentSg && parentSg === childSg && parentSg.direction ? parentSg.direction : graph.config.graphDirection;
3818
- let childLevel = edgeDir === "LR" ? gc.x + 4 : gc.y + 4;
3892
+ let maxPredLevel = edgeDir === "LR" ? gc.x : gc.y;
3893
+ for (const e of graph.edges) {
3894
+ if (e.to.name === child.name && e.from.gridCoord !== null) {
3895
+ const predLevel = edgeDir === "LR" ? e.from.gridCoord.x : e.from.gridCoord.y;
3896
+ if (predLevel > maxPredLevel) maxPredLevel = predLevel;
3897
+ }
3898
+ }
3899
+ let childLevel = maxPredLevel + 4;
3819
3900
  if (childSg && parentSg !== childSg && deferredRoots.size > 0) {
3820
3901
  let placedDeferred = false;
3821
3902
  for (const dr of [...deferredRoots]) {
@@ -3880,17 +3961,44 @@ function createMapping(graph) {
3880
3961
  for (const node of graph.nodes) {
3881
3962
  setColumnWidth(graph, node);
3882
3963
  }
3964
+ const labeledInDegree = /* @__PURE__ */ new Map();
3965
+ for (const edge of graph.edges) {
3966
+ if (edge.text.length > 0) {
3967
+ labeledInDegree.set(edge.to.name, (labeledInDegree.get(edge.to.name) ?? 0) + 1);
3968
+ }
3969
+ }
3970
+ for (const node of graph.nodes) {
3971
+ const deg = labeledInDegree.get(node.name) ?? 0;
3972
+ if (deg >= 3 && node.gridCoord) {
3973
+ const approachRow = node.gridCoord.y - 1;
3974
+ if (approachRow >= 0) {
3975
+ const extra = Math.min((deg - 2) * 2, 10);
3976
+ const current = graph.rowHeight.get(approachRow) ?? 0;
3977
+ graph.rowHeight.set(approachRow, Math.max(current, current + extra));
3978
+ }
3979
+ }
3980
+ }
3883
3981
  graph.bundles = analyzeEdgeBundles(graph);
3884
3982
  processBundles(graph);
3983
+ graph.usedLabelMidpoints = /* @__PURE__ */ new Set();
3984
+ const congestion = /* @__PURE__ */ new Map();
3885
3985
  for (const edge of graph.edges) {
3886
3986
  if (edge.bundle && edge.path.length > 0) {
3887
3987
  increaseGridSizeForPath(graph, edge.path);
3888
3988
  determineLabelLine(graph, edge);
3989
+ for (const cell of pathToCells(edge.path)) {
3990
+ const k = gridKey(cell);
3991
+ congestion.set(k, (congestion.get(k) ?? 0) + 1);
3992
+ }
3889
3993
  continue;
3890
3994
  }
3891
- determinePath(graph, edge);
3995
+ determinePath(graph, edge, congestion);
3892
3996
  increaseGridSizeForPath(graph, edge.path);
3893
3997
  determineLabelLine(graph, edge);
3998
+ for (const cell of pathToCells(edge.path)) {
3999
+ const k = gridKey(cell);
4000
+ congestion.set(k, (congestion.get(k) ?? 0) + 1);
4001
+ }
3894
4002
  }
3895
4003
  for (const node of graph.nodes) {
3896
4004
  node.drawingCoord = gridToDrawingCoord(graph, node.gridCoord);