@diagrammo/dgmo 0.32.1 → 0.34.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.
Files changed (60) hide show
  1. package/.cursorrules +7 -2
  2. package/.github/copilot-instructions.md +7 -2
  3. package/.windsurfrules +7 -2
  4. package/SKILL.md +7 -2
  5. package/dist/advanced.cjs +1434 -443
  6. package/dist/advanced.d.cts +41 -1
  7. package/dist/advanced.d.ts +41 -1
  8. package/dist/advanced.js +1433 -443
  9. package/dist/auto.cjs +1242 -318
  10. package/dist/auto.js +91 -91
  11. package/dist/auto.mjs +1242 -318
  12. package/dist/cli.cjs +138 -136
  13. package/dist/index.cjs +1308 -317
  14. package/dist/index.d.cts +35 -1
  15. package/dist/index.d.ts +35 -1
  16. package/dist/index.js +1307 -317
  17. package/docs/ai-integration.md +7 -2
  18. package/docs/language-reference.md +39 -8
  19. package/package.json +2 -2
  20. package/src/advanced.ts +6 -1
  21. package/src/boxes-and-lines/label-placement.ts +339 -0
  22. package/src/boxes-and-lines/layout-grouped.ts +909 -0
  23. package/src/boxes-and-lines/layout-search.ts +34 -3
  24. package/src/boxes-and-lines/layout.ts +32 -7
  25. package/src/boxes-and-lines/parser.ts +3 -2
  26. package/src/boxes-and-lines/renderer.ts +67 -139
  27. package/src/c4/layout.ts +2 -1
  28. package/src/c4/parser.ts +3 -2
  29. package/src/c4/renderer.ts +3 -2
  30. package/src/cli.ts +68 -0
  31. package/src/er/parser.ts +2 -1
  32. package/src/er/renderer.ts +2 -1
  33. package/src/gantt/parser.ts +3 -5
  34. package/src/gantt/renderer.ts +7 -6
  35. package/src/index.ts +6 -1
  36. package/src/infra/parser.ts +2 -1
  37. package/src/infra/renderer.ts +4 -3
  38. package/src/journey-map/parser.ts +27 -8
  39. package/src/journey-map/renderer.ts +26 -9
  40. package/src/kanban/parser.ts +4 -3
  41. package/src/kanban/renderer.ts +6 -5
  42. package/src/map/completion.ts +116 -0
  43. package/src/map/layout.ts +4 -3
  44. package/src/map/parser.ts +3 -2
  45. package/src/mindmap/parser.ts +4 -3
  46. package/src/mindmap/renderer.ts +2 -1
  47. package/src/org/layout.ts +3 -2
  48. package/src/org/parser.ts +3 -2
  49. package/src/org/renderer.ts +5 -4
  50. package/src/pert/parser.ts +3 -2
  51. package/src/sequence/renderer.ts +10 -12
  52. package/src/sequence/tag-resolution.ts +2 -1
  53. package/src/sitemap/layout.ts +3 -2
  54. package/src/sitemap/parser.ts +36 -2
  55. package/src/sitemap/renderer.ts +12 -6
  56. package/src/timeline/renderer.ts +4 -3
  57. package/src/utils/legend-d3.ts +4 -3
  58. package/src/utils/legend-svg.ts +3 -2
  59. package/src/utils/tag-groups.ts +28 -7
  60. package/src/visualizations/parse.ts +2 -1
package/dist/auto.mjs CHANGED
@@ -1156,6 +1156,9 @@ var init_parsing = __esm({
1156
1156
  });
1157
1157
 
1158
1158
  // src/utils/tag-groups.ts
1159
+ function tagAttrKey(name) {
1160
+ return name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
1161
+ }
1159
1162
  function stripDefaultModifier(text) {
1160
1163
  if (/\bdefault\s*$/.test(text)) {
1161
1164
  return { text: text.replace(/\s+default\s*$/, "").trim(), isDefault: true };
@@ -1301,17 +1304,17 @@ function parseTagDeclaration(line11) {
1301
1304
  function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
1302
1305
  if (!activeGroupName) return void 0;
1303
1306
  const group = tagGroups.find(
1304
- (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
1307
+ (g) => tagAttrKey(g.name) === tagAttrKey(activeGroupName)
1305
1308
  );
1306
1309
  if (!group) return void 0;
1307
- const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
1310
+ const metaValue = metadata[tagAttrKey(group.name)] ?? (isContainer ? void 0 : group.defaultValue);
1308
1311
  if (!metaValue) return "#999999";
1309
1312
  return group.entries.find((e) => e.value.toLowerCase() === metaValue.toLowerCase())?.color ?? "#999999";
1310
1313
  }
1311
1314
  function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
1312
1315
  if (tagGroups.length === 0) return;
1313
1316
  const groupMap = /* @__PURE__ */ new Map();
1314
- for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
1317
+ for (const g of tagGroups) groupMap.set(tagAttrKey(g.name), g);
1315
1318
  for (const entity of entities) {
1316
1319
  for (const [key, value] of Object.entries(entity.metadata)) {
1317
1320
  const group = groupMap.get(key);
@@ -1348,10 +1351,10 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1348
1351
  `'none' is a reserved keyword and cannot be used as a tag group name`
1349
1352
  );
1350
1353
  }
1351
- if (!VALID_TAG_IDENT_RE.test(group.name)) {
1354
+ if (!VALID_TAG_IDENT_RE.test(tagAttrKey(group.name))) {
1352
1355
  report(
1353
1356
  group.lineNumber,
1354
- `Tag group name "${group.name}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
1357
+ `Tag group name "${group.name}" can't form a valid key \u2014 use letters, digits, underscore, or hyphen, and quote a multi-word name (e.g. tag "Trust Zone" as tz)`
1355
1358
  );
1356
1359
  }
1357
1360
  if (group.alias != null && !VALID_TAG_IDENT_RE.test(group.alias)) {
@@ -1363,7 +1366,7 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1363
1366
  }
1364
1367
  }
1365
1368
  function cascadeTagMetadata(roots, tagGroups) {
1366
- const keys = tagGroups.map((g) => g.name.toLowerCase());
1369
+ const keys = tagGroups.map((g) => tagAttrKey(g.name));
1367
1370
  if (keys.length === 0) return;
1368
1371
  const walk = (node, inherited) => {
1369
1372
  const childInherited = { ...inherited };
@@ -1384,7 +1387,7 @@ function injectDefaultTagMetadata(entities, tagGroups, skip) {
1384
1387
  for (const group of tagGroups) {
1385
1388
  if (group.defaultValue) {
1386
1389
  defaults.push({
1387
- key: group.name.toLowerCase(),
1390
+ key: tagAttrKey(group.name),
1388
1391
  value: group.defaultValue
1389
1392
  });
1390
1393
  }
@@ -2775,7 +2778,7 @@ function parseVisualizationFull(content, palette) {
2775
2778
  );
2776
2779
  for (const group of result.timelineTagGroups) {
2777
2780
  if (!group.defaultValue) continue;
2778
- const key = group.name.toLowerCase();
2781
+ const key = tagAttrKey(group.name);
2779
2782
  for (const event of result.timelineEvents) {
2780
2783
  if (!event.metadata[key]) {
2781
2784
  event.metadata[key] = group.defaultValue;
@@ -5167,7 +5170,7 @@ function renderLegendD3(container, config, state, palette, isDark, callbacks, co
5167
5170
  };
5168
5171
  }
5169
5172
  function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, callbacks) {
5170
- const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", capsule.groupName.toLowerCase()).style("cursor", "pointer");
5173
+ const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", tagAttrKey(capsule.groupName)).style("cursor", "pointer");
5171
5174
  g.append("rect").attr("width", capsule.width).attr("height", capsule.height).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
5172
5175
  const pill = capsule.pill;
5173
5176
  g.append("rect").attr("x", pill.x).attr("y", pill.y).attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", palette.bg);
@@ -5175,7 +5178,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5175
5178
  g.append("text").attr("x", pill.x + pill.width / 2).attr("y", LEGEND_HEIGHT / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.text).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(capsule.groupName);
5176
5179
  if (capsule.gradient) {
5177
5180
  const gr = capsule.gradient;
5178
- const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
5181
+ const gradId = `dgmo-legend-ramp-${tagAttrKey(capsule.groupName).replace(/[^a-z0-9]+/g, "-")}`;
5179
5182
  const def = g.append("defs").append("linearGradient").attr("id", gradId);
5180
5183
  for (const stop of valueRampStops(gr.low, gr.high, { isDark })) {
5181
5184
  def.append("stop").attr("offset", `${stop.offset * 100}%`).attr("stop-color", stop.color);
@@ -5205,7 +5208,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5205
5208
  }
5206
5209
  }
5207
5210
  function renderPill(parent, pill, palette, groupBg, callbacks) {
5208
- const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", pill.groupName.toLowerCase()).style("cursor", "pointer");
5211
+ const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", tagAttrKey(pill.groupName)).style("cursor", "pointer");
5209
5212
  g.append("rect").attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", groupBg);
5210
5213
  g.append("text").attr("x", pill.width / 2).attr("y", pill.height / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(pill.groupName);
5211
5214
  if (callbacks?.onGroupToggle) {
@@ -5311,6 +5314,7 @@ var LEGEND_TEXT_DY;
5311
5314
  var init_legend_d3 = __esm({
5312
5315
  "src/utils/legend-d3.ts"() {
5313
5316
  "use strict";
5317
+ init_tag_groups();
5314
5318
  init_legend_constants();
5315
5319
  init_legend_layout();
5316
5320
  init_color_utils();
@@ -9297,7 +9301,7 @@ function parseERDiagram(content, palette) {
9297
9301
  );
9298
9302
  for (const group of result.tagGroups) {
9299
9303
  if (!group.defaultValue) continue;
9300
- const key = group.name.toLowerCase();
9304
+ const key = tagAttrKey(group.name);
9301
9305
  for (const table of result.tables) {
9302
9306
  if (!table.metadata[key]) {
9303
9307
  const mutableMeta = tableMetadataMap.get(table.id);
@@ -9527,12 +9531,12 @@ function parseOrg(content, palette) {
9527
9531
  if (tagBlockMatch.alias) {
9528
9532
  metaAliasMap.set(
9529
9533
  normalizeName(tagBlockMatch.alias),
9530
- tagBlockMatch.name.toLowerCase()
9534
+ tagAttrKey(tagBlockMatch.name)
9531
9535
  );
9532
9536
  }
9533
9537
  metaAliasMap.set(
9534
9538
  normalizeName(tagBlockMatch.name),
9535
- tagBlockMatch.name.toLowerCase()
9539
+ tagAttrKey(tagBlockMatch.name)
9536
9540
  );
9537
9541
  result.tagGroups.push(currentTagGroup);
9538
9542
  continue;
@@ -9918,12 +9922,12 @@ function parseSitemap(content, palette) {
9918
9922
  if (tagBlockMatch.alias) {
9919
9923
  metaAliasMap.set(
9920
9924
  normalizeName(tagBlockMatch.alias),
9921
- tagBlockMatch.name.toLowerCase()
9925
+ tagAttrKey(tagBlockMatch.name)
9922
9926
  );
9923
9927
  }
9924
9928
  metaAliasMap.set(
9925
9929
  normalizeName(tagBlockMatch.name),
9926
- tagBlockMatch.name.toLowerCase()
9930
+ tagAttrKey(tagBlockMatch.name)
9927
9931
  );
9928
9932
  result.tagGroups.push(newTagGroup);
9929
9933
  continue;
@@ -10093,6 +10097,14 @@ function parseSitemap(content, palette) {
10093
10097
  continue;
10094
10098
  }
10095
10099
  }
10100
+ const enclosingContainer = nestedPageInsideContainer(indent, indentStack);
10101
+ if (enclosingContainer) {
10102
+ pushError(
10103
+ lineNumber,
10104
+ `Pages inside container '[${enclosingContainer}]' cannot have indented sub-pages. Containers hold a flat list of pages.`
10105
+ );
10106
+ continue;
10107
+ }
10096
10108
  const node = parseNodeLabel2(
10097
10109
  trimmed,
10098
10110
  lineNumber,
@@ -10240,6 +10252,16 @@ function attachNode2(node, indent, indentStack, result) {
10240
10252
  }
10241
10253
  indentStack.push({ node, indent });
10242
10254
  }
10255
+ function nestedPageInsideContainer(indent, indentStack) {
10256
+ let idx = indentStack.length - 1;
10257
+ while (idx >= 0 && indentStack[idx].indent >= indent) idx--;
10258
+ if (idx < 0) return null;
10259
+ if (indentStack[idx].node.isContainer) return null;
10260
+ for (let k = idx - 1; k >= 0; k--) {
10261
+ if (indentStack[k].node.isContainer) return indentStack[k].node.label;
10262
+ }
10263
+ return null;
10264
+ }
10243
10265
  function findParentNode(indent, indentStack) {
10244
10266
  for (let i = indentStack.length - 1; i >= 0; i--) {
10245
10267
  if (indentStack[i].indent < indent) {
@@ -10823,12 +10845,12 @@ function parsePert(content, parseOpts = {}) {
10823
10845
  if (tagBlockMatch.alias) {
10824
10846
  metaAliasMap.set(
10825
10847
  tagBlockMatch.alias.toLowerCase(),
10826
- tagBlockMatch.name.toLowerCase()
10848
+ tagAttrKey(tagBlockMatch.name)
10827
10849
  );
10828
10850
  }
10829
10851
  metaAliasMap.set(
10830
10852
  normalizeName(tagBlockMatch.name),
10831
- tagBlockMatch.name.toLowerCase()
10853
+ tagAttrKey(tagBlockMatch.name)
10832
10854
  );
10833
10855
  tagGroups.push(currentTagGroup);
10834
10856
  if (tagBlockMatch.inlineValues) {
@@ -12293,10 +12315,10 @@ function emitCapsule(capsule, palette, groupBg, pillBorder) {
12293
12315
  `<g data-legend-entry="${esc(entry.value.toLowerCase())}" data-series-name="${esc(entry.value)}" style="cursor:pointer"><circle cx="${entry.dotCx}" cy="${entry.dotCy}" r="${LEGEND_DOT_R}" fill="${esc(entry.color)}"/><text x="${entry.textX}" y="${entry.dotCy + LEGEND_ENTRY_FONT_SIZE / 2 - 1}" font-size="${LEGEND_ENTRY_FONT_SIZE}" fill="${esc(palette.textMuted)}" font-family="${esc(FONT_FAMILY)}">${esc(label)}</text></g>`
12294
12316
  );
12295
12317
  }
12296
- return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(capsule.groupName.toLowerCase())}" style="cursor:pointer">${inner.join("")}</g>`;
12318
+ return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(tagAttrKey(capsule.groupName))}" style="cursor:pointer">${inner.join("")}</g>`;
12297
12319
  }
12298
12320
  function emitPill(pill, palette, groupBg) {
12299
- return `<g transform="translate(${pill.x},${pill.y})" data-legend-group="${esc(pill.groupName.toLowerCase())}" style="cursor:pointer"><rect width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="${esc(groupBg)}"/><text x="${pill.width / 2}" y="${pill.height / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2}" font-size="${LEGEND_PILL_FONT_SIZE}" font-weight="500" fill="${esc(palette.textMuted)}" text-anchor="middle" font-family="${esc(FONT_FAMILY)}">${esc(pill.groupName)}</text></g>`;
12321
+ return `<g transform="translate(${pill.x},${pill.y})" data-legend-group="${esc(tagAttrKey(pill.groupName))}" style="cursor:pointer"><rect width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="${esc(groupBg)}"/><text x="${pill.width / 2}" y="${pill.height / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2}" font-size="${LEGEND_PILL_FONT_SIZE}" font-weight="500" fill="${esc(palette.textMuted)}" text-anchor="middle" font-family="${esc(FONT_FAMILY)}">${esc(pill.groupName)}</text></g>`;
12300
12322
  }
12301
12323
  function renderLegendSvg(groups, options) {
12302
12324
  if (groups.length === 0) return { svg: "", height: 0, width: 0 };
@@ -12344,6 +12366,7 @@ function renderLegendSvg(groups, options) {
12344
12366
  var init_legend_svg = __esm({
12345
12367
  "src/utils/legend-svg.ts"() {
12346
12368
  "use strict";
12369
+ init_tag_groups();
12347
12370
  init_legend_constants();
12348
12371
  init_legend_layout();
12349
12372
  init_color_utils();
@@ -15052,12 +15075,12 @@ function parseKanban(content, palette) {
15052
15075
  if (tagBlockMatch.alias) {
15053
15076
  metaAliasMap.set(
15054
15077
  normalizeName(tagBlockMatch.alias),
15055
- tagBlockMatch.name.toLowerCase()
15078
+ tagAttrKey(tagBlockMatch.name)
15056
15079
  );
15057
15080
  }
15058
15081
  metaAliasMap.set(
15059
15082
  normalizeName(tagBlockMatch.name),
15060
- tagBlockMatch.name.toLowerCase()
15083
+ tagAttrKey(tagBlockMatch.name)
15061
15084
  );
15062
15085
  result.tagGroups.push(currentTagGroup);
15063
15086
  continue;
@@ -15246,7 +15269,7 @@ function parseKanban(content, palette) {
15246
15269
  }
15247
15270
  for (const group of result.tagGroups) {
15248
15271
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
15249
- tagValueSets.set(group.name.toLowerCase(), values);
15272
+ tagValueSets.set(tagAttrKey(group.name), values);
15250
15273
  }
15251
15274
  for (const col of result.columns) {
15252
15275
  if (col.wipLimit != null && col.cards.length > col.wipLimit) {
@@ -15508,12 +15531,12 @@ function parseC4(content, palette) {
15508
15531
  if (tagBlockMatch.alias) {
15509
15532
  metaAliasMap.set(
15510
15533
  normalizeName(tagBlockMatch.alias),
15511
- tagBlockMatch.name.toLowerCase()
15534
+ tagAttrKey(tagBlockMatch.name)
15512
15535
  );
15513
15536
  }
15514
15537
  metaAliasMap.set(
15515
15538
  normalizeName(tagBlockMatch.name),
15516
- tagBlockMatch.name.toLowerCase()
15539
+ tagAttrKey(tagBlockMatch.name)
15517
15540
  );
15518
15541
  result.tagGroups.push(currentTagGroup);
15519
15542
  continue;
@@ -17196,7 +17219,7 @@ function parseInfra(content) {
17196
17219
  }
17197
17220
  for (const tg of result.tagGroups) {
17198
17221
  if (!tg.defaultValue) continue;
17199
- const key = (tg.alias ?? tg.name).toLowerCase();
17222
+ const key = tagAttrKey(tg.alias ?? tg.name);
17200
17223
  for (const node of result.nodes) {
17201
17224
  if (node.isEdge) continue;
17202
17225
  if (!(key in node.tags)) {
@@ -17333,6 +17356,7 @@ var CONNECTION_RE, SIMPLE_CONNECTION_RE, ASYNC_CONNECTION_RE, ASYNC_SIMPLE_CONNE
17333
17356
  var init_parser15 = __esm({
17334
17357
  "src/infra/parser.ts"() {
17335
17358
  "use strict";
17359
+ init_tag_groups();
17336
17360
  init_diagnostics();
17337
17361
  init_description_helpers();
17338
17362
  init_arrows();
@@ -17878,13 +17902,10 @@ function parseGantt(content, palette) {
17878
17902
  if (tagMatch.alias) {
17879
17903
  metaAliasMap.set(
17880
17904
  normalizeName(tagMatch.alias),
17881
- tagMatch.name.toLowerCase()
17905
+ tagAttrKey(tagMatch.name)
17882
17906
  );
17883
17907
  }
17884
- metaAliasMap.set(
17885
- normalizeName(tagMatch.name),
17886
- tagMatch.name.toLowerCase()
17887
- );
17908
+ metaAliasMap.set(normalizeName(tagMatch.name), tagAttrKey(tagMatch.name));
17888
17909
  continue;
17889
17910
  }
17890
17911
  const topWorkweekMatch = line11.match(WORKWEEK_RE);
@@ -18810,7 +18831,7 @@ function parseMap(content, palette) {
18810
18831
  const aliasMap = /* @__PURE__ */ new Map();
18811
18832
  const open = { tag: null, route: null, poi: null };
18812
18833
  const registry = () => withTagAliases(MAP_REGISTRY, new Set(aliasMap.keys()));
18813
- const tagGroupNames = () => new Set(tagGroups.map((g) => g.name.toLowerCase()));
18834
+ const tagGroupNames = () => new Set(tagGroups.map((g) => tagAttrKey(g.name)));
18814
18835
  const closeBlocks = () => {
18815
18836
  open.tag = null;
18816
18837
  open.route = null;
@@ -18994,7 +19015,7 @@ function parseMap(content, palette) {
18994
19015
  entries: [],
18995
19016
  lineNumber: line11
18996
19017
  };
18997
- if (m.alias) aliasMap.set(m.alias.toLowerCase(), m.name.toLowerCase());
19018
+ if (m.alias) aliasMap.set(m.alias.toLowerCase(), tagAttrKey(m.name));
18998
19019
  tagGroups.push(group);
18999
19020
  open.tag = group;
19000
19021
  if (m.inlineValues?.length) {
@@ -19527,12 +19548,12 @@ function parseBoxesAndLines(content, palette) {
19527
19548
  if (tagBlockMatch.alias) {
19528
19549
  metaAliasMap.set(
19529
19550
  normalizeName(tagBlockMatch.alias),
19530
- tagBlockMatch.name.toLowerCase()
19551
+ tagAttrKey(tagBlockMatch.name)
19531
19552
  );
19532
19553
  }
19533
19554
  metaAliasMap.set(
19534
19555
  normalizeName(tagBlockMatch.name),
19535
- tagBlockMatch.name.toLowerCase()
19556
+ tagAttrKey(tagBlockMatch.name)
19536
19557
  );
19537
19558
  if (tagBlockMatch.inlineValues) {
19538
19559
  for (const rawVal of tagBlockMatch.inlineValues) {
@@ -20273,12 +20294,12 @@ function parseMindmap(content, palette) {
20273
20294
  if (tagBlockMatch.alias) {
20274
20295
  aliasMap.set(
20275
20296
  tagBlockMatch.alias.toLowerCase(),
20276
- tagBlockMatch.name.toLowerCase()
20297
+ tagAttrKey(tagBlockMatch.name)
20277
20298
  );
20278
20299
  }
20279
20300
  aliasMap.set(
20280
- tagBlockMatch.name.toLowerCase(),
20281
- tagBlockMatch.name.toLowerCase()
20301
+ tagAttrKey(tagBlockMatch.name),
20302
+ tagAttrKey(tagBlockMatch.name)
20282
20303
  );
20283
20304
  result.tagGroups.push(currentTagGroup);
20284
20305
  continue;
@@ -21987,7 +22008,18 @@ function parseJourneyMap(content, palette) {
21987
22008
  palette
21988
22009
  ) ?? void 0;
21989
22010
  } else {
21990
- personaName = afterKeyword;
22011
+ const { label, colorName } = peelTrailingColorName(afterKeyword);
22012
+ if (colorName) {
22013
+ personaName = label;
22014
+ personaColor = resolveColorWithDiagnostic(
22015
+ colorName,
22016
+ lineNumber,
22017
+ result.diagnostics,
22018
+ palette
22019
+ ) ?? void 0;
22020
+ } else {
22021
+ personaName = afterKeyword;
22022
+ }
21991
22023
  }
21992
22024
  }
21993
22025
  if (!personaName) {
@@ -22030,12 +22062,12 @@ function parseJourneyMap(content, palette) {
22030
22062
  if (tagBlockMatch.alias) {
22031
22063
  aliasMap.set(
22032
22064
  tagBlockMatch.alias.toLowerCase(),
22033
- tagBlockMatch.name.toLowerCase()
22065
+ tagAttrKey(tagBlockMatch.name)
22034
22066
  );
22035
22067
  }
22036
22068
  aliasMap.set(
22037
- tagBlockMatch.name.toLowerCase(),
22038
- tagBlockMatch.name.toLowerCase()
22069
+ tagAttrKey(tagBlockMatch.name),
22070
+ tagAttrKey(tagBlockMatch.name)
22039
22071
  );
22040
22072
  result.tagGroups.push(currentTagGroup);
22041
22073
  continue;
@@ -22179,7 +22211,7 @@ function parseJourneyMap(content, palette) {
22179
22211
  }
22180
22212
  for (const group of result.tagGroups) {
22181
22213
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
22182
- tagValueSets.set(group.name.toLowerCase(), values);
22214
+ tagValueSets.set(tagAttrKey(group.name), values);
22183
22215
  }
22184
22216
  const allSteps = hasPhases ? result.phases.flatMap((p) => p.steps) : result.steps;
22185
22217
  for (const step of allSteps) {
@@ -24166,7 +24198,7 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
24166
24198
  const groups = [];
24167
24199
  for (const group of tagGroups) {
24168
24200
  if (group.entries.length === 0) continue;
24169
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
24201
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
24170
24202
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
24171
24203
  if (visibleEntries.length === 0) continue;
24172
24204
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -24779,7 +24811,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
24779
24811
  const totalHeight = finalMaxY - minY + MARGIN * 2;
24780
24812
  const usedValuesByGroup = /* @__PURE__ */ new Map();
24781
24813
  for (const group of parsed.tagGroups) {
24782
- const key = group.name.toLowerCase();
24814
+ const key = tagAttrKey(group.name);
24783
24815
  const used = /* @__PURE__ */ new Set();
24784
24816
  const walk = (node) => {
24785
24817
  if (!node.isContainer && node.metadata[key]) {
@@ -24838,6 +24870,7 @@ var init_layout = __esm({
24838
24870
  "src/org/layout.ts"() {
24839
24871
  "use strict";
24840
24872
  init_tag_groups();
24873
+ init_tag_groups();
24841
24874
  init_legend_constants();
24842
24875
  init_text_measure();
24843
24876
  LABEL_FONT_SIZE = 13;
@@ -25198,14 +25231,14 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25198
25231
  const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
25199
25232
  const displayNames = /* @__PURE__ */ new Map();
25200
25233
  for (const group of parsed.tagGroups) {
25201
- displayNames.set(group.name.toLowerCase(), group.name);
25234
+ displayNames.set(tagAttrKey(group.name), group.name);
25202
25235
  }
25203
25236
  const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
25204
25237
  const colorOff = parsed.options?.["color"] === "off";
25205
25238
  for (const c of layout.containers) {
25206
25239
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
25207
25240
  if (activeTagGroup) {
25208
- const tagKey = activeTagGroup.toLowerCase();
25241
+ const tagKey = tagAttrKey(activeTagGroup);
25209
25242
  const metaValue = c.tagMetadata[tagKey];
25210
25243
  if (metaValue) {
25211
25244
  cG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25287,7 +25320,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25287
25320
  if (containerNodeIds.has(node.id)) continue;
25288
25321
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x - node.width / 2}, ${node.y})`).attr("class", "org-node").attr("data-line-number", String(node.lineNumber));
25289
25322
  if (activeTagGroup) {
25290
- const tagKey = activeTagGroup.toLowerCase();
25323
+ const tagKey = tagAttrKey(activeTagGroup);
25291
25324
  const metaValue = node.tagMetadata[tagKey];
25292
25325
  if (metaValue) {
25293
25326
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25460,7 +25493,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25460
25493
  const computedLayout = legendHandle.getLayout();
25461
25494
  if (computedLayout.activeCapsule?.addonX != null) {
25462
25495
  const capsule = computedLayout.activeCapsule;
25463
- const groupKey = capsule.groupName.toLowerCase();
25496
+ const groupKey = tagAttrKey(capsule.groupName);
25464
25497
  const isHidden = hiddenAttributes?.has(groupKey) ?? false;
25465
25498
  const activeGroupEl = legendParentBase.select(
25466
25499
  `[data-legend-group="${groupKey}"]`
@@ -25499,6 +25532,7 @@ var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCESTOR_LABEL_FON
25499
25532
  var init_renderer = __esm({
25500
25533
  "src/org/renderer.ts"() {
25501
25534
  "use strict";
25535
+ init_tag_groups();
25502
25536
  init_fonts();
25503
25537
  init_export_container();
25504
25538
  init_scaling();
@@ -25584,7 +25618,7 @@ function computeLegendGroups2(tagGroups, usedValuesByGroup) {
25584
25618
  const groups = [];
25585
25619
  for (const group of tagGroups) {
25586
25620
  if (group.entries.length === 0) continue;
25587
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
25621
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
25588
25622
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
25589
25623
  if (visibleEntries.length === 0) continue;
25590
25624
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -26064,7 +26098,7 @@ function layoutSitemap(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, e
26064
26098
  totalHeight += MARGIN2;
26065
26099
  const usedValuesByGroup = /* @__PURE__ */ new Map();
26066
26100
  for (const group of parsed.tagGroups) {
26067
- const key = group.name.toLowerCase();
26101
+ const key = tagAttrKey(group.name);
26068
26102
  const used = /* @__PURE__ */ new Set();
26069
26103
  const walk = (node) => {
26070
26104
  if (!node.isContainer && node.metadata[key]) {
@@ -26109,6 +26143,7 @@ var init_layout2 = __esm({
26109
26143
  "src/sitemap/layout.ts"() {
26110
26144
  "use strict";
26111
26145
  init_tag_groups();
26146
+ init_tag_groups();
26112
26147
  init_legend_constants();
26113
26148
  init_text_measure();
26114
26149
  LABEL_FONT_SIZE3 = 13;
@@ -26378,10 +26413,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26378
26413
  const displayNames = /* @__PURE__ */ new Map();
26379
26414
  const tagColors = /* @__PURE__ */ new Map();
26380
26415
  for (const group of parsed.tagGroups) {
26381
- displayNames.set(group.name.toLowerCase(), group.name);
26416
+ displayNames.set(tagAttrKey(group.name), group.name);
26382
26417
  for (const entry of group.entries) {
26383
26418
  tagColors.set(
26384
- `${group.name.toLowerCase()}:${entry.value.toLowerCase()}`,
26419
+ `${tagAttrKey(group.name)}:${entry.value.toLowerCase()}`,
26385
26420
  entry.color
26386
26421
  );
26387
26422
  }
@@ -26398,7 +26433,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26398
26433
  );
26399
26434
  }
26400
26435
  if (activeTagGroup) {
26401
- const tagKey = activeTagGroup.toLowerCase();
26436
+ const tagKey = tagAttrKey(activeTagGroup);
26402
26437
  const tagVal = c.tagMetadata[tagKey];
26403
26438
  if (tagVal) cG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26404
26439
  }
@@ -26461,7 +26496,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26461
26496
  nodeG.style("cursor", "pointer").on("click", () => onClickItem(node.lineNumber));
26462
26497
  }
26463
26498
  if (activeTagGroup) {
26464
- const tagKey = activeTagGroup.toLowerCase();
26499
+ const tagKey = tagAttrKey(activeTagGroup);
26465
26500
  const tagVal = node.tagMetadata[tagKey];
26466
26501
  if (tagVal) nodeG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26467
26502
  }
@@ -26588,7 +26623,7 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
26588
26623
  const computedLayout = legendHandle.getLayout();
26589
26624
  if (computedLayout.activeCapsule?.addonX != null) {
26590
26625
  const capsule = computedLayout.activeCapsule;
26591
- const groupKey = capsule.groupName.toLowerCase();
26626
+ const groupKey = tagAttrKey(capsule.groupName);
26592
26627
  const isHidden = hiddenAttributes?.has(groupKey) ?? false;
26593
26628
  const activeGroupEl = parent.select(`[data-legend-group="${groupKey}"]`);
26594
26629
  if (!activeGroupEl.empty()) {
@@ -26653,6 +26688,7 @@ var DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_
26653
26688
  var init_renderer2 = __esm({
26654
26689
  "src/sitemap/renderer.ts"() {
26655
26690
  "use strict";
26691
+ init_tag_groups();
26656
26692
  init_fonts();
26657
26693
  init_color_utils();
26658
26694
  init_inline_markdown();
@@ -26700,7 +26736,7 @@ function resolveCardTagMeta(card, tagGroups, hiddenMetaGroups) {
26700
26736
  const meta = [];
26701
26737
  for (const group of tagGroups) {
26702
26738
  if (hiddenMetaGroups?.includes(group.name.toLowerCase())) continue;
26703
- const tagValue = card.tags[group.name.toLowerCase()];
26739
+ const tagValue = card.tags[tagAttrKey(group.name)];
26704
26740
  const value = tagValue ?? group.defaultValue;
26705
26741
  if (!value) continue;
26706
26742
  const entry = group.entries.find(
@@ -26720,7 +26756,7 @@ function resolveCardTagColor(card, tagGroups, activeTagGroup) {
26720
26756
  (g) => g.name.toLowerCase() === activeTagGroup.toLowerCase()
26721
26757
  );
26722
26758
  if (!group) return card.color;
26723
- const tagValue = card.tags[group.name.toLowerCase()];
26759
+ const tagValue = card.tags[tagAttrKey(group.name)];
26724
26760
  const value = tagValue ?? group.defaultValue;
26725
26761
  if (!value) return void 0;
26726
26762
  const entry = group.entries.find(
@@ -27030,7 +27066,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
27030
27066
  );
27031
27067
  const cg = g.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27032
27068
  if (activeTagGroup) {
27033
- const tagKey = activeTagGroup.toLowerCase();
27069
+ const tagKey = tagAttrKey(activeTagGroup);
27034
27070
  const tagValue = card.tags[tagKey];
27035
27071
  const group = parsed.tagGroups.find(
27036
27072
  (tg) => tg.name.toLowerCase() === tagKey
@@ -27091,7 +27127,7 @@ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
27091
27127
  return iconG;
27092
27128
  }
27093
27129
  function bucketCardsBySwimlane(columns, swimlaneGroup) {
27094
- const tagKey = swimlaneGroup.name.toLowerCase();
27130
+ const tagKey = tagAttrKey(swimlaneGroup.name);
27095
27131
  const buckets = /* @__PURE__ */ new Map();
27096
27132
  for (const entry of swimlaneGroup.entries) {
27097
27133
  buckets.set(entry.value.toLowerCase(), {
@@ -27379,7 +27415,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
27379
27415
  );
27380
27416
  const cg = parent.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27381
27417
  if (activeTagGroup) {
27382
- const tagKey = activeTagGroup.toLowerCase();
27418
+ const tagKey = tagAttrKey(activeTagGroup);
27383
27419
  const group = tagGroups.find((tg) => tg.name.toLowerCase() === tagKey);
27384
27420
  const value = card.tags[tagKey] ?? group?.defaultValue;
27385
27421
  if (value) {
@@ -27412,6 +27448,7 @@ var DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_M
27412
27448
  var init_renderer3 = __esm({
27413
27449
  "src/kanban/renderer.ts"() {
27414
27450
  "use strict";
27451
+ init_tag_groups();
27415
27452
  init_fonts();
27416
27453
  init_color_utils();
27417
27454
  init_inline_markdown();
@@ -28393,7 +28430,7 @@ function classifyEREntities(tables, relationships) {
28393
28430
  const variance = indegreeValues.reduce((a, b) => a + (b - mean) ** 2, 0) / indegreeValues.length;
28394
28431
  const stddev = Math.sqrt(variance);
28395
28432
  const sorted = [...indegreeValues].sort((a, b) => a - b);
28396
- const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28433
+ const median2 = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28397
28434
  for (const table of tables) {
28398
28435
  const id = table.id;
28399
28436
  const cols = table.columns;
@@ -28434,7 +28471,7 @@ function classifyEREntities(tables, relationships) {
28434
28471
  const nameMatchesLookup = LOOKUP_NAME_SUFFIXES.some(
28435
28472
  (s) => nameLower.endsWith(s)
28436
28473
  );
28437
- if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median) {
28474
+ if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median2) {
28438
28475
  result.set(id, "lookup");
28439
28476
  continue;
28440
28477
  }
@@ -28675,7 +28712,7 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
28675
28712
  seriesColors2[ni % seriesColors2.length];
28676
28713
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "er-table").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
28677
28714
  if (activeTagGroup) {
28678
- const tagKey = activeTagGroup.toLowerCase();
28715
+ const tagKey = tagAttrKey(activeTagGroup);
28679
28716
  const tagValue = node.metadata[tagKey];
28680
28717
  if (tagValue) {
28681
28718
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -28854,6 +28891,7 @@ var DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABE
28854
28891
  var init_renderer5 = __esm({
28855
28892
  "src/er/renderer.ts"() {
28856
28893
  "use strict";
28894
+ init_tag_groups();
28857
28895
  init_fonts();
28858
28896
  init_color_utils();
28859
28897
  init_palettes();
@@ -29024,63 +29062,6 @@ function ensureArrowMarkers(defs, colors) {
29024
29062
  ).attr("fill", color);
29025
29063
  }
29026
29064
  }
29027
- function edgePolylineMidpoint(points) {
29028
- if (points.length === 0) return { x: 0, y: 0 };
29029
- if (points.length === 1) return { x: points[0].x, y: points[0].y };
29030
- let total = 0;
29031
- const segLen = [];
29032
- for (let k = 1; k < points.length; k++) {
29033
- const len = Math.hypot(
29034
- points[k].x - points[k - 1].x,
29035
- points[k].y - points[k - 1].y
29036
- );
29037
- segLen.push(len);
29038
- total += len;
29039
- }
29040
- let half = total / 2;
29041
- for (let k = 1; k < points.length; k++) {
29042
- const len = segLen[k - 1];
29043
- if (half <= len || k === points.length - 1) {
29044
- const t = len === 0 ? 0 : Math.min(1, half / len);
29045
- return {
29046
- x: points[k - 1].x + (points[k].x - points[k - 1].x) * t,
29047
- y: points[k - 1].y + (points[k].y - points[k - 1].y) * t
29048
- };
29049
- }
29050
- half -= len;
29051
- }
29052
- const last = points[points.length - 1];
29053
- return { x: last.x, y: last.y };
29054
- }
29055
- function resolveEdgeLabelOverlaps(labels) {
29056
- const MAX_PASSES = 8;
29057
- const PAD = 4;
29058
- for (let pass = 0; pass < MAX_PASSES; pass++) {
29059
- let moved = false;
29060
- for (let i = 0; i < labels.length; i++) {
29061
- for (let j = i + 1; j < labels.length; j++) {
29062
- const a = labels[i];
29063
- const b = labels[j];
29064
- const dx = Math.abs(a.x - b.x);
29065
- const dy = Math.abs(a.y - b.y);
29066
- const overlapX = (a.width + b.width) / 2 + PAD - dx;
29067
- const overlapY = (a.height + b.height) / 2 + PAD - dy;
29068
- if (overlapX > 0 && overlapY > 0) {
29069
- const shift = overlapY / 2 + 1;
29070
- if (a.y < b.y) {
29071
- a.y -= shift;
29072
- b.y += shift;
29073
- } else {
29074
- a.y += shift;
29075
- b.y -= shift;
29076
- }
29077
- moved = true;
29078
- }
29079
- }
29080
- }
29081
- if (!moved) break;
29082
- }
29083
- }
29084
29065
  function renderBoxesAndLines(container, parsed, layout, palette, isDark, options) {
29085
29066
  const {
29086
29067
  onClickItem,
@@ -29287,8 +29268,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29287
29268
  groupG.append("text").attr("class", "bl-group-label").attr("x", gx + group.width / 2).attr("y", gy + 18).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sGroupLabelFontSize).attr("font-weight", "600").attr("fill", palette.text).text(group.label);
29288
29269
  }
29289
29270
  }
29290
- const labelPositions = [];
29291
- const edgeGroups = /* @__PURE__ */ new Map();
29292
29271
  for (let i = 0; i < layout.edges.length; i++) {
29293
29272
  const le = layout.edges[i];
29294
29273
  const color = edgeColorMap.get(i) ?? palette.textMuted;
@@ -29307,7 +29286,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29307
29286
  const nodeLayout = layoutNodeMap.get(le.source);
29308
29287
  if (nodeLayout) {
29309
29288
  const edgeG2 = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
29310
- edgeGroups.set(i, edgeG2);
29311
29289
  const markerId2 = `bl-arrow-${color.replace("#", "")}`;
29312
29290
  const cx = nodeLayout.x;
29313
29291
  const cy = nodeLayout.y;
@@ -29356,7 +29334,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29356
29334
  }
29357
29335
  if (points.length < 2) continue;
29358
29336
  const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
29359
- edgeGroups.set(i, edgeG);
29360
29337
  const markerId = `bl-arrow-${color.replace("#", "")}`;
29361
29338
  const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
29362
29339
  const path = edgeG.append("path").attr("class", "bl-edge").attr("d", gen(points) ?? "").attr("fill", "none").attr("stroke", color).attr("stroke-width", sEdgeStrokeWidth).attr("marker-end", `url(#${markerId})`);
@@ -29364,31 +29341,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29364
29341
  const revId = `bl-arrow-rev-${color.replace("#", "")}`;
29365
29342
  path.attr("marker-start", `url(#${revId})`);
29366
29343
  }
29367
- if (le.label && le.points.length > 0) {
29368
- const lw = measureText(le.label, sEdgeLabelFontSize);
29369
- const labelH = sEdgeLabelFontSize + 6;
29370
- const mid = edgePolylineMidpoint(le.points);
29371
- let ly = mid.y;
29372
- if (le.parallelCount > 1 && le.yOffset !== 0) {
29373
- ly += (le.yOffset < 0 ? -labelH : labelH) * 0.5;
29374
- }
29375
- labelPositions.push({
29376
- x: mid.x,
29377
- y: ly,
29378
- width: lw + 8,
29379
- height: labelH,
29380
- idx: i
29381
- });
29382
- }
29383
- }
29384
- resolveEdgeLabelOverlaps(labelPositions);
29385
- for (const lp of labelPositions) {
29386
- const le = layout.edges[lp.idx];
29387
- if (!le.label) continue;
29388
- const edgeG = edgeGroups.get(lp.idx);
29389
- const target = edgeG ?? diagramG;
29390
- target.append("rect").attr("x", lp.x - lp.width / 2).attr("y", lp.y - lp.height / 2).attr("width", lp.width).attr("height", lp.height).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
29391
- target.append("text").attr("x", lp.x).attr("y", lp.y + sEdgeLabelFontSize / 3).attr("text-anchor", "middle").attr("font-size", sEdgeLabelFontSize).attr("fill", palette.textMuted).text(le.label);
29392
29344
  }
29393
29345
  for (const ln of layout.nodes) {
29394
29346
  const node = nodeMap.get(ln.label);
@@ -29439,8 +29391,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29439
29391
  nodeG.append("rect").attr("x", x).attr("y", y).attr("width", ln.width).attr("height", ln.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", colors.fill).attr("stroke", colors.stroke).attr("stroke-width", sNodeStrokeWidth);
29440
29392
  const desc = node.description;
29441
29393
  if (desc && desc.length > 0 && !hideDescriptions) {
29442
- const MAX_LABEL_LINES3 = 3;
29443
- const fitted = fitLabelToHeader(node.label, ln.width, MAX_LABEL_LINES3);
29394
+ const MAX_LABEL_LINES4 = 3;
29395
+ const fitted = fitLabelToHeader(node.label, ln.width, MAX_LABEL_LINES4);
29444
29396
  const labelLines = fitted.lines;
29445
29397
  const labelLineH = fitted.fontSize * 1.3;
29446
29398
  const labelTotalH = labelLines.length * labelLineH;
@@ -29574,6 +29526,35 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29574
29526
  }
29575
29527
  }
29576
29528
  }
29529
+ const labelLayer = diagramG.append("g").attr("class", "bl-edge-labels");
29530
+ const labelLineHeight = sEdgeLabelFontSize * 1.3;
29531
+ for (const le of layout.edges) {
29532
+ if (!le.labelLines || le.labelLines.length === 0) continue;
29533
+ if (le.labelX === void 0 || le.labelY === void 0) continue;
29534
+ const lx = le.labelX;
29535
+ const ly = le.labelY;
29536
+ if (hidden.size > 0) {
29537
+ let isHidden = false;
29538
+ for (const [groupKey, hiddenVals] of hidden) {
29539
+ const val = le.metadata[groupKey];
29540
+ if (val && hiddenVals.has(val.toLowerCase())) {
29541
+ isHidden = true;
29542
+ break;
29543
+ }
29544
+ }
29545
+ if (isHidden) continue;
29546
+ }
29547
+ const lw = le.labelWidth ?? 0;
29548
+ const lh = le.labelHeight ?? sEdgeLabelFontSize + 6;
29549
+ const lines = le.labelLines;
29550
+ const labelG = labelLayer.append("g").attr("class", "bl-edge-label").attr("data-line-number", String(le.lineNumber));
29551
+ labelG.append("rect").attr("x", lx - lw / 2).attr("y", ly - lh / 2).attr("width", lw).attr("height", lh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.72);
29552
+ const text = labelG.append("text").attr("x", lx).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sEdgeLabelFontSize).attr("fill", palette.textMuted);
29553
+ const firstY = ly - (lines.length - 1) / 2 * labelLineHeight + sEdgeLabelFontSize / 3;
29554
+ lines.forEach((line11, k) => {
29555
+ text.append("tspan").attr("x", lx).attr("y", firstY + k * labelLineHeight).text(line11);
29556
+ });
29557
+ }
29577
29558
  const hasDescriptions = parsed.nodes.some(
29578
29559
  (n) => n.description && n.description.length > 0
29579
29560
  );
@@ -29688,6 +29669,222 @@ var init_renderer6 = __esm({
29688
29669
  }
29689
29670
  });
29690
29671
 
29672
+ // src/boxes-and-lines/label-placement.ts
29673
+ function measureEdgeLabel(label, fontSize = EDGE_LABEL_FONT_SIZE6, maxWidth = LABEL_MAX_WIDTH) {
29674
+ let lines = wrapTextToWidth(label, fontSize, maxWidth, { hardBreak: true });
29675
+ if (lines.length > MAX_LABEL_LINES) {
29676
+ const kept = lines.slice(0, MAX_LABEL_LINES - 1);
29677
+ const rest = lines.slice(MAX_LABEL_LINES - 1).join(" ");
29678
+ kept.push(truncateText(rest, fontSize, maxWidth));
29679
+ lines = kept;
29680
+ }
29681
+ let textW = 0;
29682
+ for (const ln of lines) {
29683
+ const w = measureText(ln, fontSize);
29684
+ if (w > textW) textW = w;
29685
+ }
29686
+ return {
29687
+ lines,
29688
+ width: textW + 2 * H_PAD,
29689
+ height: lines.length * fontSize * LABEL_LINE_HEIGHT + 2 * V_PAD
29690
+ };
29691
+ }
29692
+ function edgePolylineMidpoint(points) {
29693
+ return pointAtArcFraction(points, 0.5).p;
29694
+ }
29695
+ function pointAtArcFraction(points, t) {
29696
+ if (points.length === 0) return { p: { x: 0, y: 0 }, nx: 0, ny: 1 };
29697
+ if (points.length === 1)
29698
+ return { p: { x: points[0].x, y: points[0].y }, nx: 0, ny: 1 };
29699
+ const segLen = [];
29700
+ let total = 0;
29701
+ for (let k = 1; k < points.length; k++) {
29702
+ const len = Math.hypot(
29703
+ points[k].x - points[k - 1].x,
29704
+ points[k].y - points[k - 1].y
29705
+ );
29706
+ segLen.push(len);
29707
+ total += len;
29708
+ }
29709
+ let want = total * Math.max(0, Math.min(1, t));
29710
+ for (let k = 1; k < points.length; k++) {
29711
+ const len = segLen[k - 1];
29712
+ if (want <= len || k === points.length - 1) {
29713
+ const f = len === 0 ? 0 : Math.min(1, want / len);
29714
+ const ax = points[k - 1].x;
29715
+ const ay = points[k - 1].y;
29716
+ const bx = points[k].x;
29717
+ const by = points[k].y;
29718
+ const dx = bx - ax;
29719
+ const dy = by - ay;
29720
+ const dlen = Math.hypot(dx, dy) || 1;
29721
+ return {
29722
+ p: { x: ax + dx * f, y: ay + dy * f },
29723
+ nx: -dy / dlen,
29724
+ ny: dx / dlen
29725
+ };
29726
+ }
29727
+ want -= len;
29728
+ }
29729
+ const last = points[points.length - 1];
29730
+ return { p: { x: last.x, y: last.y }, nx: 0, ny: 1 };
29731
+ }
29732
+ function rectFromCenter(cx, cy, w, h) {
29733
+ return {
29734
+ minX: cx - w / 2,
29735
+ minY: cy - h / 2,
29736
+ maxX: cx + w / 2,
29737
+ maxY: cy + h / 2
29738
+ };
29739
+ }
29740
+ function overlapsAny(cx, cy, w, h, obstacles, pad2) {
29741
+ const minX = cx - w / 2 - pad2;
29742
+ const minY = cy - h / 2 - pad2;
29743
+ const maxX = cx + w / 2 + pad2;
29744
+ const maxY = cy + h / 2 + pad2;
29745
+ for (const o of obstacles) {
29746
+ if (minX < o.maxX && maxX > o.minX && minY < o.maxY && maxY > o.minY)
29747
+ return true;
29748
+ }
29749
+ return false;
29750
+ }
29751
+ function slideFractions() {
29752
+ const out = [0.5];
29753
+ for (let k = 1; k <= SLIDE_SAMPLES; k++) {
29754
+ const off = k * 0.08;
29755
+ if (0.5 - off >= 0.12) out.push(0.5 - off);
29756
+ if (0.5 + off <= 0.88) out.push(0.5 + off);
29757
+ }
29758
+ return out;
29759
+ }
29760
+ function findClearPosition(w, h, points, obstacles) {
29761
+ const ts = slideFractions();
29762
+ for (const t of ts) {
29763
+ const { p } = pointAtArcFraction(points, t);
29764
+ if (!overlapsAny(p.x, p.y, w, h, obstacles, BOX_CLEAR_PAD)) return p;
29765
+ }
29766
+ for (let mag = PERP_STEP; mag <= PERP_MAX; mag += PERP_STEP) {
29767
+ for (const t of ts) {
29768
+ const { p, nx, ny } = pointAtArcFraction(points, t);
29769
+ for (const sign of [-1, 1]) {
29770
+ const x = p.x + nx * mag * sign;
29771
+ const y = p.y + ny * mag * sign;
29772
+ if (!overlapsAny(x, y, w, h, obstacles, BOX_CLEAR_PAD)) return { x, y };
29773
+ }
29774
+ }
29775
+ }
29776
+ return null;
29777
+ }
29778
+ function resolveLabelOverlaps(boxes) {
29779
+ const MAX_PASSES = 8;
29780
+ const PAD = 4;
29781
+ for (let pass = 0; pass < MAX_PASSES; pass++) {
29782
+ let moved = false;
29783
+ for (let i = 0; i < boxes.length; i++) {
29784
+ for (let j = i + 1; j < boxes.length; j++) {
29785
+ const a = boxes[i];
29786
+ const b = boxes[j];
29787
+ const dx = Math.abs(a.cx - b.cx);
29788
+ const dy = Math.abs(a.cy - b.cy);
29789
+ const overlapX = (a.w + b.w) / 2 + PAD - dx;
29790
+ const overlapY = (a.h + b.h) / 2 + PAD - dy;
29791
+ if (overlapX > 0 && overlapY > 0) {
29792
+ const shift = overlapY / 2 + 1;
29793
+ if (a.cy < b.cy) {
29794
+ a.cy -= shift;
29795
+ b.cy += shift;
29796
+ } else {
29797
+ a.cy += shift;
29798
+ b.cy -= shift;
29799
+ }
29800
+ moved = true;
29801
+ }
29802
+ }
29803
+ }
29804
+ if (!moved) break;
29805
+ }
29806
+ }
29807
+ function placeEdgeLabels(layout, opts) {
29808
+ const fontSize = opts?.fontSize ?? EDGE_LABEL_FONT_SIZE6;
29809
+ const maxWidth = opts?.maxWidth ?? LABEL_MAX_WIDTH;
29810
+ const obstacles = [];
29811
+ for (const n of layout.nodes)
29812
+ obstacles.push(rectFromCenter(n.x, n.y, n.width, n.height));
29813
+ for (const g of layout.groups)
29814
+ if (g.collapsed)
29815
+ obstacles.push(rectFromCenter(g.x, g.y, g.width, g.height));
29816
+ const boxes = [];
29817
+ layout.edges.forEach((e, idx) => {
29818
+ if (!e.label || e.points.length < 2) return;
29819
+ const m = measureEdgeLabel(e.label, fontSize, maxWidth);
29820
+ const mid = edgePolylineMidpoint(e.points);
29821
+ let cy = mid.y;
29822
+ if (e.parallelCount > 1 && e.yOffset !== 0)
29823
+ cy += (e.yOffset < 0 ? -1 : 1) * (m.height / 2);
29824
+ boxes.push({
29825
+ edgeIdx: idx,
29826
+ cx: mid.x,
29827
+ cy,
29828
+ w: m.width,
29829
+ h: m.height,
29830
+ lines: m.lines,
29831
+ resolved: true
29832
+ });
29833
+ });
29834
+ for (const box of boxes) {
29835
+ if (!overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
29836
+ continue;
29837
+ const e = layout.edges[box.edgeIdx];
29838
+ const clear = findClearPosition(box.w, box.h, e.points, obstacles);
29839
+ if (clear) {
29840
+ box.cx = clear.x;
29841
+ box.cy = clear.y;
29842
+ } else {
29843
+ box.resolved = false;
29844
+ }
29845
+ }
29846
+ resolveLabelOverlaps(boxes);
29847
+ for (const box of boxes) {
29848
+ if (overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
29849
+ box.resolved = false;
29850
+ }
29851
+ const boxByIdx = /* @__PURE__ */ new Map();
29852
+ for (const b of boxes) boxByIdx.set(b.edgeIdx, b);
29853
+ const edges = layout.edges.map((e, idx) => {
29854
+ const b = boxByIdx.get(idx);
29855
+ if (!b) return e;
29856
+ return {
29857
+ ...e,
29858
+ labelX: b.cx,
29859
+ labelY: b.cy,
29860
+ labelWidth: b.w,
29861
+ labelHeight: b.h,
29862
+ labelLines: b.lines
29863
+ };
29864
+ });
29865
+ return {
29866
+ layout: { ...layout, edges },
29867
+ unresolved: boxes.filter((b) => !b.resolved).map((b) => b.edgeIdx)
29868
+ };
29869
+ }
29870
+ var EDGE_LABEL_FONT_SIZE6, LABEL_MAX_WIDTH, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, H_PAD, V_PAD, BOX_CLEAR_PAD, PERP_STEP, PERP_MAX, SLIDE_SAMPLES;
29871
+ var init_label_placement = __esm({
29872
+ "src/boxes-and-lines/label-placement.ts"() {
29873
+ "use strict";
29874
+ init_text_measure();
29875
+ EDGE_LABEL_FONT_SIZE6 = 11;
29876
+ LABEL_MAX_WIDTH = 160;
29877
+ MAX_LABEL_LINES = 3;
29878
+ LABEL_LINE_HEIGHT = 1.3;
29879
+ H_PAD = 6;
29880
+ V_PAD = 3;
29881
+ BOX_CLEAR_PAD = 4;
29882
+ PERP_STEP = 8;
29883
+ PERP_MAX = 40;
29884
+ SLIDE_SAMPLES = 9;
29885
+ }
29886
+ });
29887
+
29691
29888
  // src/boxes-and-lines/layout-layered.ts
29692
29889
  function rng(s) {
29693
29890
  return () => {
@@ -29847,7 +30044,7 @@ function layeredCandidates(parsed, sizes, opts) {
29847
30044
  for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
29848
30045
  return c;
29849
30046
  };
29850
- const median = (vals) => {
30047
+ const median2 = (vals) => {
29851
30048
  if (vals.length === 0) return -1;
29852
30049
  vals.sort((x, y) => x - y);
29853
30050
  const m = Math.floor(vals.length / 2);
@@ -29862,7 +30059,7 @@ function layeredCandidates(parsed, sizes, opts) {
29862
30059
  const neigh = useUp ? up : down;
29863
30060
  const med = /* @__PURE__ */ new Map();
29864
30061
  for (const id of ids)
29865
- med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30062
+ med.set(id, median2(neigh.get(id).map((x) => orderOf.get(x))));
29866
30063
  const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
29867
30064
  const out = new Array(ids.length).fill(null);
29868
30065
  for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
@@ -30192,6 +30389,695 @@ var init_layout_layered = __esm({
30192
30389
  }
30193
30390
  });
30194
30391
 
30392
+ // src/boxes-and-lines/layout-grouped.ts
30393
+ function rng2(s) {
30394
+ return () => {
30395
+ s |= 0;
30396
+ s = s + 1831565813 | 0;
30397
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
30398
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
30399
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
30400
+ };
30401
+ }
30402
+ function median(vals) {
30403
+ if (vals.length === 0) return -1;
30404
+ vals.sort((x, y) => x - y);
30405
+ const m = Math.floor(vals.length / 2);
30406
+ if (vals.length % 2 === 1) return vals[m];
30407
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
30408
+ const left = vals[m - 1] - vals[0];
30409
+ const right = vals[vals.length - 1] - vals[m];
30410
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
30411
+ }
30412
+ function acyclicOrient(ids, edges) {
30413
+ const adj = /* @__PURE__ */ new Map();
30414
+ for (const l of ids) adj.set(l, []);
30415
+ for (const e of edges) adj.get(e.from)?.push({ to: e.to, idx: e.idx });
30416
+ const reversed = /* @__PURE__ */ new Set();
30417
+ const state = /* @__PURE__ */ new Map();
30418
+ for (const l of ids) state.set(l, 0);
30419
+ const stack = [];
30420
+ const visit = (root) => {
30421
+ stack.push(root);
30422
+ const it = /* @__PURE__ */ new Map();
30423
+ while (stack.length) {
30424
+ const u = stack[stack.length - 1];
30425
+ if (state.get(u) === 0) state.set(u, 1);
30426
+ const list = adj.get(u);
30427
+ let i = it.get(u) ?? 0;
30428
+ let descended = false;
30429
+ for (; i < list.length; i++) {
30430
+ const { to, idx } = list[i];
30431
+ const st = state.get(to);
30432
+ if (st === 1) reversed.add(idx);
30433
+ else if (st === 0) {
30434
+ it.set(u, i + 1);
30435
+ stack.push(to);
30436
+ descended = true;
30437
+ break;
30438
+ }
30439
+ }
30440
+ if (!descended) {
30441
+ it.set(u, list.length);
30442
+ state.set(u, 2);
30443
+ stack.pop();
30444
+ }
30445
+ }
30446
+ };
30447
+ for (const l of ids) if (state.get(l) === 0) visit(l);
30448
+ const dag = edges.map(
30449
+ (e) => reversed.has(e.idx) ? { from: e.to, to: e.from, idx: e.idx, rev: true } : { from: e.from, to: e.to, idx: e.idx, rev: false }
30450
+ );
30451
+ return { dag };
30452
+ }
30453
+ function longestPath(ids, dag) {
30454
+ const out = /* @__PURE__ */ new Map();
30455
+ const inDeg = /* @__PURE__ */ new Map();
30456
+ for (const l of ids) {
30457
+ out.set(l, []);
30458
+ inDeg.set(l, 0);
30459
+ }
30460
+ for (const d of dag) {
30461
+ out.get(d.from).push(d.to);
30462
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
30463
+ }
30464
+ const rank = /* @__PURE__ */ new Map();
30465
+ for (const l of ids) rank.set(l, 0);
30466
+ const queue = ids.filter((l) => (inDeg.get(l) ?? 0) === 0);
30467
+ const indeg2 = new Map(inDeg);
30468
+ let seen = 0;
30469
+ while (queue.length) {
30470
+ const u = queue.shift();
30471
+ seen++;
30472
+ for (const v of out.get(u)) {
30473
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
30474
+ indeg2.set(v, indeg2.get(v) - 1);
30475
+ if (indeg2.get(v) === 0) queue.push(v);
30476
+ }
30477
+ }
30478
+ return seen === ids.length ? rank : null;
30479
+ }
30480
+ function groupedTierCandidates(parsed, sizes, opts) {
30481
+ if (parsed.groups.length === 0) return [];
30482
+ const nCount = parsed.nodes.length;
30483
+ if (nCount < 3 || nCount > 60) return [];
30484
+ const isTB = parsed.direction === "TB";
30485
+ const labels = parsed.nodes.map((n) => n.label);
30486
+ const labelSet = new Set(labels);
30487
+ for (const e of parsed.edges)
30488
+ if (e.source.startsWith("__group_") || e.target.startsWith("__group_"))
30489
+ return [];
30490
+ const groupByLabel = new Map(parsed.groups.map((g) => [g.label, g]));
30491
+ const directGroupOf = /* @__PURE__ */ new Map();
30492
+ for (const grp of parsed.groups)
30493
+ for (const c of grp.children)
30494
+ if (labelSet.has(c)) directGroupOf.set(c, grp.label);
30495
+ const topGroupOf = (groupLabel) => {
30496
+ let cur = groupLabel;
30497
+ const guard = /* @__PURE__ */ new Set();
30498
+ while (true) {
30499
+ const g = groupByLabel.get(cur);
30500
+ if (!g?.parentGroup || guard.has(cur)) return cur;
30501
+ guard.add(cur);
30502
+ cur = g.parentGroup;
30503
+ }
30504
+ };
30505
+ const pathOf = (label) => {
30506
+ const direct = directGroupOf.get(label);
30507
+ if (!direct) return [];
30508
+ const chain = [direct];
30509
+ let cur = direct;
30510
+ const guard = /* @__PURE__ */ new Set([direct]);
30511
+ while (true) {
30512
+ const g = groupByLabel.get(cur);
30513
+ if (!g?.parentGroup || guard.has(g.parentGroup)) break;
30514
+ chain.push(g.parentGroup);
30515
+ guard.add(g.parentGroup);
30516
+ cur = g.parentGroup;
30517
+ }
30518
+ return chain.reverse();
30519
+ };
30520
+ const SINGLE = "\0node\0";
30521
+ const unitOf = (label) => {
30522
+ const direct = directGroupOf.get(label);
30523
+ return direct ? topGroupOf(direct) : SINGLE + label;
30524
+ };
30525
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
30526
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
30527
+ );
30528
+ if (edges.length === 0) return [];
30529
+ const unitOfNode = /* @__PURE__ */ new Map();
30530
+ for (const l of labels) unitOfNode.set(l, unitOf(l));
30531
+ const units = Array.from(new Set(labels.map((l) => unitOfNode.get(l))));
30532
+ const interUnit = edges.filter(({ e }) => unitOfNode.get(e.source) !== unitOfNode.get(e.target)).map(({ e, i }) => ({
30533
+ from: unitOfNode.get(e.source),
30534
+ to: unitOfNode.get(e.target),
30535
+ idx: i
30536
+ }));
30537
+ const { dag: unitDag } = acyclicOrient(units, interUnit);
30538
+ const tier = longestPath(
30539
+ units,
30540
+ unitDag.map((d) => ({ from: d.from, to: d.to }))
30541
+ );
30542
+ if (!tier) return [];
30543
+ const localRank = /* @__PURE__ */ new Map();
30544
+ const unitMembers = /* @__PURE__ */ new Map();
30545
+ for (const l of labels) {
30546
+ const u = unitOfNode.get(l);
30547
+ (unitMembers.get(u) ?? unitMembers.set(u, []).get(u)).push(l);
30548
+ }
30549
+ for (const [u, members] of unitMembers) {
30550
+ const memberSet = new Set(members);
30551
+ const intra = edges.filter(
30552
+ ({ e }) => unitOfNode.get(e.source) === u && unitOfNode.get(e.target) === u && memberSet.has(e.source) && memberSet.has(e.target)
30553
+ ).map(({ e, i }) => ({ from: e.source, to: e.target, idx: i }));
30554
+ const { dag } = acyclicOrient(members, intra);
30555
+ const lr = longestPath(
30556
+ members,
30557
+ dag.map((d) => ({ from: d.from, to: d.to }))
30558
+ );
30559
+ if (!lr) return [];
30560
+ for (const m of members) localRank.set(m, lr.get(m));
30561
+ }
30562
+ const maxTier = Math.max(...units.map((u) => tier.get(u)));
30563
+ const bandHeight = Array.from({ length: maxTier + 1 }, () => 1);
30564
+ for (const u of units) {
30565
+ const h = Math.max(
30566
+ ...unitMembers.get(u).map((m) => localRank.get(m) + 1)
30567
+ );
30568
+ const t = tier.get(u);
30569
+ bandHeight[t] = Math.max(bandHeight[t], h);
30570
+ }
30571
+ const tierBase = [];
30572
+ let accBase = 0;
30573
+ for (let t = 0; t <= maxTier; t++) {
30574
+ tierBase[t] = accBase;
30575
+ accBase += bandHeight[t];
30576
+ }
30577
+ const tierStartRanks = new Set(tierBase.slice(1));
30578
+ const globalRank = /* @__PURE__ */ new Map();
30579
+ for (const l of labels)
30580
+ globalRank.set(
30581
+ l,
30582
+ tierBase[tier.get(unitOfNode.get(l))] + localRank.get(l)
30583
+ );
30584
+ const maxRank = Math.max(...labels.map((l) => globalRank.get(l)));
30585
+ const node = /* @__PURE__ */ new Map();
30586
+ for (const n of parsed.nodes) {
30587
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30588
+ node.set(n.label, {
30589
+ id: n.label,
30590
+ dummy: false,
30591
+ rank: globalRank.get(n.label),
30592
+ cross: 0,
30593
+ thick: isTB ? s.width : s.height,
30594
+ depth: isTB ? s.height : s.width,
30595
+ realW: s.width,
30596
+ realH: s.height,
30597
+ unit: unitOfNode.get(n.label),
30598
+ path: pathOf(n.label)
30599
+ });
30600
+ }
30601
+ const chains = [];
30602
+ const segs = [];
30603
+ const backEdges = [];
30604
+ const flatEdges = [];
30605
+ for (const { e, i } of edges) {
30606
+ const rs = globalRank.get(e.source);
30607
+ const rt = globalRank.get(e.target);
30608
+ if (rs === rt) {
30609
+ flatEdges.push({ edgeIdx: i, a: e.source, b: e.target });
30610
+ continue;
30611
+ }
30612
+ if (rt < rs) {
30613
+ backEdges.push({ edgeIdx: i, src: e.source, tgt: e.target });
30614
+ continue;
30615
+ }
30616
+ const chain = [e.source];
30617
+ let prev = e.source;
30618
+ for (let r = rs + 1; r < rt; r++) {
30619
+ const id = `__d${i}_${r}`;
30620
+ node.set(id, {
30621
+ id,
30622
+ dummy: true,
30623
+ rank: r,
30624
+ cross: 0,
30625
+ thick: DUMMY_THICK2,
30626
+ depth: 0,
30627
+ realW: 0,
30628
+ realH: 0,
30629
+ unit: SINGLE + id,
30630
+ path: []
30631
+ });
30632
+ segs.push({ a: prev, b: id });
30633
+ chain.push(id);
30634
+ prev = id;
30635
+ }
30636
+ segs.push({ a: prev, b: e.target });
30637
+ chain.push(e.target);
30638
+ chains.push({ edgeIdx: i, chain });
30639
+ }
30640
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
30641
+ for (const n of node.values()) ranks[n.rank].push(n.id);
30642
+ const up = /* @__PURE__ */ new Map();
30643
+ const down = /* @__PURE__ */ new Map();
30644
+ for (const id of node.keys()) {
30645
+ up.set(id, []);
30646
+ down.set(id, []);
30647
+ }
30648
+ for (const s of segs) {
30649
+ down.get(s.a).push(s.b);
30650
+ up.get(s.b).push(s.a);
30651
+ }
30652
+ const orderOf = /* @__PURE__ */ new Map();
30653
+ const gapCrossings = (r) => {
30654
+ const lower = ranks[r + 1];
30655
+ if (!lower) return 0;
30656
+ const es = [];
30657
+ for (const a of ranks[r]) {
30658
+ const pa = orderOf.get(a);
30659
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
30660
+ }
30661
+ let c = 0;
30662
+ for (let i = 0; i < es.length; i++)
30663
+ for (let j = i + 1; j < es.length; j++) {
30664
+ const A = es[i], B = es[j];
30665
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
30666
+ }
30667
+ return c;
30668
+ };
30669
+ const totalCrossings = () => {
30670
+ let c = 0;
30671
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
30672
+ return c;
30673
+ };
30674
+ const wmedianRank = (r, useUp) => {
30675
+ const ids = ranks[r];
30676
+ const neigh = useUp ? up : down;
30677
+ const med = /* @__PURE__ */ new Map();
30678
+ for (const id of ids)
30679
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30680
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
30681
+ const out = new Array(ids.length).fill(null);
30682
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
30683
+ let mi = 0;
30684
+ for (let slot = 0; slot < out.length; slot++)
30685
+ if (out[slot] === null) out[slot] = movable[mi++];
30686
+ const final = out;
30687
+ final.forEach((id, i) => orderOf.set(id, i));
30688
+ ranks[r] = final;
30689
+ };
30690
+ const transpose = () => {
30691
+ let improved = true;
30692
+ let guard = 0;
30693
+ while (improved && guard++ < 12) {
30694
+ improved = false;
30695
+ for (let r = 0; r <= maxRank; r++) {
30696
+ const ids = ranks[r];
30697
+ for (let i = 0; i < ids.length - 1; i++) {
30698
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30699
+ const a = ids[i], b = ids[i + 1];
30700
+ ids[i] = b;
30701
+ ids[i + 1] = a;
30702
+ orderOf.set(b, i);
30703
+ orderOf.set(a, i + 1);
30704
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30705
+ if (after < before) improved = true;
30706
+ else {
30707
+ ids[i] = a;
30708
+ ids[i + 1] = b;
30709
+ orderOf.set(a, i);
30710
+ orderOf.set(b, i + 1);
30711
+ }
30712
+ }
30713
+ }
30714
+ }
30715
+ };
30716
+ const initOrder = (seed) => {
30717
+ const r = seed === 0 ? null : rng2(seed);
30718
+ for (let rr = 0; rr <= maxRank; rr++) {
30719
+ const ids = ranks[rr].slice();
30720
+ if (r)
30721
+ for (let i = ids.length - 1; i > 0; i--) {
30722
+ const j = Math.floor(r() * (i + 1));
30723
+ [ids[i], ids[j]] = [ids[j], ids[i]];
30724
+ }
30725
+ ranks[rr] = ids;
30726
+ ids.forEach((id, i) => orderOf.set(id, i));
30727
+ }
30728
+ };
30729
+ const restarts = opts?.restarts ?? (nCount <= 16 ? 20 : nCount <= 30 ? 12 : 6);
30730
+ const keepK = opts?.keepK ?? 3;
30731
+ const found = [];
30732
+ for (let s = 0; s < restarts; s++) {
30733
+ initOrder(s);
30734
+ let best = totalCrossings();
30735
+ let bestSnap = new Map(orderOf);
30736
+ for (let iter = 0; iter < 8; iter++) {
30737
+ if (iter % 2 === 0)
30738
+ for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
30739
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
30740
+ transpose();
30741
+ const c = totalCrossings();
30742
+ if (c < best) {
30743
+ best = c;
30744
+ bestSnap = new Map(orderOf);
30745
+ }
30746
+ if (c === 0) break;
30747
+ }
30748
+ for (const [k, v] of bestSnap) orderOf.set(k, v);
30749
+ for (let r = 0; r <= maxRank; r++)
30750
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
30751
+ const sig = ranks.map((r) => r.join(",")).join("|");
30752
+ if (!found.some((f) => f.sig === sig))
30753
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
30754
+ }
30755
+ found.sort((a, b) => a.cross - b.cross);
30756
+ const keep = found.slice(0, keepK);
30757
+ if (keep.length === 0) return [];
30758
+ const results = [];
30759
+ for (const cand of keep) {
30760
+ for (const [k, v] of cand.order) orderOf.set(k, v);
30761
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
30762
+ for (const n of node.values()) rk[n.rank].push(n.id);
30763
+ for (let r = 0; r <= maxRank; r++)
30764
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
30765
+ results.push(realize(rk));
30766
+ }
30767
+ return results;
30768
+ function weight(a, b) {
30769
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY2;
30770
+ if (a.dummy || b.dummy) return W_REAL_DUMMY2;
30771
+ return W_REAL_REAL2;
30772
+ }
30773
+ function placeWithSeparation(ids, desired, gap) {
30774
+ const n = ids.length;
30775
+ if (n === 0) return;
30776
+ const off = [0];
30777
+ for (let i = 1; i < n; i++) off[i] = off[i - 1] + gap[i];
30778
+ const d = desired.map((v, i) => v - off[i]);
30779
+ const blocks = [];
30780
+ for (let i = 0; i < n; i++) {
30781
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
30782
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
30783
+ const prev = blocks.pop();
30784
+ const count = prev.count + b.count;
30785
+ const sumOffset = prev.sumOffset + b.sumOffset;
30786
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
30787
+ }
30788
+ blocks.push(b);
30789
+ }
30790
+ const xs = new Array(n);
30791
+ for (const b of blocks)
30792
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
30793
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
30794
+ }
30795
+ function gapBetween(a, b) {
30796
+ const base = a.thick / 2 + b.thick / 2 + NODESEP2;
30797
+ if (a.dummy || b.dummy) return base;
30798
+ if (a.unit !== b.unit) return base + 2 * GROUP_PAD + GROUP_GAP;
30799
+ const da = a.path[a.path.length - 1];
30800
+ const db = b.path[b.path.length - 1];
30801
+ if (da !== db && (da || db)) return base + 2 * GROUP_PAD;
30802
+ return base;
30803
+ }
30804
+ function realize(rk) {
30805
+ const bandDepth = rk.map(
30806
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
30807
+ );
30808
+ const bandCenter = [];
30809
+ let acc = MARGIN4;
30810
+ for (let r = 0; r <= maxRank; r++) {
30811
+ if (r > 0) acc += RANKSEP2 + (tierStartRanks.has(r) ? TIER_GAP : 0);
30812
+ bandCenter[r] = acc + bandDepth[r] / 2;
30813
+ acc += bandDepth[r];
30814
+ }
30815
+ const gapsFor = (ids) => ids.map(
30816
+ (id, i) => i === 0 ? 0 : gapBetween(node.get(ids[i - 1]), node.get(id))
30817
+ );
30818
+ for (let r = 0; r <= maxRank; r++) {
30819
+ const ids = rk[r];
30820
+ const gap = gapsFor(ids);
30821
+ let x = MARGIN4;
30822
+ for (let i = 0; i < ids.length; i++) {
30823
+ const n = node.get(ids[i]);
30824
+ x += gap[i];
30825
+ n.cross = i === 0 ? MARGIN4 + n.thick / 2 : x;
30826
+ }
30827
+ }
30828
+ const ITER = 18;
30829
+ for (let it = 0; it < ITER; it++) {
30830
+ const downPass = it % 2 === 0;
30831
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
30832
+ for (const r of order) {
30833
+ const ids = rk[r];
30834
+ const desired = ids.map((id) => {
30835
+ const n = node.get(id);
30836
+ let sw = 0, sx2 = 0;
30837
+ for (const nb of up.get(id)) {
30838
+ const w = weight(n, node.get(nb));
30839
+ sw += w;
30840
+ sx2 += w * node.get(nb).cross;
30841
+ }
30842
+ for (const nb of down.get(id)) {
30843
+ const w = weight(n, node.get(nb));
30844
+ sw += w;
30845
+ sx2 += w * node.get(nb).cross;
30846
+ }
30847
+ return sw > 0 ? sx2 / sw : n.cross;
30848
+ });
30849
+ placeWithSeparation(ids, desired, gapsFor(ids));
30850
+ }
30851
+ }
30852
+ let minC = Infinity;
30853
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
30854
+ const shiftC = MARGIN4 - minC;
30855
+ for (const n of node.values()) n.cross += shiftC;
30856
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
30857
+ const layoutNodes = parsed.nodes.map((pn) => {
30858
+ const n = node.get(pn.label);
30859
+ const c = coord(n);
30860
+ return {
30861
+ label: pn.label,
30862
+ x: c.x,
30863
+ y: c.y,
30864
+ width: n.realW,
30865
+ height: n.realH
30866
+ };
30867
+ });
30868
+ const chainById = /* @__PURE__ */ new Map();
30869
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
30870
+ let minCross = Infinity, maxCross = -Infinity;
30871
+ for (const n of node.values()) {
30872
+ if (n.dummy) continue;
30873
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
30874
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
30875
+ }
30876
+ const GAP = 34;
30877
+ const LANE = 28;
30878
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
30879
+ const sideOf = (b) => {
30880
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
30881
+ return d >= 0 ? 1 : -1;
30882
+ };
30883
+ const backSorted = backEdges.map((b) => ({
30884
+ ...b,
30885
+ side: sideOf(b),
30886
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
30887
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
30888
+ const sideCounts = /* @__PURE__ */ new Map();
30889
+ for (const b of backSorted)
30890
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
30891
+ const laneCounter = /* @__PURE__ */ new Map();
30892
+ const backPoints = /* @__PURE__ */ new Map();
30893
+ for (const b of backSorted) {
30894
+ const s = b.side;
30895
+ const k = laneCounter.get(s) ?? 0;
30896
+ laneCounter.set(s, k + 1);
30897
+ const K = sideCounts.get(s);
30898
+ const src = node.get(b.src);
30899
+ const tgt = node.get(b.tgt);
30900
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
30901
+ const srcCtr = bandCenter[src.rank];
30902
+ const tgtCtr = bandCenter[tgt.rank];
30903
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
30904
+ const srcR = srcCtr - frac * faceLim(src.depth);
30905
+ const tgtR = tgtCtr + frac * faceLim(tgt.depth);
30906
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30907
+ const off = 10;
30908
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
30909
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
30910
+ backPoints.set(b.edgeIdx, [
30911
+ m(src.cross, srcCtr),
30912
+ m(srcEdgeC, srcR),
30913
+ m(laneC, srcR),
30914
+ m(laneC, tgtR),
30915
+ m(tgtEdgeC, tgtR),
30916
+ m(tgt.cross, tgtCtr)
30917
+ ]);
30918
+ }
30919
+ const flatPoints = /* @__PURE__ */ new Map();
30920
+ for (const f of flatEdges) {
30921
+ const a = node.get(f.a);
30922
+ const b = node.get(f.b);
30923
+ const ctr = bandCenter[a.rank];
30924
+ const bow = ctr - (a.depth / 2 + 26);
30925
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30926
+ flatPoints.set(f.edgeIdx, [
30927
+ m(a.cross, ctr),
30928
+ m(a.cross, bow),
30929
+ m(b.cross, bow),
30930
+ m(b.cross, ctr)
30931
+ ]);
30932
+ }
30933
+ const layoutEdges = [];
30934
+ for (let i = 0; i < parsed.edges.length; i++) {
30935
+ const e = parsed.edges[i];
30936
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
30937
+ let points;
30938
+ if (e.source === e.target) {
30939
+ const c = coord(node.get(e.source));
30940
+ points = [c, c];
30941
+ } else if (backPoints.has(i)) points = backPoints.get(i);
30942
+ else if (flatPoints.has(i)) points = flatPoints.get(i);
30943
+ else {
30944
+ const chain = chainById.get(i);
30945
+ if (!chain) continue;
30946
+ points = chain.map((id) => coord(node.get(id)));
30947
+ }
30948
+ layoutEdges.push({
30949
+ source: e.source,
30950
+ target: e.target,
30951
+ ...e.label !== void 0 && { label: e.label },
30952
+ bidirectional: e.bidirectional,
30953
+ lineNumber: e.lineNumber,
30954
+ points,
30955
+ yOffset: 0,
30956
+ parallelCount: 1,
30957
+ metadata: e.metadata
30958
+ });
30959
+ }
30960
+ const nodeBox = new Map(
30961
+ layoutNodes.map((n) => [
30962
+ n.label,
30963
+ {
30964
+ l: n.x - n.width / 2,
30965
+ r: n.x + n.width / 2,
30966
+ t: n.y - n.height / 2,
30967
+ b: n.y + n.height / 2
30968
+ }
30969
+ ])
30970
+ );
30971
+ const depthOf = (label) => {
30972
+ let d = 0;
30973
+ let cur = label;
30974
+ const guard = /* @__PURE__ */ new Set();
30975
+ while (cur && !guard.has(cur)) {
30976
+ guard.add(cur);
30977
+ cur = groupByLabel.get(cur)?.parentGroup;
30978
+ if (cur) d++;
30979
+ }
30980
+ return d;
30981
+ };
30982
+ const childGroups = /* @__PURE__ */ new Map();
30983
+ for (const g of parsed.groups)
30984
+ if (g.parentGroup)
30985
+ (childGroups.get(g.parentGroup) ?? childGroups.set(g.parentGroup, []).get(g.parentGroup)).push(g.label);
30986
+ const rectByGroup = /* @__PURE__ */ new Map();
30987
+ const ordered = [...parsed.groups].sort(
30988
+ (a, b) => depthOf(b.label) - depthOf(a.label)
30989
+ );
30990
+ const layoutGroups = [];
30991
+ for (const grp of ordered) {
30992
+ let l = Infinity, r = -Infinity, t = Infinity, bm = -Infinity;
30993
+ const acc2 = (box) => {
30994
+ l = Math.min(l, box.l);
30995
+ r = Math.max(r, box.r);
30996
+ t = Math.min(t, box.t);
30997
+ bm = Math.max(bm, box.b);
30998
+ };
30999
+ for (const c of grp.children) {
31000
+ const nb = nodeBox.get(c);
31001
+ if (nb) acc2(nb);
31002
+ }
31003
+ for (const sub of childGroups.get(grp.label) ?? []) {
31004
+ const sr = rectByGroup.get(sub);
31005
+ if (sr) acc2(sr);
31006
+ }
31007
+ if (!Number.isFinite(l)) continue;
31008
+ const x0 = l - GROUP_PAD;
31009
+ const x1 = r + GROUP_PAD;
31010
+ const y0 = t - GROUP_LABEL_ZONE2;
31011
+ const y1 = bm + GROUP_PAD;
31012
+ rectByGroup.set(grp.label, { l: x0, r: x1, t: y0, b: y1 });
31013
+ layoutGroups.push({
31014
+ label: grp.label,
31015
+ lineNumber: grp.lineNumber,
31016
+ x: (x0 + x1) / 2,
31017
+ y: (y0 + y1) / 2,
31018
+ width: x1 - x0,
31019
+ height: y1 - y0,
31020
+ collapsed: false,
31021
+ childCount: grp.children.length
31022
+ });
31023
+ }
31024
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
31025
+ const ext = (x0, y0, x1, y1) => {
31026
+ minX = Math.min(minX, x0);
31027
+ minY = Math.min(minY, y0);
31028
+ maxX = Math.max(maxX, x1);
31029
+ maxY = Math.max(maxY, y1);
31030
+ };
31031
+ for (const n of layoutNodes)
31032
+ ext(
31033
+ n.x - n.width / 2,
31034
+ n.y - n.height / 2,
31035
+ n.x + n.width / 2,
31036
+ n.y + n.height / 2
31037
+ );
31038
+ for (const e of layoutEdges)
31039
+ for (const p of e.points) ext(p.x, p.y, p.x, p.y);
31040
+ for (const g of layoutGroups)
31041
+ ext(
31042
+ g.x - g.width / 2,
31043
+ g.y - g.height / 2,
31044
+ g.x + g.width / 2,
31045
+ g.y + g.height / 2
31046
+ );
31047
+ const sx = MARGIN4 - minX;
31048
+ const sy = MARGIN4 - minY;
31049
+ const shift = sx !== 0 || sy !== 0;
31050
+ return {
31051
+ nodes: shift ? layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : layoutNodes,
31052
+ edges: shift ? layoutEdges.map((e) => ({
31053
+ ...e,
31054
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
31055
+ })) : layoutEdges,
31056
+ groups: shift ? layoutGroups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : layoutGroups,
31057
+ width: maxX + sx + MARGIN4,
31058
+ height: maxY + sy + MARGIN4
31059
+ };
31060
+ }
31061
+ }
31062
+ var NODESEP2, RANKSEP2, DUMMY_THICK2, MARGIN4, GROUP_PAD, GROUP_LABEL_ZONE2, GROUP_GAP, TIER_GAP, W_REAL_REAL2, W_REAL_DUMMY2, W_DUMMY_DUMMY2;
31063
+ var init_layout_grouped = __esm({
31064
+ "src/boxes-and-lines/layout-grouped.ts"() {
31065
+ "use strict";
31066
+ init_layout5();
31067
+ NODESEP2 = 50;
31068
+ RANKSEP2 = 60;
31069
+ DUMMY_THICK2 = 10;
31070
+ MARGIN4 = 40;
31071
+ GROUP_PAD = 16;
31072
+ GROUP_LABEL_ZONE2 = 32;
31073
+ GROUP_GAP = 24;
31074
+ TIER_GAP = GROUP_LABEL_ZONE2 + 2 * GROUP_PAD;
31075
+ W_REAL_REAL2 = 1;
31076
+ W_REAL_DUMMY2 = 2;
31077
+ W_DUMMY_DUMMY2 = 8;
31078
+ }
31079
+ });
31080
+
30195
31081
  // src/boxes-and-lines/layout-search.ts
30196
31082
  var layout_search_exports = {};
30197
31083
  __export(layout_search_exports, {
@@ -30209,7 +31095,7 @@ __export(layout_search_exports, {
30209
31095
  });
30210
31096
  import dagre4 from "@dagrejs/dagre";
30211
31097
  import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
30212
- function rng2(s) {
31098
+ function rng3(s) {
30213
31099
  return () => {
30214
31100
  s |= 0;
30215
31101
  s = s + 1831565813 | 0;
@@ -30564,7 +31450,7 @@ function detectGroupOverlaps(layout, opts) {
30564
31450
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30565
31451
  const rend = raw.map((a, i) => {
30566
31452
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30567
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31453
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30568
31454
  });
30569
31455
  const hit = /* @__PURE__ */ new Set();
30570
31456
  for (let i = 0; i < raw.length; i++)
@@ -30591,7 +31477,7 @@ function countGroupOverlaps(layout, opts) {
30591
31477
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30592
31478
  const rend = raw.map((a, i) => {
30593
31479
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30594
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31480
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30595
31481
  });
30596
31482
  let count = 0;
30597
31483
  for (let i = 0; i < raw.length; i++)
@@ -30634,7 +31520,7 @@ function separateGroupBands(layout, parsed) {
30634
31520
  let lo = (axis === "y" ? g.y : g.x) - half2;
30635
31521
  const hi = (axis === "y" ? g.y : g.x) + half2;
30636
31522
  const isParent = parsed.groups.some((c) => c.parentGroup === label);
30637
- if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
31523
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE3;
30638
31524
  return { label, lo, hi, c: (lo + hi) / 2 };
30639
31525
  });
30640
31526
  bands.sort((a, b) => a.c - b.c);
@@ -30705,7 +31591,7 @@ function separateGroupBands(layout, parsed) {
30705
31591
  acc(n.x + n.width / 2, n.y + n.height / 2);
30706
31592
  }
30707
31593
  for (const g of groups) {
30708
- acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
31594
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE3);
30709
31595
  acc(g.x + g.width / 2, g.y + g.height / 2);
30710
31596
  }
30711
31597
  for (const e of edges) for (const p of e.points) acc(p.x, p.y);
@@ -30788,6 +31674,7 @@ function edgeLength(layout) {
30788
31674
  }
30789
31675
  async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30790
31676
  const hideDescriptions = opts?.hideDescriptions ?? false;
31677
+ const reserveEdgeLabels = opts?.reserveEdgeLabels ?? false;
30791
31678
  const onProgress = opts?.onProgress;
30792
31679
  const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30793
31680
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
@@ -30969,7 +31856,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30969
31856
  }
30970
31857
  ];
30971
31858
  });
30972
- const GROUP_PAD = 16;
31859
+ const GROUP_PAD2 = 16;
30973
31860
  const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
30974
31861
  const groups = [];
30975
31862
  for (const grp of parsed.groups) {
@@ -30983,10 +31870,10 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30983
31870
  gy1 = Math.max(gy1, n2.y + n2.height / 2);
30984
31871
  }
30985
31872
  if (!Number.isFinite(gx0)) continue;
30986
- const x0 = gx0 - GROUP_PAD;
30987
- const x1 = gx1 + GROUP_PAD;
30988
- const y0 = gy0 - GROUP_LABEL_ZONE2;
30989
- const y1 = gy1 + GROUP_PAD;
31873
+ const x0 = gx0 - GROUP_PAD2;
31874
+ const x1 = gx1 + GROUP_PAD2;
31875
+ const y0 = gy0 - GROUP_LABEL_ZONE3;
31876
+ const y1 = gy1 + GROUP_PAD2;
30990
31877
  groups.push({
30991
31878
  label: grp.label,
30992
31879
  lineNumber: grp.lineNumber,
@@ -31043,7 +31930,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31043
31930
  };
31044
31931
  }
31045
31932
  function place(cfg) {
31046
- const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
31933
+ const r = cfg.seed === void 0 ? null : rng3(cfg.seed + 1);
31047
31934
  const ord = (a) => r ? shuffle(a, r) : a.slice();
31048
31935
  const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
31049
31936
  g.setGraph({
@@ -31080,8 +31967,14 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31080
31967
  for (const e of ord(parsed.edges)) {
31081
31968
  const s = srcRep(e.source);
31082
31969
  const t = tgtRep(e.target);
31083
- if (s !== t && g.hasNode(s) && g.hasNode(t))
31084
- g.setEdge(s, t, {}, edgeKey(e));
31970
+ if (s !== t && g.hasNode(s) && g.hasNode(t)) {
31971
+ let edgeLabel = {};
31972
+ if (reserveEdgeLabels && e.label) {
31973
+ const m = measureEdgeLabel(e.label, EDGE_LABEL_FONT_SIZE6);
31974
+ edgeLabel = { width: m.width, height: m.height, labelpos: "c" };
31975
+ }
31976
+ g.setEdge(s, t, edgeLabel, edgeKey(e));
31977
+ }
31085
31978
  }
31086
31979
  dagre4.layout(g);
31087
31980
  const nodes = parsed.nodes.map((n2) => {
@@ -31230,8 +32123,15 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31230
32123
  layered = layeredCandidates(parsed, sizes);
31231
32124
  } catch {
31232
32125
  }
32126
+ if (parsed.groups.length > 0 && collapsedGroupLabels.size === 0) {
32127
+ try {
32128
+ layered = layered.concat(groupedTierCandidates(parsed, sizes));
32129
+ } catch {
32130
+ }
32131
+ }
31233
32132
  const fastKey = /* @__PURE__ */ new Map();
31234
- for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
32133
+ for (const lay of pool)
32134
+ fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31235
32135
  pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
31236
32136
  const refineK = Math.min(REFINE_K, pool.length);
31237
32137
  let best = pool[0];
@@ -31296,12 +32196,14 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31296
32196
  }
31297
32197
  return best;
31298
32198
  }
31299
- var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
32199
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE3;
31300
32200
  var init_layout_search = __esm({
31301
32201
  "src/boxes-and-lines/layout-search.ts"() {
31302
32202
  "use strict";
31303
32203
  init_layout5();
31304
32204
  init_layout_layered();
32205
+ init_layout_grouped();
32206
+ init_label_placement();
31305
32207
  DEFAULT_LAMBDA = 4;
31306
32208
  ESCALATE_THRESHOLD = 4;
31307
32209
  ESCALATE_MAX_N = 45;
@@ -31310,7 +32212,7 @@ var init_layout_search = __esm({
31310
32212
  splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
31311
32213
  PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31312
32214
  FLAT_CACHE = /* @__PURE__ */ new WeakMap();
31313
- GROUP_LABEL_ZONE2 = 32;
32215
+ GROUP_LABEL_ZONE3 = 32;
31314
32216
  }
31315
32217
  });
31316
32218
 
@@ -31360,9 +32262,9 @@ function estimateLabelLines(label, nodeWidth = NODE_WIDTH) {
31360
32262
  current = word;
31361
32263
  }
31362
32264
  }
31363
- if (lines <= MAX_LABEL_LINES) return Math.min(lines, MAX_LABEL_LINES);
32265
+ if (lines <= MAX_LABEL_LINES2) return Math.min(lines, MAX_LABEL_LINES2);
31364
32266
  }
31365
- return MAX_LABEL_LINES;
32267
+ return MAX_LABEL_LINES2;
31366
32268
  }
31367
32269
  function computeNodeSize(node, reserveValueRow) {
31368
32270
  if (!node.description || node.description.length === 0) {
@@ -31370,7 +32272,7 @@ function computeNodeSize(node, reserveValueRow) {
31370
32272
  }
31371
32273
  const w = DESC_NODE_WIDTH;
31372
32274
  const labelLines = estimateLabelLines(node.label, w);
31373
- const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT + LABEL_PAD;
32275
+ const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT2 + LABEL_PAD;
31374
32276
  const maxTextWidth = w - 24;
31375
32277
  let totalRenderedLines = 0;
31376
32278
  for (const line11 of node.description) {
@@ -31394,7 +32296,7 @@ function computeNodeSize(node, reserveValueRow) {
31394
32296
  }
31395
32297
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
31396
32298
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
31397
- const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
32299
+ const searchOpts = {
31398
32300
  ...layoutOptions?.hideDescriptions !== void 0 && {
31399
32301
  hideDescriptions: layoutOptions.hideDescriptions
31400
32302
  },
@@ -31404,12 +32306,23 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
31404
32306
  ...layoutOptions?.onProgress !== void 0 && {
31405
32307
  onProgress: layoutOptions.onProgress
31406
32308
  }
31407
- });
31408
- return attachNotes(
31409
- applyParallelEdgeOffsets(searched),
32309
+ };
32310
+ const searched = await layoutBoxesAndLinesSearch2(
31410
32311
  parsed,
31411
- layoutOptions?.collapsedNotes
31412
- );
32312
+ collapseInfo,
32313
+ searchOpts
32314
+ );
32315
+ let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
32316
+ if (placed.unresolved.length > 0) {
32317
+ const relaid = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
32318
+ ...searchOpts,
32319
+ reserveEdgeLabels: true
32320
+ });
32321
+ const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
32322
+ if (relaidPlaced.unresolved.length < placed.unresolved.length)
32323
+ placed = relaidPlaced;
32324
+ }
32325
+ return attachNotes(placed.layout, parsed, layoutOptions?.collapsedNotes);
31413
32326
  }
31414
32327
  function attachNotes(layout, parsed, collapsedNotes) {
31415
32328
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -31477,6 +32390,7 @@ function attachNotes(layout, parsed, collapsedNotes) {
31477
32390
  points: e.points.map((pt) => ({ x: pt.x + shiftX, y: pt.y + shiftY })),
31478
32391
  ...e.labelX !== void 0 && { labelX: e.labelX + shiftX },
31479
32392
  ...e.labelY !== void 0 && { labelY: e.labelY + shiftY }
32393
+ // labelWidth/labelHeight/labelLines are shift-invariant — carried via spread.
31480
32394
  })) : layout.edges;
31481
32395
  const finalGroups = shifted ? layout.groups.map((grp) => ({
31482
32396
  ...grp,
@@ -31487,8 +32401,8 @@ function attachNotes(layout, parsed, collapsedNotes) {
31487
32401
  nodes: finalNodes,
31488
32402
  edges: finalEdges,
31489
32403
  groups: finalGroups,
31490
- width: bbMaxX + shiftX + MARGIN4,
31491
- height: bbMaxY + shiftY + MARGIN4
32404
+ width: bbMaxX + shiftX + MARGIN5,
32405
+ height: bbMaxY + shiftY + MARGIN5
31492
32406
  };
31493
32407
  }
31494
32408
  function applyParallelEdgeOffsets(layout) {
@@ -31521,13 +32435,14 @@ function applyParallelEdgeOffsets(layout) {
31521
32435
  }))
31522
32436
  };
31523
32437
  }
31524
- var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
32438
+ var MARGIN5, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES2, LABEL_LINE_HEIGHT2, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
31525
32439
  var init_layout5 = __esm({
31526
32440
  "src/boxes-and-lines/layout.ts"() {
31527
32441
  "use strict";
31528
32442
  init_text_measure();
32443
+ init_label_placement();
31529
32444
  init_notes();
31530
- MARGIN4 = 40;
32445
+ MARGIN5 = 40;
31531
32446
  MAX_PARALLEL_EDGES = 5;
31532
32447
  PARALLEL_SPACING = 22;
31533
32448
  PHI = 1.618;
@@ -31539,8 +32454,8 @@ var init_layout5 = __esm({
31539
32454
  DESC_PADDING = 8;
31540
32455
  SEPARATOR_GAP4 = 4;
31541
32456
  MAX_DESC_LINES2 = 6;
31542
- MAX_LABEL_LINES = 3;
31543
- LABEL_LINE_HEIGHT = 1.3;
32457
+ MAX_LABEL_LINES2 = 3;
32458
+ LABEL_LINE_HEIGHT2 = 1.3;
31544
32459
  LABEL_PAD = 12;
31545
32460
  VALUE_ROW_FONT = 11;
31546
32461
  VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
@@ -31559,7 +32474,7 @@ function tokenize(text) {
31559
32474
  return tokens;
31560
32475
  }
31561
32476
  function tryWrap(tokens, maxWidth, fontSize, maxLines) {
31562
- const availWidth = maxWidth - H_PAD;
32477
+ const availWidth = maxWidth - H_PAD2;
31563
32478
  const lines = [];
31564
32479
  let currentLine = "";
31565
32480
  for (const token of tokens) {
@@ -31584,7 +32499,7 @@ function tryWrap(tokens, maxWidth, fontSize, maxLines) {
31584
32499
  return lines;
31585
32500
  }
31586
32501
  function truncateLastLine(lines, maxWidth, fontSize) {
31587
- const availWidth = maxWidth - H_PAD;
32502
+ const availWidth = maxWidth - H_PAD2;
31588
32503
  const result = [...lines];
31589
32504
  const last = result[result.length - 1];
31590
32505
  if (measureText(last, fontSize) > availWidth) {
@@ -31592,7 +32507,7 @@ function truncateLastLine(lines, maxWidth, fontSize) {
31592
32507
  }
31593
32508
  return result;
31594
32509
  }
31595
- function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES2) {
32510
+ function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES3) {
31596
32511
  if (!text) return { lines: [""], fontSize: baseFontSize };
31597
32512
  const tokens = tokenize(text);
31598
32513
  for (let fs = baseFontSize; fs >= minFontSize; fs--) {
@@ -31607,7 +32522,7 @@ function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABE
31607
32522
  if (allLines.length > maxLines) {
31608
32523
  const last = truncated[truncated.length - 1];
31609
32524
  if (!last.endsWith("\u2026")) {
31610
- const availWidth = maxWidth - H_PAD;
32525
+ const availWidth = maxWidth - H_PAD2;
31611
32526
  if (measureText(last + "\u2026", minFontSize) > availWidth) {
31612
32527
  truncated[truncated.length - 1] = truncateText(
31613
32528
  last,
@@ -31634,7 +32549,7 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
31634
32549
  nodeWidth,
31635
32550
  baseFontSize,
31636
32551
  MIN_FONT_SIZE,
31637
- MAX_LABEL_LINES2
32552
+ MAX_LABEL_LINES3
31638
32553
  );
31639
32554
  const descLines = [];
31640
32555
  let descFontSize = DESC_FONT_SIZE3;
@@ -31662,14 +32577,14 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
31662
32577
  descFontSize
31663
32578
  };
31664
32579
  }
31665
- var H_PAD, MAX_LABEL_LINES2, MAX_DESC_LINES3, ROOT_FONT_SIZE, MIN_FONT_SIZE, FONT_STEP, DESC_FONT_SIZE3;
32580
+ var H_PAD2, MAX_LABEL_LINES3, MAX_DESC_LINES3, ROOT_FONT_SIZE, MIN_FONT_SIZE, FONT_STEP, DESC_FONT_SIZE3;
31666
32581
  var init_text_wrap = __esm({
31667
32582
  "src/mindmap/text-wrap.ts"() {
31668
32583
  "use strict";
31669
32584
  init_description_helpers();
31670
32585
  init_text_measure();
31671
- H_PAD = 16;
31672
- MAX_LABEL_LINES2 = 3;
32586
+ H_PAD2 = 16;
32587
+ MAX_LABEL_LINES3 = 3;
31673
32588
  MAX_DESC_LINES3 = 2;
31674
32589
  ROOT_FONT_SIZE = 17;
31675
32590
  MIN_FONT_SIZE = 9;
@@ -31698,7 +32613,7 @@ function layoutMindmap(parsed, _palette, options) {
31698
32613
  leafWidth: ctx.structural(LEAF_WIDTH),
31699
32614
  hGap: ctx.aesthetic(H_GAP2),
31700
32615
  vGap: ctx.aesthetic(V_GAP2),
31701
- margin: ctx.aesthetic(MARGIN5),
32616
+ margin: ctx.aesthetic(MARGIN6),
31702
32617
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
31703
32618
  };
31704
32619
  populateDepthCache(roots);
@@ -32056,7 +32971,7 @@ function nodeHeightS(node, hideDescriptions, sc) {
32056
32971
  hideDescriptions
32057
32972
  );
32058
32973
  const labelLineCount = text.labelLines.length;
32059
- const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT : LABEL_LINE_HEIGHT2 * labelLineCount;
32974
+ const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT : LABEL_LINE_HEIGHT3 * labelLineCount;
32060
32975
  let h = labelH + NODE_V_PAD;
32061
32976
  if (text.descLines.length > 0) {
32062
32977
  h += DESC_LINE_HEIGHT3 * text.descLines.length + 4;
@@ -32076,7 +32991,7 @@ function populateDepthCache(roots) {
32076
32991
  };
32077
32992
  walk(roots, 0);
32078
32993
  }
32079
- var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN5, MULTI_ROOT_GAP, nodeDepthCache;
32994
+ var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN6, MULTI_ROOT_GAP, nodeDepthCache;
32080
32995
  var init_layout6 = __esm({
32081
32996
  "src/mindmap/layout.ts"() {
32082
32997
  "use strict";
@@ -32087,12 +33002,12 @@ var init_layout6 = __esm({
32087
33002
  DEPTH1_WIDTH = 150;
32088
33003
  LEAF_WIDTH = 120;
32089
33004
  SINGLE_LABEL_HEIGHT = 28;
32090
- LABEL_LINE_HEIGHT2 = 18;
33005
+ LABEL_LINE_HEIGHT3 = 18;
32091
33006
  DESC_LINE_HEIGHT3 = 14;
32092
33007
  NODE_V_PAD = 10;
32093
33008
  H_GAP2 = 40;
32094
33009
  V_GAP2 = 12;
32095
- MARGIN5 = 40;
33010
+ MARGIN6 = 40;
32096
33011
  MULTI_ROOT_GAP = 60;
32097
33012
  nodeDepthCache = /* @__PURE__ */ new Map();
32098
33013
  }
@@ -32350,7 +33265,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
32350
33265
  );
32351
33266
  const nodeG = mainG.append("g").attr("class", "mindmap-node").attr("data-line-number", node.lineNumber);
32352
33267
  if (activeTagGroup) {
32353
- const tagKey = activeTagGroup.toLowerCase();
33268
+ const tagKey = tagAttrKey(activeTagGroup);
32354
33269
  const metaValue = node.metadata[tagKey];
32355
33270
  if (metaValue) {
32356
33271
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -32379,18 +33294,18 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
32379
33294
  descFontSize
32380
33295
  } = textLayout;
32381
33296
  const labelLineCount = labelLines.length;
32382
- const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 : LABEL_LINE_HEIGHT3 * labelLineCount;
33297
+ const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 : LABEL_LINE_HEIGHT4 * labelLineCount;
32383
33298
  const labelZoneHeight = showDesc ? labelZoneH : node.height;
32384
33299
  const centerX = node.x + node.width / 2;
32385
33300
  if (labelLineCount <= 1) {
32386
33301
  const labelY = node.y + labelZoneHeight / 2 + fontSize * 0.35;
32387
33302
  nodeG.append("text").attr("x", centerX).attr("y", labelY).attr("text-anchor", "middle").attr("font-size", fontSize).attr("font-weight", isRoot ? "bold" : "normal").attr("fill", onFillText).text(labelLines[0]);
32388
33303
  } else {
32389
- const blockH = LABEL_LINE_HEIGHT3 * (labelLineCount - 1);
33304
+ const blockH = LABEL_LINE_HEIGHT4 * (labelLineCount - 1);
32390
33305
  const firstBaselineY = node.y + labelZoneHeight / 2 - blockH / 2 + fontSize * 0.35;
32391
33306
  const textEl = nodeG.append("text").attr("x", centerX).attr("text-anchor", "middle").attr("font-size", fontSize).attr("font-weight", isRoot ? "bold" : "normal").attr("fill", onFillText);
32392
33307
  for (let i = 0; i < labelLines.length; i++) {
32393
- textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i * LABEL_LINE_HEIGHT3).text(labelLines[i]);
33308
+ textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i * LABEL_LINE_HEIGHT4).text(labelLines[i]);
32394
33309
  }
32395
33310
  }
32396
33311
  if (labelLines.length > 1 || labelLines[0] !== node.label) {
@@ -32465,10 +33380,11 @@ function renderMindmapForExport(content, theme, palette) {
32465
33380
  return extractExportSvg(container, theme);
32466
33381
  });
32467
33382
  }
32468
- var DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
33383
+ var DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT4, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
32469
33384
  var init_renderer7 = __esm({
32470
33385
  "src/mindmap/renderer.ts"() {
32471
33386
  "use strict";
33387
+ init_tag_groups();
32472
33388
  init_fonts();
32473
33389
  init_export_container();
32474
33390
  init_color_utils();
@@ -32486,7 +33402,7 @@ var init_renderer7 = __esm({
32486
33402
  DIAGRAM_PADDING7 = 20;
32487
33403
  TITLE_HEIGHT4 = 30;
32488
33404
  SINGLE_LABEL_HEIGHT2 = 28;
32489
- LABEL_LINE_HEIGHT3 = 18;
33405
+ LABEL_LINE_HEIGHT4 = 18;
32490
33406
  DESC_LINE_HEIGHT4 = 14;
32491
33407
  NODE_RADIUS = 6;
32492
33408
  ROOT_STROKE_WIDTH = 2.5;
@@ -33679,7 +34595,7 @@ function resolveNodeColor4(el, tagGroups, activeGroupName, ancestors) {
33679
34595
  (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
33680
34596
  );
33681
34597
  if (!group) return void 0;
33682
- const key = group.name.toLowerCase();
34598
+ const key = tagAttrKey(group.name);
33683
34599
  let metaValue = el.metadata[key];
33684
34600
  if (!metaValue && ancestors) {
33685
34601
  for (const ancestor of ancestors) {
@@ -33881,8 +34797,8 @@ function layoutC4Context(parsed, activeTagGroup) {
33881
34797
  }
33882
34798
  }
33883
34799
  if (nodes.length > 0) {
33884
- const shiftX = MARGIN6 - minX;
33885
- const shiftY = MARGIN6 - minY;
34800
+ const shiftX = MARGIN7 - minX;
34801
+ const shiftY = MARGIN7 - minY;
33886
34802
  for (const node of nodes) {
33887
34803
  node.x += shiftX;
33888
34804
  node.y += shiftY;
@@ -33894,12 +34810,12 @@ function layoutC4Context(parsed, activeTagGroup) {
33894
34810
  }
33895
34811
  }
33896
34812
  }
33897
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
33898
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
34813
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN7 * 2 : 0;
34814
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN7 * 2 : 0;
33899
34815
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
33900
34816
  if (legendGroups.length > 0) {
33901
- const legendY = totalHeight + MARGIN6;
33902
- let legendX = MARGIN6;
34817
+ const legendY = totalHeight + MARGIN7;
34818
+ let legendX = MARGIN7;
33903
34819
  for (const lg of legendGroups) {
33904
34820
  lg.x = legendX;
33905
34821
  lg.y = legendY;
@@ -34231,8 +35147,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34231
35147
  if (pt.y > maxY) maxY = pt.y;
34232
35148
  }
34233
35149
  }
34234
- const shiftX = MARGIN6 - minX;
34235
- const shiftY = MARGIN6 - minY;
35150
+ const shiftX = MARGIN7 - minX;
35151
+ const shiftY = MARGIN7 - minY;
34236
35152
  for (const node of nodes) {
34237
35153
  node.x += shiftX;
34238
35154
  node.y += shiftY;
@@ -34249,12 +35165,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34249
35165
  pt.y += shiftY;
34250
35166
  }
34251
35167
  }
34252
- let totalWidth = maxX - minX + MARGIN6 * 2;
34253
- let totalHeight = maxY - minY + MARGIN6 * 2;
35168
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35169
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34254
35170
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34255
35171
  if (legendGroups.length > 0) {
34256
- const legendY = totalHeight + MARGIN6;
34257
- let legendX = MARGIN6;
35172
+ const legendY = totalHeight + MARGIN7;
35173
+ let legendX = MARGIN7;
34258
35174
  for (const lg of legendGroups) {
34259
35175
  lg.x = legendX;
34260
35176
  lg.y = legendY;
@@ -34646,8 +35562,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34646
35562
  if (pt.y > maxY) maxY = pt.y;
34647
35563
  }
34648
35564
  }
34649
- const shiftX = MARGIN6 - minX;
34650
- const shiftY = MARGIN6 - minY;
35565
+ const shiftX = MARGIN7 - minX;
35566
+ const shiftY = MARGIN7 - minY;
34651
35567
  for (const node of nodes) {
34652
35568
  node.x += shiftX;
34653
35569
  node.y += shiftY;
@@ -34664,12 +35580,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34664
35580
  pt.y += shiftY;
34665
35581
  }
34666
35582
  }
34667
- let totalWidth = maxX - minX + MARGIN6 * 2;
34668
- let totalHeight = maxY - minY + MARGIN6 * 2;
35583
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35584
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34669
35585
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34670
35586
  if (legendGroups.length > 0) {
34671
- const legendY = totalHeight + MARGIN6;
34672
- let legendX = MARGIN6;
35587
+ const legendY = totalHeight + MARGIN7;
35588
+ let legendX = MARGIN7;
34673
35589
  for (const lg of legendGroups) {
34674
35590
  lg.x = legendX;
34675
35591
  lg.y = legendY;
@@ -34957,8 +35873,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34957
35873
  if (pt.y > maxY) maxY = pt.y;
34958
35874
  }
34959
35875
  }
34960
- const shiftX = MARGIN6 - minX;
34961
- const shiftY = MARGIN6 - minY;
35876
+ const shiftX = MARGIN7 - minX;
35877
+ const shiftY = MARGIN7 - minY;
34962
35878
  for (const node of nodes) {
34963
35879
  node.x += shiftX;
34964
35880
  node.y += shiftY;
@@ -34973,12 +35889,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34973
35889
  pt.y += shiftY;
34974
35890
  }
34975
35891
  }
34976
- let totalWidth = maxX - minX + MARGIN6 * 2;
34977
- let totalHeight = maxY - minY + MARGIN6 * 2;
35892
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35893
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34978
35894
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34979
35895
  if (legendGroups.length > 0) {
34980
- const legendY = totalHeight + MARGIN6;
34981
- let legendX = MARGIN6;
35896
+ const legendY = totalHeight + MARGIN7;
35897
+ let legendX = MARGIN7;
34982
35898
  for (const lg of legendGroups) {
34983
35899
  lg.x = legendX;
34984
35900
  lg.y = legendY;
@@ -34998,10 +35914,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34998
35914
  height: totalHeight
34999
35915
  };
35000
35916
  }
35001
- var gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
35917
+ var gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN7, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
35002
35918
  var init_layout8 = __esm({
35003
35919
  "src/c4/layout.ts"() {
35004
35920
  "use strict";
35921
+ init_tag_groups();
35005
35922
  init_legend_constants();
35006
35923
  init_text_measure();
35007
35924
  gNode = (g, name) => g.node(name);
@@ -35018,7 +35935,7 @@ var init_layout8 = __esm({
35018
35935
  CARD_H_PAD3 = 20;
35019
35936
  META_LINE_HEIGHT4 = 16;
35020
35937
  META_FONT_SIZE4 = 11;
35021
- MARGIN6 = 40;
35938
+ MARGIN7 = 40;
35022
35939
  BOUNDARY_PAD = 40;
35023
35940
  GROUP_BOUNDARY_PAD = 24;
35024
35941
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -35154,7 +36071,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35154
36071
  const labelText = edge.label ?? "";
35155
36072
  const techText = edge.technology ? `[${edge.technology}]` : "";
35156
36073
  const textW = Math.max(
35157
- measureText(labelText, EDGE_LABEL_FONT_SIZE6),
36074
+ measureText(labelText, EDGE_LABEL_FONT_SIZE7),
35158
36075
  measureText(techText, TECH_FONT_SIZE)
35159
36076
  );
35160
36077
  const bgW = textW + 12;
@@ -35165,7 +36082,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35165
36082
  textY = midPt.y - 4;
35166
36083
  }
35167
36084
  if (labelText) {
35168
- edgeG.append("text").attr("x", midPt.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", EDGE_LABEL_FONT_SIZE6).attr("class", "c4-edge-label").text(labelText);
36085
+ edgeG.append("text").attr("x", midPt.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("class", "c4-edge-label").text(labelText);
35169
36086
  }
35170
36087
  if (techText) {
35171
36088
  edgeG.append("text").attr("x", midPt.x).attr("y", labelText ? textY + 18 : textY + 4).attr("text-anchor", "middle").attr("fill", edgeColor3).attr("font-size", TECH_FONT_SIZE).attr("font-style", "italic").attr("class", "c4-edge-tech").text(techText);
@@ -35175,7 +36092,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35175
36092
  for (const node of layout.nodes) {
35176
36093
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "c4-card").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
35177
36094
  if (activeTagGroup) {
35178
- const tagKey = activeTagGroup.toLowerCase();
36095
+ const tagKey = tagAttrKey(activeTagGroup);
35179
36096
  const tagValue = node.metadata[tagKey];
35180
36097
  if (tagValue) {
35181
36098
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35349,7 +36266,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
35349
36266
  const labelText = edge.label ?? "";
35350
36267
  const techText = edge.technology ? `[${edge.technology}]` : "";
35351
36268
  const textW = Math.max(
35352
- measureText(labelText, EDGE_LABEL_FONT_SIZE6),
36269
+ measureText(labelText, EDGE_LABEL_FONT_SIZE7),
35353
36270
  measureText(techText, TECH_FONT_SIZE)
35354
36271
  );
35355
36272
  const bgW = textW + 12;
@@ -35367,7 +36284,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
35367
36284
  });
35368
36285
  }
35369
36286
  }
35370
- placeEdgeLabels(pendingLabels, edges, obstacleRects);
36287
+ placeEdgeLabels2(pendingLabels, edges, obstacleRects);
35371
36288
  for (const lbl of pendingLabels) {
35372
36289
  lbl.edgeG.append("rect").attr("x", lbl.x - lbl.bgW / 2).attr("y", lbl.y - lbl.bgH / 2).attr("width", lbl.bgW).attr("height", lbl.bgH).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.9).attr("class", "c4-edge-label-bg");
35373
36290
  let textY = lbl.y;
@@ -35375,7 +36292,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
35375
36292
  textY = lbl.y - 4;
35376
36293
  }
35377
36294
  if (lbl.labelText) {
35378
- lbl.edgeG.append("text").attr("x", lbl.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", EDGE_LABEL_FONT_SIZE6).attr("class", "c4-edge-label").text(lbl.labelText);
36295
+ lbl.edgeG.append("text").attr("x", lbl.x).attr("y", textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", EDGE_LABEL_FONT_SIZE7).attr("class", "c4-edge-label").text(lbl.labelText);
35379
36296
  }
35380
36297
  if (lbl.techText) {
35381
36298
  lbl.edgeG.append("text").attr("x", lbl.x).attr("y", lbl.labelText ? textY + 18 : textY + 4).attr("text-anchor", "middle").attr("fill", lbl.edgeColor).attr("font-size", TECH_FONT_SIZE).attr("font-style", "italic").attr("class", "c4-edge-tech").text(lbl.techText);
@@ -35456,7 +36373,7 @@ function tangentAt(points, t) {
35456
36373
  }
35457
36374
  return { x: 0, y: 1 };
35458
36375
  }
35459
- function placeEdgeLabels(labels, edges, obstacleRects) {
36376
+ function placeEdgeLabels2(labels, edges, obstacleRects) {
35460
36377
  if (labels.length === 0) return;
35461
36378
  const allPaths = edges.map((e) => e.points);
35462
36379
  const placedRects = [];
@@ -35734,7 +36651,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35734
36651
  for (const node of layout.nodes) {
35735
36652
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "c4-card").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
35736
36653
  if (activeTagGroup) {
35737
- const tagKey = activeTagGroup.toLowerCase();
36654
+ const tagKey = tagAttrKey(activeTagGroup);
35738
36655
  const tagValue = node.metadata[tagKey];
35739
36656
  if (tagValue) {
35740
36657
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35994,10 +36911,11 @@ function renderC4DeploymentForExport(content, theme, palette) {
35994
36911
  document.body.removeChild(el);
35995
36912
  }
35996
36913
  }
35997
- var DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE6, TECH_FONT_SIZE, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
36914
+ var DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE7, TECH_FONT_SIZE, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
35998
36915
  var init_renderer9 = __esm({
35999
36916
  "src/c4/renderer.ts"() {
36000
36917
  "use strict";
36918
+ init_tag_groups();
36001
36919
  init_fonts();
36002
36920
  init_color_utils();
36003
36921
  init_inline_markdown();
@@ -36016,7 +36934,7 @@ var init_renderer9 = __esm({
36016
36934
  NAME_FONT_SIZE2 = 14;
36017
36935
  DESC_FONT_SIZE5 = 11;
36018
36936
  DESC_LINE_HEIGHT6 = 16;
36019
- EDGE_LABEL_FONT_SIZE6 = 11;
36937
+ EDGE_LABEL_FONT_SIZE7 = 11;
36020
36938
  TECH_FONT_SIZE = 10;
36021
36939
  CARD_H_PAD4 = 20;
36022
36940
  CARD_V_PAD4 = 14;
@@ -36641,7 +37559,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
36641
37559
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
36642
37560
  const sTitleY = ctx.structural(TITLE_Y);
36643
37561
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
36644
- const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE7);
37562
+ const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE8);
36645
37563
  const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
36646
37564
  const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
36647
37565
  const sArrowheadW = ctx.structural(ARROWHEAD_W3);
@@ -36867,7 +37785,7 @@ function renderFlowchartForExport(content, theme, palette) {
36867
37785
  document.body.removeChild(container);
36868
37786
  }
36869
37787
  }
36870
- var DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
37788
+ var DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE8, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
36871
37789
  var init_flowchart_renderer = __esm({
36872
37790
  "src/graph/flowchart-renderer.ts"() {
36873
37791
  "use strict";
@@ -36886,7 +37804,7 @@ var init_flowchart_renderer = __esm({
36886
37804
  DIAGRAM_PADDING9 = 20;
36887
37805
  MAX_SCALE6 = 3;
36888
37806
  NODE_FONT_SIZE2 = 13;
36889
- EDGE_LABEL_FONT_SIZE7 = 11;
37807
+ EDGE_LABEL_FONT_SIZE8 = 11;
36890
37808
  ARROWHEAD_W3 = 10;
36891
37809
  ARROWHEAD_H3 = 7;
36892
37810
  IO_SKEW = 15;
@@ -38061,7 +38979,7 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
38061
38979
  const crossOverlap = isLR ? Math.min(ga.x + ga.width, gb.x + gb.width) - Math.max(ga.x, gb.x) : Math.min(ga.y + ga.height, gb.y + gb.height) - Math.max(ga.y, gb.y);
38062
38980
  if (crossOverlap <= 0) continue;
38063
38981
  anyOverlap = true;
38064
- const shift = primaryOverlap + GROUP_GAP;
38982
+ const shift = primaryOverlap + GROUP_GAP2;
38065
38983
  const aCenter = isLR ? ga.y + ga.height / 2 : ga.x + ga.width / 2;
38066
38984
  const bCenter = isLR ? gb.y + gb.height / 2 : gb.x + gb.width / 2;
38067
38985
  const groupToShift = aCenter <= bCenter ? gb : ga;
@@ -38321,7 +39239,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
38321
39239
  const midIdx = Math.floor(edge.points.length / 2);
38322
39240
  const midPt = edge.points[midIdx];
38323
39241
  if (midPt) {
38324
- const halfWidth = (measureText(edge.label, EDGE_LABEL_FONT_SIZE8) + 8) / 2;
39242
+ const halfWidth = (measureText(edge.label, EDGE_LABEL_FONT_SIZE9) + 8) / 2;
38325
39243
  if (midPt.x - halfWidth < minX) minX = midPt.x - halfWidth;
38326
39244
  if (midPt.x + halfWidth > maxX) maxX = midPt.x + halfWidth;
38327
39245
  }
@@ -38355,7 +39273,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
38355
39273
  height: totalHeight
38356
39274
  };
38357
39275
  }
38358
- var MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
39276
+ var MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE9, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP2;
38359
39277
  var init_layout10 = __esm({
38360
39278
  "src/infra/layout.ts"() {
38361
39279
  "use strict";
@@ -38369,7 +39287,7 @@ var init_layout10 = __esm({
38369
39287
  COLLAPSE_BAR_HEIGHT3 = 6;
38370
39288
  NODE_FONT_SIZE3 = 13;
38371
39289
  META_FONT_SIZE5 = 10;
38372
- EDGE_LABEL_FONT_SIZE8 = 11;
39290
+ EDGE_LABEL_FONT_SIZE9 = 11;
38373
39291
  PADDING_X3 = 24;
38374
39292
  GROUP_PADDING2 = 20;
38375
39293
  GROUP_HEADER_HEIGHT = 24;
@@ -38410,7 +39328,7 @@ var init_layout10 = __esm({
38410
39328
  "retention-hours": "retention",
38411
39329
  partitions: "partitions"
38412
39330
  };
38413
- GROUP_GAP = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
39331
+ GROUP_GAP2 = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
38414
39332
  }
38415
39333
  });
38416
39334
 
@@ -38476,7 +39394,7 @@ function buildScaledConstants(ctx) {
38476
39394
  sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
38477
39395
  sMetaFontSize: ctx.text(META_FONT_SIZE6),
38478
39396
  sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
38479
- sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE9),
39397
+ sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE10),
38480
39398
  sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
38481
39399
  sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
38482
39400
  sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
@@ -39205,7 +40123,7 @@ function renderEdgeLabels(svg, edges, nodes, groups, palette, _isDark, animate,
39205
40123
  if (!midPt) continue;
39206
40124
  const labelText = edge.label;
39207
40125
  const g = svg.append("g").attr("class", animate ? "infra-edge-label" : "");
39208
- const textWidth = measureText(labelText, EDGE_LABEL_FONT_SIZE9) + 8;
40126
+ const textWidth = measureText(labelText, EDGE_LABEL_FONT_SIZE10) + 8;
39209
40127
  g.append("rect").attr("x", midPt.x - textWidth / 2).attr("y", midPt.y - 8).attr("width", textWidth).attr("height", 16).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.9);
39210
40128
  g.append("text").attr("x", midPt.x).attr("y", midPt.y + 4).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sEdgeLabelFontSize).attr("fill", palette.textMuted).text(labelText);
39211
40129
  if (animate) {
@@ -39219,7 +40137,7 @@ function resolveActiveTagStroke(node, activeGroup, tagGroups, palette) {
39219
40137
  (t) => t.name.toLowerCase() === activeGroup.toLowerCase()
39220
40138
  );
39221
40139
  if (!tg) return null;
39222
- const tagKey = (tg.alias ?? tg.name).toLowerCase();
40140
+ const tagKey = tagAttrKey(tg.alias ?? tg.name);
39223
40141
  const tagVal = node.tags[tagKey];
39224
40142
  if (!tagVal) return null;
39225
40143
  const tv = tg.values.find(
@@ -39548,7 +40466,7 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
39548
40466
  groups.push({
39549
40467
  name: tg.name,
39550
40468
  type: "tag",
39551
- tagKey: (tg.alias ?? tg.name).toLowerCase(),
40469
+ tagKey: tagAttrKey(tg.alias ?? tg.name),
39552
40470
  entries,
39553
40471
  width: LEGEND_CAPSULE_PAD * 2 + pillWidth2 + 4 + entriesWidth2,
39554
40472
  minifiedWidth: pillWidth2
@@ -39596,7 +40514,7 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
39596
40514
  });
39597
40515
  legendG.selectAll("[data-legend-group]").classed("infra-legend-group", true);
39598
40516
  for (const group of legendGroups) {
39599
- const groupKey = group.name.toLowerCase();
40517
+ const groupKey = tagAttrKey(group.name);
39600
40518
  for (const entry of group.entries) {
39601
40519
  const entryEl = legendG.select(
39602
40520
  `[data-legend-group="${groupKey}"] [data-legend-entry="${entry.value.toLowerCase()}"]`
@@ -39815,10 +40733,11 @@ function parseAndLayoutInfra(content) {
39815
40733
  const layout = layoutInfra(computed);
39816
40734
  return { parsed, computed, layout };
39817
40735
  }
39818
- var NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE9, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
40736
+ var NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
39819
40737
  var init_renderer10 = __esm({
39820
40738
  "src/infra/renderer.ts"() {
39821
40739
  "use strict";
40740
+ init_tag_groups();
39822
40741
  init_fonts();
39823
40742
  init_color_utils();
39824
40743
  init_colors();
@@ -39838,7 +40757,7 @@ var init_renderer10 = __esm({
39838
40757
  NODE_FONT_SIZE4 = 13;
39839
40758
  META_FONT_SIZE6 = 10;
39840
40759
  META_LINE_HEIGHT6 = 14;
39841
- EDGE_LABEL_FONT_SIZE9 = 11;
40760
+ EDGE_LABEL_FONT_SIZE10 = 11;
39842
40761
  GROUP_LABEL_FONT_SIZE2 = 14;
39843
40762
  NODE_BORDER_RADIUS = 8;
39844
40763
  OVERLOAD_STROKE_WIDTH = 3;
@@ -39915,17 +40834,17 @@ function mulberry32(seed) {
39915
40834
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
39916
40835
  };
39917
40836
  }
39918
- function standardNormal(rng3) {
40837
+ function standardNormal(rng4) {
39919
40838
  let u = 0;
39920
40839
  let v = 0;
39921
- while (u === 0) u = rng3();
39922
- while (v === 0) v = rng3();
40840
+ while (u === 0) u = rng4();
40841
+ while (v === 0) v = rng4();
39923
40842
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
39924
40843
  }
39925
- function gamma(shape, rng3) {
40844
+ function gamma(shape, rng4) {
39926
40845
  if (shape < 1) {
39927
- const u = rng3();
39928
- return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
40846
+ const u = rng4();
40847
+ return gamma(shape + 1, rng4) * Math.pow(u, 1 / shape);
39929
40848
  }
39930
40849
  const d = shape - 1 / 3;
39931
40850
  const c = 1 / Math.sqrt(9 * d);
@@ -39933,29 +40852,29 @@ function gamma(shape, rng3) {
39933
40852
  let x;
39934
40853
  let v;
39935
40854
  do {
39936
- x = standardNormal(rng3);
40855
+ x = standardNormal(rng4);
39937
40856
  v = 1 + c * x;
39938
40857
  } while (v <= 0);
39939
40858
  v = v * v * v;
39940
- const u = rng3();
40859
+ const u = rng4();
39941
40860
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
39942
40861
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
39943
40862
  }
39944
40863
  }
39945
- function sampleBeta(alpha, beta, rng3) {
39946
- const x = gamma(alpha, rng3);
39947
- const y = gamma(beta, rng3);
40864
+ function sampleBeta(alpha, beta, rng4) {
40865
+ const x = gamma(alpha, rng4);
40866
+ const y = gamma(beta, rng4);
39948
40867
  return x / (x + y);
39949
40868
  }
39950
- function sampleBetaPert(o, m, p, rng3) {
40869
+ function sampleBetaPert(o, m, p, rng4) {
39951
40870
  if (o === p) return m;
39952
40871
  const range = p - o;
39953
40872
  const alpha = 1 + 4 * (m - o) / range;
39954
40873
  const beta = 1 + 4 * (p - m) / range;
39955
- return o + sampleBeta(alpha, beta, rng3) * range;
40874
+ return o + sampleBeta(alpha, beta, rng4) * range;
39956
40875
  }
39957
40876
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
39958
- const rng3 = mulberry32(opts.seed);
40877
+ const rng4 = mulberry32(opts.seed);
39959
40878
  const expById = /* @__PURE__ */ new Map();
39960
40879
  for (const e of expanded) expById.set(e.id, e);
39961
40880
  const completionTimes = new Array(opts.trials);
@@ -39978,7 +40897,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
39978
40897
  } else if (exp.o === exp.p) {
39979
40898
  duration = exp.m;
39980
40899
  } else {
39981
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
40900
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng4);
39982
40901
  }
39983
40902
  let esLower = 0;
39984
40903
  let efLower = -Infinity;
@@ -44265,7 +45184,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44265
45184
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(containerWidth, idealWidth);
44266
45185
  const sBarH = ctx.structural(BAR_H);
44267
45186
  const sRowGap = ctx.structural(ROW_GAP);
44268
- const sGroupGap = ctx.structural(GROUP_GAP2);
45187
+ const sGroupGap = ctx.structural(GROUP_GAP3);
44269
45188
  const sMilestoneSize = ctx.structural(MILESTONE_SIZE);
44270
45189
  const sBottomMargin = ctx.aesthetic(BOTTOM_MARGIN);
44271
45190
  const sRightMargin = ctx.aesthetic(RIGHT_MARGIN);
@@ -44437,7 +45356,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44437
45356
  (tg) => tg.name.toLowerCase() === currentSwimlaneGroup.toLowerCase()
44438
45357
  );
44439
45358
  if (tagGroup) {
44440
- const tagKey = tagGroup.name.toLowerCase();
45359
+ const tagKey = tagAttrKey(tagGroup.name);
44441
45360
  for (const rt of resolved.tasks) {
44442
45361
  let value = rt.effectiveMetadata[tagKey];
44443
45362
  if (!value && tagGroup.defaultValue) value = tagGroup.defaultValue;
@@ -45178,7 +46097,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45178
46097
  const usedValues = /* @__PURE__ */ new Map();
45179
46098
  if (resolvedTasks) {
45180
46099
  for (const group of visibleGroups) {
45181
- const key = group.name.toLowerCase();
46100
+ const key = tagAttrKey(group.name);
45182
46101
  const used = /* @__PURE__ */ new Set();
45183
46102
  for (const rt of resolvedTasks) {
45184
46103
  const val = rt.effectiveMetadata[key];
@@ -45189,7 +46108,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45189
46108
  }
45190
46109
  const filteredEntries = /* @__PURE__ */ new Map();
45191
46110
  for (const group of visibleGroups) {
45192
- const key = group.name.toLowerCase();
46111
+ const key = tagAttrKey(group.name);
45193
46112
  const used = usedValues.get(key);
45194
46113
  if (used && used.size > 0) {
45195
46114
  filteredEntries.set(
@@ -45210,7 +46129,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45210
46129
  const pillW = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD + iconReserve;
45211
46130
  let groupW = pillW;
45212
46131
  if (isActive) {
45213
- const entries = filteredEntries.get(group.name.toLowerCase()) ?? group.entries;
46132
+ const entries = filteredEntries.get(tagAttrKey(group.name)) ?? group.entries;
45214
46133
  let entriesW = 0;
45215
46134
  for (const entry of entries) {
45216
46135
  entriesW += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + measureLegendText(entry.value, LEGEND_ENTRY_FONT_SIZE) + LEGEND_ENTRY_TRAIL;
@@ -45267,7 +46186,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45267
46186
  },
45268
46187
  ...onControlsToggle !== void 0 && { onControlsToggle },
45269
46188
  onEntryHover: (groupName, entryValue) => {
45270
- const tagKey = groupName.toLowerCase();
46189
+ const tagKey = tagAttrKey(groupName);
45271
46190
  if (entryValue) {
45272
46191
  const ev = entryValue.toLowerCase();
45273
46192
  chartG.selectAll(".gantt-task").each(function() {
@@ -45957,7 +46876,7 @@ function buildTagLaneRowList(resolved, swimlaneGroup, collapsedLanes) {
45957
46876
  (g) => g.name.toLowerCase() === swimlaneGroup.toLowerCase()
45958
46877
  );
45959
46878
  if (!tagGroup) return null;
45960
- const tagKey = tagGroup.name.toLowerCase();
46879
+ const tagKey = tagAttrKey(tagGroup.name);
45961
46880
  const rows = [];
45962
46881
  const buckets = /* @__PURE__ */ new Map();
45963
46882
  const unbucketed = [];
@@ -46141,10 +47060,11 @@ function renderTimeScaleHorizontal(g, scale, _innerWidth, innerHeight, textColor
46141
47060
  g.append("text").attr("class", "gantt-scale-tick").attr("x", tick.pos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", textColor).attr("opacity", opacity).text(tick.label);
46142
47061
  }
46143
47062
  }
46144
- var BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
47063
+ var BAR_H, ROW_GAP, GROUP_GAP3, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
46145
47064
  var init_renderer12 = __esm({
46146
47065
  "src/gantt/renderer.ts"() {
46147
47066
  "use strict";
47067
+ init_tag_groups();
46148
47068
  init_fonts();
46149
47069
  init_palettes();
46150
47070
  init_color_utils();
@@ -46159,7 +47079,7 @@ var init_renderer12 = __esm({
46159
47079
  init_title_constants();
46160
47080
  BAR_H = 22;
46161
47081
  ROW_GAP = 6;
46162
- GROUP_GAP2 = 14;
47082
+ GROUP_GAP3 = 14;
46163
47083
  MILESTONE_SIZE = 10;
46164
47084
  MIN_LEFT_MARGIN = 120;
46165
47085
  BOTTOM_MARGIN = 40;
@@ -46226,7 +47146,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
46226
47146
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
46227
47147
  const sTitleY = ctx.structural(TITLE_Y);
46228
47148
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
46229
- const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE10);
47149
+ const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE11);
46230
47150
  const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
46231
47151
  const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
46232
47152
  const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
@@ -46506,7 +47426,7 @@ function renderStateForExport(content, theme, palette) {
46506
47426
  document.body.removeChild(container);
46507
47427
  }
46508
47428
  }
46509
- var DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE3, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
47429
+ var DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, GROUP_LABEL_FONT_SIZE3, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
46510
47430
  var init_state_renderer = __esm({
46511
47431
  "src/graph/state-renderer.ts"() {
46512
47432
  "use strict";
@@ -46525,7 +47445,7 @@ var init_state_renderer = __esm({
46525
47445
  DIAGRAM_PADDING12 = 20;
46526
47446
  MAX_SCALE7 = 3;
46527
47447
  NODE_FONT_SIZE6 = 13;
46528
- EDGE_LABEL_FONT_SIZE10 = 11;
47448
+ EDGE_LABEL_FONT_SIZE11 = 11;
46529
47449
  GROUP_LABEL_FONT_SIZE3 = 11;
46530
47450
  ARROWHEAD_W5 = 10;
46531
47451
  ARROWHEAD_H5 = 7;
@@ -48234,7 +49154,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48234
49154
  const panelWidth = 280;
48235
49155
  const textAreaWidth = panelWidth - silhouetteZone - CARD_PADDING_X3;
48236
49156
  const descLineH = 14;
48237
- const descLines = parsed.persona.description ? wrapText2(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
49157
+ const descLines = parsed.persona.description ? wrapNoteBody(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
48238
49158
  const descRowH = descLines.length > 0 ? descLines.length * descLineH + 8 : 0;
48239
49159
  const panelHeight = titleRowH + descRowH;
48240
49160
  const panelX = layout.totalWidth - PADDING2 - panelWidth;
@@ -48259,8 +49179,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48259
49179
  personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", "none").attr("stroke", personaColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48260
49180
  personaG.append("text").attr("x", textX).attr("y", panelY + CARD_PADDING_Y3 + FONT_SIZE_STEP).attr("font-size", FONT_SIZE_STEP).attr("font-weight", "500").attr("fill", onPersonaText).text(parsed.persona.name);
48261
49181
  for (let li = 0; li < descLines.length; li++) {
48262
- const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
48263
- renderInlineText(lineEl, descLines[li], palette, FONT_SIZE_META);
49182
+ const line11 = descLines[li];
49183
+ const indent = line11.kind === "plain" ? 0 : NOTE_BULLET_INDENT;
49184
+ const lineY = panelY + titleRowH + descLineH * (li + 1);
49185
+ if (line11.kind === "bullet-first") {
49186
+ personaG.append("text").attr("x", textX).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText).text("\u2022");
49187
+ }
49188
+ const lineEl = personaG.append("text").attr("x", textX + indent).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
49189
+ renderInlineText(lineEl, line11.text, palette, FONT_SIZE_META);
48264
49190
  }
48265
49191
  if (onNavigateToLine) {
48266
49192
  personaG.style("cursor", "pointer").on("click", () => {
@@ -48299,7 +49225,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48299
49225
  return;
48300
49226
  }
48301
49227
  const isScore = groupName === "Score";
48302
- const attrName = isScore ? "data-score" : `data-tag-${groupName.toLowerCase()}`;
49228
+ const attrName = isScore ? "data-score" : `data-tag-${tagAttrKey(groupName)}`;
48303
49229
  const matches = (el) => el.getAttribute(attrName) === entryValue;
48304
49230
  svg.selectAll(".journey-step").each(function() {
48305
49231
  const hit = matches(this);
@@ -48780,7 +49706,7 @@ function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
48780
49706
  (g) => g.name.toLowerCase() === activeGroup.toLowerCase()
48781
49707
  );
48782
49708
  if (!group) return void 0;
48783
- const tagValue = step.tags[group.name.toLowerCase()];
49709
+ const tagValue = step.tags[tagAttrKey(group.name)];
48784
49710
  if (!tagValue) return void 0;
48785
49711
  const entry = group.entries.find(
48786
49712
  (e) => e.value.toLowerCase() === tagValue.toLowerCase()
@@ -48995,11 +49921,13 @@ var DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_H
48995
49921
  var init_renderer14 = __esm({
48996
49922
  "src/journey-map/renderer.ts"() {
48997
49923
  "use strict";
49924
+ init_tag_groups();
48998
49925
  init_color_utils();
48999
49926
  init_fonts();
49000
49927
  init_parser23();
49001
49928
  init_layout13();
49002
49929
  init_inline_markdown();
49930
+ init_note_box();
49003
49931
  init_legend_integration();
49004
49932
  init_tag_groups();
49005
49933
  init_scaling();
@@ -49339,10 +50267,10 @@ function renderedDescNodeHeight(numLines, scale) {
49339
50267
  return headerH + textBlockH + bottomPad;
49340
50268
  }
49341
50269
  function wrapEdgeLabelText(label, description, maxWidth = EDGE_LABEL_MAX_WIDTH) {
49342
- const labelLines = label ? wrapTextToWidth(label, EDGE_LABEL_FONT_SIZE11, maxWidth) : [];
50270
+ const labelLines = label ? wrapTextToWidth(label, EDGE_LABEL_FONT_SIZE12, maxWidth) : [];
49343
50271
  const descLines = [];
49344
50272
  for (const d of description) {
49345
- descLines.push(...wrapTextToWidth(d, EDGE_LABEL_FONT_SIZE11, maxWidth));
50273
+ descLines.push(...wrapTextToWidth(d, EDGE_LABEL_FONT_SIZE12, maxWidth));
49346
50274
  }
49347
50275
  return { labelLines, descLines };
49348
50276
  }
@@ -49480,9 +50408,9 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
49480
50408
  const lineCount = labelLines.length + descLines.length;
49481
50409
  let labelPxW = 0;
49482
50410
  for (const l of labelLines)
49483
- labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE11));
50411
+ labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
49484
50412
  for (const l of descLines)
49485
- labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE11));
50413
+ labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
49486
50414
  const { labelX, labelY, labelAngle } = computeEdgeLabelPosition(
49487
50415
  midAngle,
49488
50416
  radius,
@@ -49584,9 +50512,9 @@ function fitToCanvas(nodes, edges, parsed, _cx, _cy, radius, width, height, _isC
49584
50512
  );
49585
50513
  let textWidth = 0;
49586
50514
  for (const l of labelLines)
49587
- textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE11));
50515
+ textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
49588
50516
  for (const l of descLines)
49589
- textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE11));
50517
+ textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
49590
50518
  if (textWidth === 0) continue;
49591
50519
  const normAngle = (le.labelAngle % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
49592
50520
  const isRight = normAngle < Math.PI * 0.4 || normAngle > Math.PI * 1.6;
@@ -49660,7 +50588,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
49660
50588
  const midAngle = srcExitAngle + dir * visibleSweep / 2;
49661
50589
  return { path, midAngle };
49662
50590
  }
49663
- var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT4, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
50591
+ var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE12, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT4, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
49664
50592
  var init_layout14 = __esm({
49665
50593
  "src/cycle/layout.ts"() {
49666
50594
  "use strict";
@@ -49671,7 +50599,7 @@ var init_layout14 = __esm({
49671
50599
  LABEL_FONT_SIZE4 = 13;
49672
50600
  CIRCLE_LABEL_FONT_SIZE = 16;
49673
50601
  DESC_FONT_SIZE6 = 11;
49674
- EDGE_LABEL_FONT_SIZE11 = 11;
50602
+ EDGE_LABEL_FONT_SIZE12 = 11;
49675
50603
  MIN_NODE_WIDTH4 = 70;
49676
50604
  MAX_NODE_WIDTH3 = 260;
49677
50605
  DESC_MIN_WIDTH = 140;
@@ -49817,7 +50745,7 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
49817
50745
  const scaledNodeFont = Math.max(6, Math.round(NODE_FONT_SIZE7 * layout.scale));
49818
50746
  const scaledEdgeLabelFont = Math.max(
49819
50747
  6,
49820
- Math.round(EDGE_LABEL_FONT_SIZE12 * layout.scale)
50748
+ Math.round(EDGE_LABEL_FONT_SIZE13 * layout.scale)
49821
50749
  );
49822
50750
  const CIRCLE_LABEL_FONT_SIZE2 = 16;
49823
50751
  const scaledCircleLabelFont = Math.max(
@@ -49980,7 +50908,7 @@ function ensureArrowMarker(defs, color, strokeWidth) {
49980
50908
  const mh = Math.max(1.5, mw * 0.5);
49981
50909
  defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${mw} ${mh}`).attr("refX", mw * 0.1).attr("refY", mh / 2).attr("markerWidth", mw).attr("markerHeight", mh).attr("orient", "auto").append("polygon").attr("points", `0,0 ${mw},${mh / 2} 0,${mh}`).attr("fill", color);
49982
50910
  }
49983
- var NODE_FONT_SIZE7, DESC_FONT_SIZE7, EDGE_LABEL_FONT_SIZE12, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
50911
+ var NODE_FONT_SIZE7, DESC_FONT_SIZE7, EDGE_LABEL_FONT_SIZE13, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
49984
50912
  var init_renderer15 = __esm({
49985
50913
  "src/cycle/renderer.ts"() {
49986
50914
  "use strict";
@@ -49997,7 +50925,7 @@ var init_renderer15 = __esm({
49997
50925
  init_text_measure();
49998
50926
  NODE_FONT_SIZE7 = 13;
49999
50927
  DESC_FONT_SIZE7 = 11;
50000
- EDGE_LABEL_FONT_SIZE12 = 11;
50928
+ EDGE_LABEL_FONT_SIZE13 = 11;
50001
50929
  DESC_LINE_HEIGHT8 = 15;
50002
50930
  TITLE_AREA_HEIGHT = 50;
50003
50931
  }
@@ -51834,7 +52762,7 @@ function layoutMap(resolvedIn, data, size, opts) {
51834
52762
  (g) => g.name.toLowerCase() === groupName.toLowerCase()
51835
52763
  );
51836
52764
  if (!group) return null;
51837
- const val = tags[group.name.toLowerCase()] ?? group.defaultValue;
52765
+ const val = tags[tagAttrKey(group.name)] ?? group.defaultValue;
51838
52766
  if (!val) return null;
51839
52767
  const entry = group.entries.find(
51840
52768
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52448,7 +53376,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52448
53376
  if (directHex)
52449
53377
  return { fill: directHex, stroke: mix(directHex, palette.text, 18) };
52450
53378
  for (const group of resolved.tagGroups) {
52451
- const val = p.tags[group.name.toLowerCase()];
53379
+ const val = p.tags[tagAttrKey(group.name)];
52452
53380
  if (!val) continue;
52453
53381
  const entry = group.entries.find(
52454
53382
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52463,7 +53391,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52463
53391
  };
52464
53392
  const lineColor = (tags) => {
52465
53393
  for (const group of resolved.tagGroups) {
52466
- const val = tags[group.name.toLowerCase()];
53394
+ const val = tags[tagAttrKey(group.name)];
52467
53395
  if (!val) continue;
52468
53396
  const entry = group.entries.find(
52469
53397
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -53729,6 +54657,7 @@ var FIT_PAD, PATH_DIGITS, THIN_TOL, ALBERS_SKEW_MAX, RAMP_FLOOR2, R_DEFAULT, R_M
53729
54657
  var init_layout15 = __esm({
53730
54658
  "src/map/layout.ts"() {
53731
54659
  "use strict";
54660
+ init_tag_groups();
53732
54661
  init_color_utils();
53733
54662
  init_geo();
53734
54663
  init_colorize();
@@ -56142,7 +57071,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56142
57071
  }
56143
57072
  return result;
56144
57073
  }
56145
- const groupKey = group.name.toLowerCase();
57074
+ const groupKey = tagAttrKey(group.name);
56146
57075
  const participantMeta = /* @__PURE__ */ new Map();
56147
57076
  for (const p of parsed.participants) {
56148
57077
  participantMeta.set(p.id, { ...p.metadata || {} });
@@ -56178,6 +57107,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56178
57107
  var init_tag_resolution = __esm({
56179
57108
  "src/sequence/tag-resolution.ts"() {
56180
57109
  "use strict";
57110
+ init_tag_groups();
56181
57111
  }
56182
57112
  });
56183
57113
 
@@ -56653,7 +57583,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56653
57583
  }
56654
57584
  }
56655
57585
  const getTagColor = (value) => value ? tagValueToColor.get(value.toLowerCase()) : void 0;
56656
- const tagKey = activeTagGroup?.toLowerCase();
57586
+ const tagKey = activeTagGroup ? tagAttrKey(activeTagGroup) : void 0;
56657
57587
  const hiddenMsgIndices = /* @__PURE__ */ new Set();
56658
57588
  if (collapsedSections && collapsedSections.size > 0) {
56659
57589
  const sectionGroups = groupMessagesBySection(elements, messages);
@@ -57245,11 +58175,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57245
58175
  const boxH = sBoxH + GROUP_PADDING_TOP2 + GROUP_PADDING_BOTTOM2;
57246
58176
  const groupTagValue = tagKey && group.metadata?.[tagKey];
57247
58177
  const groupTagColor = getTagColor(groupTagValue || void 0);
57248
- const fillColor = groupTagColor ? mix(
57249
- groupTagColor,
57250
- themeBaseBg(palette, isDark),
57251
- isDark ? 15 : 20
57252
- ) : isDark ? palette.surface : palette.bg;
58178
+ const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
57253
58179
  const strokeColor = groupTagColor || palette.textMuted;
57254
58180
  const groupG = svg.append("g").attr("class", "group-box-wrapper").attr("data-group-toggle", "").attr("data-group-line", String(group.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("cursor", "pointer");
57255
58181
  groupG.append("title").text("Click to collapse");
@@ -57299,11 +58225,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57299
58225
  );
57300
58226
  participantG.attr("data-group-toggle", "").attr("data-group-line", String(meta.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "false").attr("cursor", "pointer");
57301
58227
  participantG.append("title").text("Click to expand");
57302
- const pFill = effectiveTagColor ? mix(
57303
- effectiveTagColor,
57304
- themeBaseBg(palette, isDark),
57305
- isDark ? 30 : 40
57306
- ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
58228
+ const pFill = effectiveTagColor ? mix(effectiveTagColor, themeBaseBg(palette, isDark), isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
57307
58229
  const pStroke = effectiveTagColor || palette.border;
57308
58230
  participantG.append("rect").attr("x", -boxW / 2).attr("y", -GROUP_PADDING_TOP2).attr("width", boxW).attr("height", fullH).attr("rx", 6).attr("fill", pFill).attr("stroke", pStroke).attr("stroke-width", 1.5);
57309
58231
  participantG.append("text").attr("x", 0).attr("y", -GROUP_PADDING_TOP2 + fullH / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", ctx.text(13)).attr("font-weight", 500).text(participant.label);
@@ -57799,6 +58721,7 @@ var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, LABEL_FONT_S
57799
58721
  var init_renderer20 = __esm({
57800
58722
  "src/sequence/renderer.ts"() {
57801
58723
  "use strict";
58724
+ init_tag_groups();
57802
58725
  init_color_utils();
57803
58726
  init_inline_markdown();
57804
58727
  init_fonts();
@@ -58953,6 +59876,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
58953
59876
  init_parser3();
58954
59877
 
58955
59878
  // src/timeline/renderer.ts
59879
+ init_tag_groups();
58956
59880
  init_fonts();
58957
59881
  init_time_ticks();
58958
59882
  init_scaling();
@@ -59217,7 +60141,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
59217
60141
  });
59218
60142
  let tagLanes = null;
59219
60143
  if (resolvedSwimlaneTG) {
59220
- const tagKey = resolvedSwimlaneTG.toLowerCase();
60144
+ const tagKey = tagAttrKey(resolvedSwimlaneTG);
59221
60145
  const tagGroup = parsed.timelineTagGroups.find(
59222
60146
  (g) => g.name.toLowerCase() === tagKey
59223
60147
  );
@@ -59523,7 +60447,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59523
60447
  onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
59524
60448
  },
59525
60449
  onEntryHover: (groupName, entryValue) => {
59526
- const tagKey = groupName.toLowerCase();
60450
+ const tagKey = tagAttrKey(groupName);
59527
60451
  if (entryValue) {
59528
60452
  const tagVal = entryValue.toLowerCase();
59529
60453
  fadeToTagValue(mainG, tagKey, tagVal);
@@ -59542,7 +60466,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59542
60466
  }
59543
60467
  },
59544
60468
  onGroupRendered: (groupName, groupEl, isActive) => {
59545
- const groupKey = groupName.toLowerCase();
60469
+ const groupKey = tagAttrKey(groupName);
59546
60470
  groupEl.attr("data-tag-group", groupKey);
59547
60471
  if (isActive && !viewMode) {
59548
60472
  const isSwimActive = currentSwimlaneGroup?.toLowerCase() === groupKey;
@@ -63264,7 +64188,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
63264
64188
 
63265
64189
  // src/auto/index.ts
63266
64190
  init_safe_href();
63267
- var VERSION = "0.32.1";
64191
+ var VERSION = "0.34.0";
63268
64192
  var DEFAULTS = {
63269
64193
  theme: "auto",
63270
64194
  palette: "slate",