@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/index.cjs CHANGED
@@ -1158,6 +1158,9 @@ var init_parsing = __esm({
1158
1158
  });
1159
1159
 
1160
1160
  // src/utils/tag-groups.ts
1161
+ function tagAttrKey(name) {
1162
+ return name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
1163
+ }
1161
1164
  function stripDefaultModifier(text) {
1162
1165
  if (/\bdefault\s*$/.test(text)) {
1163
1166
  return { text: text.replace(/\s+default\s*$/, "").trim(), isDefault: true };
@@ -1303,17 +1306,17 @@ function parseTagDeclaration(line11) {
1303
1306
  function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
1304
1307
  if (!activeGroupName) return void 0;
1305
1308
  const group = tagGroups.find(
1306
- (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
1309
+ (g) => tagAttrKey(g.name) === tagAttrKey(activeGroupName)
1307
1310
  );
1308
1311
  if (!group) return void 0;
1309
- const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
1312
+ const metaValue = metadata[tagAttrKey(group.name)] ?? (isContainer ? void 0 : group.defaultValue);
1310
1313
  if (!metaValue) return "#999999";
1311
1314
  return group.entries.find((e) => e.value.toLowerCase() === metaValue.toLowerCase())?.color ?? "#999999";
1312
1315
  }
1313
1316
  function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
1314
1317
  if (tagGroups.length === 0) return;
1315
1318
  const groupMap = /* @__PURE__ */ new Map();
1316
- for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
1319
+ for (const g of tagGroups) groupMap.set(tagAttrKey(g.name), g);
1317
1320
  for (const entity of entities) {
1318
1321
  for (const [key, value] of Object.entries(entity.metadata)) {
1319
1322
  const group = groupMap.get(key);
@@ -1350,10 +1353,10 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1350
1353
  `'none' is a reserved keyword and cannot be used as a tag group name`
1351
1354
  );
1352
1355
  }
1353
- if (!VALID_TAG_IDENT_RE.test(group.name)) {
1356
+ if (!VALID_TAG_IDENT_RE.test(tagAttrKey(group.name))) {
1354
1357
  report(
1355
1358
  group.lineNumber,
1356
- `Tag group name "${group.name}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
1359
+ `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)`
1357
1360
  );
1358
1361
  }
1359
1362
  if (group.alias != null && !VALID_TAG_IDENT_RE.test(group.alias)) {
@@ -1365,7 +1368,7 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1365
1368
  }
1366
1369
  }
1367
1370
  function cascadeTagMetadata(roots, tagGroups) {
1368
- const keys = tagGroups.map((g) => g.name.toLowerCase());
1371
+ const keys = tagGroups.map((g) => tagAttrKey(g.name));
1369
1372
  if (keys.length === 0) return;
1370
1373
  const walk = (node, inherited) => {
1371
1374
  const childInherited = { ...inherited };
@@ -1386,7 +1389,7 @@ function injectDefaultTagMetadata(entities, tagGroups, skip) {
1386
1389
  for (const group of tagGroups) {
1387
1390
  if (group.defaultValue) {
1388
1391
  defaults.push({
1389
- key: group.name.toLowerCase(),
1392
+ key: tagAttrKey(group.name),
1390
1393
  value: group.defaultValue
1391
1394
  });
1392
1395
  }
@@ -2777,7 +2780,7 @@ function parseVisualizationFull(content, palette) {
2777
2780
  );
2778
2781
  for (const group of result.timelineTagGroups) {
2779
2782
  if (!group.defaultValue) continue;
2780
- const key = group.name.toLowerCase();
2783
+ const key = tagAttrKey(group.name);
2781
2784
  for (const event of result.timelineEvents) {
2782
2785
  if (!event.metadata[key]) {
2783
2786
  event.metadata[key] = group.defaultValue;
@@ -5169,7 +5172,7 @@ function renderLegendD3(container, config, state, palette, isDark, callbacks, co
5169
5172
  };
5170
5173
  }
5171
5174
  function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, callbacks) {
5172
- const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", capsule.groupName.toLowerCase()).style("cursor", "pointer");
5175
+ const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", tagAttrKey(capsule.groupName)).style("cursor", "pointer");
5173
5176
  g.append("rect").attr("width", capsule.width).attr("height", capsule.height).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
5174
5177
  const pill = capsule.pill;
5175
5178
  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);
@@ -5177,7 +5180,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5177
5180
  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);
5178
5181
  if (capsule.gradient) {
5179
5182
  const gr = capsule.gradient;
5180
- const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
5183
+ const gradId = `dgmo-legend-ramp-${tagAttrKey(capsule.groupName).replace(/[^a-z0-9]+/g, "-")}`;
5181
5184
  const def = g.append("defs").append("linearGradient").attr("id", gradId);
5182
5185
  for (const stop of valueRampStops(gr.low, gr.high, { isDark })) {
5183
5186
  def.append("stop").attr("offset", `${stop.offset * 100}%`).attr("stop-color", stop.color);
@@ -5207,7 +5210,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5207
5210
  }
5208
5211
  }
5209
5212
  function renderPill(parent, pill, palette, groupBg, callbacks) {
5210
- const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", pill.groupName.toLowerCase()).style("cursor", "pointer");
5213
+ const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", tagAttrKey(pill.groupName)).style("cursor", "pointer");
5211
5214
  g.append("rect").attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", groupBg);
5212
5215
  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);
5213
5216
  if (callbacks?.onGroupToggle) {
@@ -5313,6 +5316,7 @@ var LEGEND_TEXT_DY;
5313
5316
  var init_legend_d3 = __esm({
5314
5317
  "src/utils/legend-d3.ts"() {
5315
5318
  "use strict";
5319
+ init_tag_groups();
5316
5320
  init_legend_constants();
5317
5321
  init_legend_layout();
5318
5322
  init_color_utils();
@@ -9299,7 +9303,7 @@ function parseERDiagram(content, palette) {
9299
9303
  );
9300
9304
  for (const group of result.tagGroups) {
9301
9305
  if (!group.defaultValue) continue;
9302
- const key = group.name.toLowerCase();
9306
+ const key = tagAttrKey(group.name);
9303
9307
  for (const table of result.tables) {
9304
9308
  if (!table.metadata[key]) {
9305
9309
  const mutableMeta = tableMetadataMap.get(table.id);
@@ -9529,12 +9533,12 @@ function parseOrg(content, palette) {
9529
9533
  if (tagBlockMatch.alias) {
9530
9534
  metaAliasMap.set(
9531
9535
  normalizeName(tagBlockMatch.alias),
9532
- tagBlockMatch.name.toLowerCase()
9536
+ tagAttrKey(tagBlockMatch.name)
9533
9537
  );
9534
9538
  }
9535
9539
  metaAliasMap.set(
9536
9540
  normalizeName(tagBlockMatch.name),
9537
- tagBlockMatch.name.toLowerCase()
9541
+ tagAttrKey(tagBlockMatch.name)
9538
9542
  );
9539
9543
  result.tagGroups.push(currentTagGroup);
9540
9544
  continue;
@@ -9920,12 +9924,12 @@ function parseSitemap(content, palette) {
9920
9924
  if (tagBlockMatch.alias) {
9921
9925
  metaAliasMap.set(
9922
9926
  normalizeName(tagBlockMatch.alias),
9923
- tagBlockMatch.name.toLowerCase()
9927
+ tagAttrKey(tagBlockMatch.name)
9924
9928
  );
9925
9929
  }
9926
9930
  metaAliasMap.set(
9927
9931
  normalizeName(tagBlockMatch.name),
9928
- tagBlockMatch.name.toLowerCase()
9932
+ tagAttrKey(tagBlockMatch.name)
9929
9933
  );
9930
9934
  result.tagGroups.push(newTagGroup);
9931
9935
  continue;
@@ -10095,6 +10099,14 @@ function parseSitemap(content, palette) {
10095
10099
  continue;
10096
10100
  }
10097
10101
  }
10102
+ const enclosingContainer = nestedPageInsideContainer(indent, indentStack);
10103
+ if (enclosingContainer) {
10104
+ pushError(
10105
+ lineNumber,
10106
+ `Pages inside container '[${enclosingContainer}]' cannot have indented sub-pages. Containers hold a flat list of pages.`
10107
+ );
10108
+ continue;
10109
+ }
10098
10110
  const node = parseNodeLabel2(
10099
10111
  trimmed,
10100
10112
  lineNumber,
@@ -10242,6 +10254,16 @@ function attachNode2(node, indent, indentStack, result) {
10242
10254
  }
10243
10255
  indentStack.push({ node, indent });
10244
10256
  }
10257
+ function nestedPageInsideContainer(indent, indentStack) {
10258
+ let idx = indentStack.length - 1;
10259
+ while (idx >= 0 && indentStack[idx].indent >= indent) idx--;
10260
+ if (idx < 0) return null;
10261
+ if (indentStack[idx].node.isContainer) return null;
10262
+ for (let k = idx - 1; k >= 0; k--) {
10263
+ if (indentStack[k].node.isContainer) return indentStack[k].node.label;
10264
+ }
10265
+ return null;
10266
+ }
10245
10267
  function findParentNode(indent, indentStack) {
10246
10268
  for (let i = indentStack.length - 1; i >= 0; i--) {
10247
10269
  if (indentStack[i].indent < indent) {
@@ -10825,12 +10847,12 @@ function parsePert(content, parseOpts = {}) {
10825
10847
  if (tagBlockMatch.alias) {
10826
10848
  metaAliasMap.set(
10827
10849
  tagBlockMatch.alias.toLowerCase(),
10828
- tagBlockMatch.name.toLowerCase()
10850
+ tagAttrKey(tagBlockMatch.name)
10829
10851
  );
10830
10852
  }
10831
10853
  metaAliasMap.set(
10832
10854
  normalizeName(tagBlockMatch.name),
10833
- tagBlockMatch.name.toLowerCase()
10855
+ tagAttrKey(tagBlockMatch.name)
10834
10856
  );
10835
10857
  tagGroups.push(currentTagGroup);
10836
10858
  if (tagBlockMatch.inlineValues) {
@@ -12295,10 +12317,10 @@ function emitCapsule(capsule, palette, groupBg, pillBorder) {
12295
12317
  `<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>`
12296
12318
  );
12297
12319
  }
12298
- return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(capsule.groupName.toLowerCase())}" style="cursor:pointer">${inner.join("")}</g>`;
12320
+ return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(tagAttrKey(capsule.groupName))}" style="cursor:pointer">${inner.join("")}</g>`;
12299
12321
  }
12300
12322
  function emitPill(pill, palette, groupBg) {
12301
- 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>`;
12323
+ 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>`;
12302
12324
  }
12303
12325
  function renderLegendSvg(groups, options) {
12304
12326
  if (groups.length === 0) return { svg: "", height: 0, width: 0 };
@@ -12346,6 +12368,7 @@ function renderLegendSvg(groups, options) {
12346
12368
  var init_legend_svg = __esm({
12347
12369
  "src/utils/legend-svg.ts"() {
12348
12370
  "use strict";
12371
+ init_tag_groups();
12349
12372
  init_legend_constants();
12350
12373
  init_legend_layout();
12351
12374
  init_color_utils();
@@ -15036,12 +15059,12 @@ function parseKanban(content, palette) {
15036
15059
  if (tagBlockMatch.alias) {
15037
15060
  metaAliasMap.set(
15038
15061
  normalizeName(tagBlockMatch.alias),
15039
- tagBlockMatch.name.toLowerCase()
15062
+ tagAttrKey(tagBlockMatch.name)
15040
15063
  );
15041
15064
  }
15042
15065
  metaAliasMap.set(
15043
15066
  normalizeName(tagBlockMatch.name),
15044
- tagBlockMatch.name.toLowerCase()
15067
+ tagAttrKey(tagBlockMatch.name)
15045
15068
  );
15046
15069
  result.tagGroups.push(currentTagGroup);
15047
15070
  continue;
@@ -15230,7 +15253,7 @@ function parseKanban(content, palette) {
15230
15253
  }
15231
15254
  for (const group of result.tagGroups) {
15232
15255
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
15233
- tagValueSets.set(group.name.toLowerCase(), values);
15256
+ tagValueSets.set(tagAttrKey(group.name), values);
15234
15257
  }
15235
15258
  for (const col of result.columns) {
15236
15259
  if (col.wipLimit != null && col.cards.length > col.wipLimit) {
@@ -15492,12 +15515,12 @@ function parseC4(content, palette) {
15492
15515
  if (tagBlockMatch.alias) {
15493
15516
  metaAliasMap.set(
15494
15517
  normalizeName(tagBlockMatch.alias),
15495
- tagBlockMatch.name.toLowerCase()
15518
+ tagAttrKey(tagBlockMatch.name)
15496
15519
  );
15497
15520
  }
15498
15521
  metaAliasMap.set(
15499
15522
  normalizeName(tagBlockMatch.name),
15500
- tagBlockMatch.name.toLowerCase()
15523
+ tagAttrKey(tagBlockMatch.name)
15501
15524
  );
15502
15525
  result.tagGroups.push(currentTagGroup);
15503
15526
  continue;
@@ -17180,7 +17203,7 @@ function parseInfra(content) {
17180
17203
  }
17181
17204
  for (const tg of result.tagGroups) {
17182
17205
  if (!tg.defaultValue) continue;
17183
- const key = (tg.alias ?? tg.name).toLowerCase();
17206
+ const key = tagAttrKey(tg.alias ?? tg.name);
17184
17207
  for (const node of result.nodes) {
17185
17208
  if (node.isEdge) continue;
17186
17209
  if (!(key in node.tags)) {
@@ -17317,6 +17340,7 @@ var CONNECTION_RE, SIMPLE_CONNECTION_RE, ASYNC_CONNECTION_RE, ASYNC_SIMPLE_CONNE
17317
17340
  var init_parser15 = __esm({
17318
17341
  "src/infra/parser.ts"() {
17319
17342
  "use strict";
17343
+ init_tag_groups();
17320
17344
  init_diagnostics();
17321
17345
  init_description_helpers();
17322
17346
  init_arrows();
@@ -17862,13 +17886,10 @@ function parseGantt(content, palette) {
17862
17886
  if (tagMatch.alias) {
17863
17887
  metaAliasMap.set(
17864
17888
  normalizeName(tagMatch.alias),
17865
- tagMatch.name.toLowerCase()
17889
+ tagAttrKey(tagMatch.name)
17866
17890
  );
17867
17891
  }
17868
- metaAliasMap.set(
17869
- normalizeName(tagMatch.name),
17870
- tagMatch.name.toLowerCase()
17871
- );
17892
+ metaAliasMap.set(normalizeName(tagMatch.name), tagAttrKey(tagMatch.name));
17872
17893
  continue;
17873
17894
  }
17874
17895
  const topWorkweekMatch = line11.match(WORKWEEK_RE);
@@ -18794,7 +18815,7 @@ function parseMap(content, palette) {
18794
18815
  const aliasMap = /* @__PURE__ */ new Map();
18795
18816
  const open = { tag: null, route: null, poi: null };
18796
18817
  const registry = () => withTagAliases(MAP_REGISTRY, new Set(aliasMap.keys()));
18797
- const tagGroupNames = () => new Set(tagGroups.map((g) => g.name.toLowerCase()));
18818
+ const tagGroupNames = () => new Set(tagGroups.map((g) => tagAttrKey(g.name)));
18798
18819
  const closeBlocks = () => {
18799
18820
  open.tag = null;
18800
18821
  open.route = null;
@@ -18978,7 +18999,7 @@ function parseMap(content, palette) {
18978
18999
  entries: [],
18979
19000
  lineNumber: line11
18980
19001
  };
18981
- if (m.alias) aliasMap.set(m.alias.toLowerCase(), m.name.toLowerCase());
19002
+ if (m.alias) aliasMap.set(m.alias.toLowerCase(), tagAttrKey(m.name));
18982
19003
  tagGroups.push(group);
18983
19004
  open.tag = group;
18984
19005
  if (m.inlineValues?.length) {
@@ -19511,12 +19532,12 @@ function parseBoxesAndLines(content, palette) {
19511
19532
  if (tagBlockMatch.alias) {
19512
19533
  metaAliasMap.set(
19513
19534
  normalizeName(tagBlockMatch.alias),
19514
- tagBlockMatch.name.toLowerCase()
19535
+ tagAttrKey(tagBlockMatch.name)
19515
19536
  );
19516
19537
  }
19517
19538
  metaAliasMap.set(
19518
19539
  normalizeName(tagBlockMatch.name),
19519
- tagBlockMatch.name.toLowerCase()
19540
+ tagAttrKey(tagBlockMatch.name)
19520
19541
  );
19521
19542
  if (tagBlockMatch.inlineValues) {
19522
19543
  for (const rawVal of tagBlockMatch.inlineValues) {
@@ -20257,12 +20278,12 @@ function parseMindmap(content, palette) {
20257
20278
  if (tagBlockMatch.alias) {
20258
20279
  aliasMap.set(
20259
20280
  tagBlockMatch.alias.toLowerCase(),
20260
- tagBlockMatch.name.toLowerCase()
20281
+ tagAttrKey(tagBlockMatch.name)
20261
20282
  );
20262
20283
  }
20263
20284
  aliasMap.set(
20264
- tagBlockMatch.name.toLowerCase(),
20265
- tagBlockMatch.name.toLowerCase()
20285
+ tagAttrKey(tagBlockMatch.name),
20286
+ tagAttrKey(tagBlockMatch.name)
20266
20287
  );
20267
20288
  result.tagGroups.push(currentTagGroup);
20268
20289
  continue;
@@ -21971,7 +21992,18 @@ function parseJourneyMap(content, palette) {
21971
21992
  palette
21972
21993
  ) ?? void 0;
21973
21994
  } else {
21974
- personaName = afterKeyword;
21995
+ const { label, colorName } = peelTrailingColorName(afterKeyword);
21996
+ if (colorName) {
21997
+ personaName = label;
21998
+ personaColor = resolveColorWithDiagnostic(
21999
+ colorName,
22000
+ lineNumber,
22001
+ result.diagnostics,
22002
+ palette
22003
+ ) ?? void 0;
22004
+ } else {
22005
+ personaName = afterKeyword;
22006
+ }
21975
22007
  }
21976
22008
  }
21977
22009
  if (!personaName) {
@@ -22014,12 +22046,12 @@ function parseJourneyMap(content, palette) {
22014
22046
  if (tagBlockMatch.alias) {
22015
22047
  aliasMap.set(
22016
22048
  tagBlockMatch.alias.toLowerCase(),
22017
- tagBlockMatch.name.toLowerCase()
22049
+ tagAttrKey(tagBlockMatch.name)
22018
22050
  );
22019
22051
  }
22020
22052
  aliasMap.set(
22021
- tagBlockMatch.name.toLowerCase(),
22022
- tagBlockMatch.name.toLowerCase()
22053
+ tagAttrKey(tagBlockMatch.name),
22054
+ tagAttrKey(tagBlockMatch.name)
22023
22055
  );
22024
22056
  result.tagGroups.push(currentTagGroup);
22025
22057
  continue;
@@ -22163,7 +22195,7 @@ function parseJourneyMap(content, palette) {
22163
22195
  }
22164
22196
  for (const group of result.tagGroups) {
22165
22197
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
22166
- tagValueSets.set(group.name.toLowerCase(), values);
22198
+ tagValueSets.set(tagAttrKey(group.name), values);
22167
22199
  }
22168
22200
  const allSteps = hasPhases ? result.phases.flatMap((p) => p.steps) : result.steps;
22169
22201
  for (const step of allSteps) {
@@ -24149,7 +24181,7 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
24149
24181
  const groups = [];
24150
24182
  for (const group of tagGroups) {
24151
24183
  if (group.entries.length === 0) continue;
24152
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
24184
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
24153
24185
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
24154
24186
  if (visibleEntries.length === 0) continue;
24155
24187
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -24762,7 +24794,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
24762
24794
  const totalHeight = finalMaxY - minY + MARGIN * 2;
24763
24795
  const usedValuesByGroup = /* @__PURE__ */ new Map();
24764
24796
  for (const group of parsed.tagGroups) {
24765
- const key = group.name.toLowerCase();
24797
+ const key = tagAttrKey(group.name);
24766
24798
  const used = /* @__PURE__ */ new Set();
24767
24799
  const walk = (node) => {
24768
24800
  if (!node.isContainer && node.metadata[key]) {
@@ -24820,6 +24852,7 @@ var import_d3_hierarchy, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT, HEAD
24820
24852
  var init_layout = __esm({
24821
24853
  "src/org/layout.ts"() {
24822
24854
  "use strict";
24855
+ init_tag_groups();
24823
24856
  import_d3_hierarchy = require("d3-hierarchy");
24824
24857
  init_tag_groups();
24825
24858
  init_legend_constants();
@@ -25181,14 +25214,14 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25181
25214
  const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
25182
25215
  const displayNames = /* @__PURE__ */ new Map();
25183
25216
  for (const group of parsed.tagGroups) {
25184
- displayNames.set(group.name.toLowerCase(), group.name);
25217
+ displayNames.set(tagAttrKey(group.name), group.name);
25185
25218
  }
25186
25219
  const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
25187
25220
  const colorOff = parsed.options?.["color"] === "off";
25188
25221
  for (const c of layout.containers) {
25189
25222
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
25190
25223
  if (activeTagGroup) {
25191
- const tagKey = activeTagGroup.toLowerCase();
25224
+ const tagKey = tagAttrKey(activeTagGroup);
25192
25225
  const metaValue = c.tagMetadata[tagKey];
25193
25226
  if (metaValue) {
25194
25227
  cG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25270,7 +25303,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25270
25303
  if (containerNodeIds.has(node.id)) continue;
25271
25304
  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));
25272
25305
  if (activeTagGroup) {
25273
- const tagKey = activeTagGroup.toLowerCase();
25306
+ const tagKey = tagAttrKey(activeTagGroup);
25274
25307
  const metaValue = node.tagMetadata[tagKey];
25275
25308
  if (metaValue) {
25276
25309
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25443,7 +25476,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25443
25476
  const computedLayout = legendHandle.getLayout();
25444
25477
  if (computedLayout.activeCapsule?.addonX != null) {
25445
25478
  const capsule = computedLayout.activeCapsule;
25446
- const groupKey = capsule.groupName.toLowerCase();
25479
+ const groupKey = tagAttrKey(capsule.groupName);
25447
25480
  const isHidden = hiddenAttributes?.has(groupKey) ?? false;
25448
25481
  const activeGroupEl = legendParentBase.select(
25449
25482
  `[data-legend-group="${groupKey}"]`
@@ -25482,6 +25515,7 @@ var d3Selection6, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCE
25482
25515
  var init_renderer = __esm({
25483
25516
  "src/org/renderer.ts"() {
25484
25517
  "use strict";
25518
+ init_tag_groups();
25485
25519
  d3Selection6 = __toESM(require("d3-selection"), 1);
25486
25520
  init_fonts();
25487
25521
  init_export_container();
@@ -25567,7 +25601,7 @@ function computeLegendGroups2(tagGroups, usedValuesByGroup) {
25567
25601
  const groups = [];
25568
25602
  for (const group of tagGroups) {
25569
25603
  if (group.entries.length === 0) continue;
25570
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
25604
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
25571
25605
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
25572
25606
  if (visibleEntries.length === 0) continue;
25573
25607
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -26047,7 +26081,7 @@ function layoutSitemap(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, e
26047
26081
  totalHeight += MARGIN2;
26048
26082
  const usedValuesByGroup = /* @__PURE__ */ new Map();
26049
26083
  for (const group of parsed.tagGroups) {
26050
- const key = group.name.toLowerCase();
26084
+ const key = tagAttrKey(group.name);
26051
26085
  const used = /* @__PURE__ */ new Set();
26052
26086
  const walk = (node) => {
26053
26087
  if (!node.isContainer && node.metadata[key]) {
@@ -26091,6 +26125,7 @@ var import_dagre, LABEL_FONT_SIZE3, META_FONT_SIZE3, CONTAINER_LABEL_FONT_SIZE2,
26091
26125
  var init_layout2 = __esm({
26092
26126
  "src/sitemap/layout.ts"() {
26093
26127
  "use strict";
26128
+ init_tag_groups();
26094
26129
  import_dagre = __toESM(require("@dagrejs/dagre"), 1);
26095
26130
  init_tag_groups();
26096
26131
  init_legend_constants();
@@ -26360,10 +26395,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26360
26395
  const displayNames = /* @__PURE__ */ new Map();
26361
26396
  const tagColors = /* @__PURE__ */ new Map();
26362
26397
  for (const group of parsed.tagGroups) {
26363
- displayNames.set(group.name.toLowerCase(), group.name);
26398
+ displayNames.set(tagAttrKey(group.name), group.name);
26364
26399
  for (const entry of group.entries) {
26365
26400
  tagColors.set(
26366
- `${group.name.toLowerCase()}:${entry.value.toLowerCase()}`,
26401
+ `${tagAttrKey(group.name)}:${entry.value.toLowerCase()}`,
26367
26402
  entry.color
26368
26403
  );
26369
26404
  }
@@ -26380,7 +26415,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26380
26415
  );
26381
26416
  }
26382
26417
  if (activeTagGroup) {
26383
- const tagKey = activeTagGroup.toLowerCase();
26418
+ const tagKey = tagAttrKey(activeTagGroup);
26384
26419
  const tagVal = c.tagMetadata[tagKey];
26385
26420
  if (tagVal) cG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26386
26421
  }
@@ -26443,7 +26478,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26443
26478
  nodeG.style("cursor", "pointer").on("click", () => onClickItem(node.lineNumber));
26444
26479
  }
26445
26480
  if (activeTagGroup) {
26446
- const tagKey = activeTagGroup.toLowerCase();
26481
+ const tagKey = tagAttrKey(activeTagGroup);
26447
26482
  const tagVal = node.tagMetadata[tagKey];
26448
26483
  if (tagVal) nodeG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26449
26484
  }
@@ -26570,7 +26605,7 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
26570
26605
  const computedLayout = legendHandle.getLayout();
26571
26606
  if (computedLayout.activeCapsule?.addonX != null) {
26572
26607
  const capsule = computedLayout.activeCapsule;
26573
- const groupKey = capsule.groupName.toLowerCase();
26608
+ const groupKey = tagAttrKey(capsule.groupName);
26574
26609
  const isHidden = hiddenAttributes?.has(groupKey) ?? false;
26575
26610
  const activeGroupEl = parent.select(`[data-legend-group="${groupKey}"]`);
26576
26611
  if (!activeGroupEl.empty()) {
@@ -26635,6 +26670,7 @@ var d3Selection7, d3Shape2, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHE
26635
26670
  var init_renderer2 = __esm({
26636
26671
  "src/sitemap/renderer.ts"() {
26637
26672
  "use strict";
26673
+ init_tag_groups();
26638
26674
  d3Selection7 = __toESM(require("d3-selection"), 1);
26639
26675
  d3Shape2 = __toESM(require("d3-shape"), 1);
26640
26676
  init_fonts();
@@ -26683,7 +26719,7 @@ function resolveCardTagMeta(card, tagGroups, hiddenMetaGroups) {
26683
26719
  const meta = [];
26684
26720
  for (const group of tagGroups) {
26685
26721
  if (hiddenMetaGroups?.includes(group.name.toLowerCase())) continue;
26686
- const tagValue = card.tags[group.name.toLowerCase()];
26722
+ const tagValue = card.tags[tagAttrKey(group.name)];
26687
26723
  const value = tagValue ?? group.defaultValue;
26688
26724
  if (!value) continue;
26689
26725
  const entry = group.entries.find(
@@ -26703,7 +26739,7 @@ function resolveCardTagColor(card, tagGroups, activeTagGroup) {
26703
26739
  (g) => g.name.toLowerCase() === activeTagGroup.toLowerCase()
26704
26740
  );
26705
26741
  if (!group) return card.color;
26706
- const tagValue = card.tags[group.name.toLowerCase()];
26742
+ const tagValue = card.tags[tagAttrKey(group.name)];
26707
26743
  const value = tagValue ?? group.defaultValue;
26708
26744
  if (!value) return void 0;
26709
26745
  const entry = group.entries.find(
@@ -27013,7 +27049,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
27013
27049
  );
27014
27050
  const cg = g.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27015
27051
  if (activeTagGroup) {
27016
- const tagKey = activeTagGroup.toLowerCase();
27052
+ const tagKey = tagAttrKey(activeTagGroup);
27017
27053
  const tagValue = card.tags[tagKey];
27018
27054
  const group = parsed.tagGroups.find(
27019
27055
  (tg) => tg.name.toLowerCase() === tagKey
@@ -27074,7 +27110,7 @@ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
27074
27110
  return iconG;
27075
27111
  }
27076
27112
  function bucketCardsBySwimlane(columns, swimlaneGroup) {
27077
- const tagKey = swimlaneGroup.name.toLowerCase();
27113
+ const tagKey = tagAttrKey(swimlaneGroup.name);
27078
27114
  const buckets = /* @__PURE__ */ new Map();
27079
27115
  for (const entry of swimlaneGroup.entries) {
27080
27116
  buckets.set(entry.value.toLowerCase(), {
@@ -27362,7 +27398,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
27362
27398
  );
27363
27399
  const cg = parent.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27364
27400
  if (activeTagGroup) {
27365
- const tagKey = activeTagGroup.toLowerCase();
27401
+ const tagKey = tagAttrKey(activeTagGroup);
27366
27402
  const group = tagGroups.find((tg) => tg.name.toLowerCase() === tagKey);
27367
27403
  const value = card.tags[tagKey] ?? group?.defaultValue;
27368
27404
  if (value) {
@@ -27395,6 +27431,7 @@ var d3Selection8, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PAD
27395
27431
  var init_renderer3 = __esm({
27396
27432
  "src/kanban/renderer.ts"() {
27397
27433
  "use strict";
27434
+ init_tag_groups();
27398
27435
  d3Selection8 = __toESM(require("d3-selection"), 1);
27399
27436
  init_fonts();
27400
27437
  init_color_utils();
@@ -28377,7 +28414,7 @@ function classifyEREntities(tables, relationships) {
28377
28414
  const variance = indegreeValues.reduce((a, b) => a + (b - mean) ** 2, 0) / indegreeValues.length;
28378
28415
  const stddev = Math.sqrt(variance);
28379
28416
  const sorted = [...indegreeValues].sort((a, b) => a - b);
28380
- const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28417
+ const median2 = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28381
28418
  for (const table of tables) {
28382
28419
  const id = table.id;
28383
28420
  const cols = table.columns;
@@ -28418,7 +28455,7 @@ function classifyEREntities(tables, relationships) {
28418
28455
  const nameMatchesLookup = LOOKUP_NAME_SUFFIXES.some(
28419
28456
  (s) => nameLower.endsWith(s)
28420
28457
  );
28421
- if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median) {
28458
+ if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median2) {
28422
28459
  result.set(id, "lookup");
28423
28460
  continue;
28424
28461
  }
@@ -28657,7 +28694,7 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
28657
28694
  seriesColors2[ni % seriesColors2.length];
28658
28695
  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);
28659
28696
  if (activeTagGroup) {
28660
- const tagKey = activeTagGroup.toLowerCase();
28697
+ const tagKey = tagAttrKey(activeTagGroup);
28661
28698
  const tagValue = node.metadata[tagKey];
28662
28699
  if (tagValue) {
28663
28700
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -28836,6 +28873,7 @@ var d3Selection10, d3Shape4, DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COL
28836
28873
  var init_renderer5 = __esm({
28837
28874
  "src/er/renderer.ts"() {
28838
28875
  "use strict";
28876
+ init_tag_groups();
28839
28877
  d3Selection10 = __toESM(require("d3-selection"), 1);
28840
28878
  d3Shape4 = __toESM(require("d3-shape"), 1);
28841
28879
  init_fonts();
@@ -29006,63 +29044,6 @@ function ensureArrowMarkers(defs, colors) {
29006
29044
  ).attr("fill", color);
29007
29045
  }
29008
29046
  }
29009
- function edgePolylineMidpoint(points) {
29010
- if (points.length === 0) return { x: 0, y: 0 };
29011
- if (points.length === 1) return { x: points[0].x, y: points[0].y };
29012
- let total = 0;
29013
- const segLen = [];
29014
- for (let k = 1; k < points.length; k++) {
29015
- const len = Math.hypot(
29016
- points[k].x - points[k - 1].x,
29017
- points[k].y - points[k - 1].y
29018
- );
29019
- segLen.push(len);
29020
- total += len;
29021
- }
29022
- let half = total / 2;
29023
- for (let k = 1; k < points.length; k++) {
29024
- const len = segLen[k - 1];
29025
- if (half <= len || k === points.length - 1) {
29026
- const t = len === 0 ? 0 : Math.min(1, half / len);
29027
- return {
29028
- x: points[k - 1].x + (points[k].x - points[k - 1].x) * t,
29029
- y: points[k - 1].y + (points[k].y - points[k - 1].y) * t
29030
- };
29031
- }
29032
- half -= len;
29033
- }
29034
- const last = points[points.length - 1];
29035
- return { x: last.x, y: last.y };
29036
- }
29037
- function resolveEdgeLabelOverlaps(labels) {
29038
- const MAX_PASSES = 8;
29039
- const PAD = 4;
29040
- for (let pass = 0; pass < MAX_PASSES; pass++) {
29041
- let moved = false;
29042
- for (let i = 0; i < labels.length; i++) {
29043
- for (let j = i + 1; j < labels.length; j++) {
29044
- const a = labels[i];
29045
- const b = labels[j];
29046
- const dx = Math.abs(a.x - b.x);
29047
- const dy = Math.abs(a.y - b.y);
29048
- const overlapX = (a.width + b.width) / 2 + PAD - dx;
29049
- const overlapY = (a.height + b.height) / 2 + PAD - dy;
29050
- if (overlapX > 0 && overlapY > 0) {
29051
- const shift = overlapY / 2 + 1;
29052
- if (a.y < b.y) {
29053
- a.y -= shift;
29054
- b.y += shift;
29055
- } else {
29056
- a.y += shift;
29057
- b.y -= shift;
29058
- }
29059
- moved = true;
29060
- }
29061
- }
29062
- }
29063
- if (!moved) break;
29064
- }
29065
- }
29066
29047
  function renderBoxesAndLines(container, parsed, layout, palette, isDark, options) {
29067
29048
  const {
29068
29049
  onClickItem,
@@ -29269,8 +29250,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29269
29250
  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);
29270
29251
  }
29271
29252
  }
29272
- const labelPositions = [];
29273
- const edgeGroups = /* @__PURE__ */ new Map();
29274
29253
  for (let i = 0; i < layout.edges.length; i++) {
29275
29254
  const le = layout.edges[i];
29276
29255
  const color = edgeColorMap.get(i) ?? palette.textMuted;
@@ -29289,7 +29268,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29289
29268
  const nodeLayout = layoutNodeMap.get(le.source);
29290
29269
  if (nodeLayout) {
29291
29270
  const edgeG2 = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
29292
- edgeGroups.set(i, edgeG2);
29293
29271
  const markerId2 = `bl-arrow-${color.replace("#", "")}`;
29294
29272
  const cx = nodeLayout.x;
29295
29273
  const cy = nodeLayout.y;
@@ -29338,7 +29316,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29338
29316
  }
29339
29317
  if (points.length < 2) continue;
29340
29318
  const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
29341
- edgeGroups.set(i, edgeG);
29342
29319
  const markerId = `bl-arrow-${color.replace("#", "")}`;
29343
29320
  const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
29344
29321
  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})`);
@@ -29346,31 +29323,6 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29346
29323
  const revId = `bl-arrow-rev-${color.replace("#", "")}`;
29347
29324
  path.attr("marker-start", `url(#${revId})`);
29348
29325
  }
29349
- if (le.label && le.points.length > 0) {
29350
- const lw = measureText(le.label, sEdgeLabelFontSize);
29351
- const labelH = sEdgeLabelFontSize + 6;
29352
- const mid = edgePolylineMidpoint(le.points);
29353
- let ly = mid.y;
29354
- if (le.parallelCount > 1 && le.yOffset !== 0) {
29355
- ly += (le.yOffset < 0 ? -labelH : labelH) * 0.5;
29356
- }
29357
- labelPositions.push({
29358
- x: mid.x,
29359
- y: ly,
29360
- width: lw + 8,
29361
- height: labelH,
29362
- idx: i
29363
- });
29364
- }
29365
- }
29366
- resolveEdgeLabelOverlaps(labelPositions);
29367
- for (const lp of labelPositions) {
29368
- const le = layout.edges[lp.idx];
29369
- if (!le.label) continue;
29370
- const edgeG = edgeGroups.get(lp.idx);
29371
- const target = edgeG ?? diagramG;
29372
- 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);
29373
- 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);
29374
29326
  }
29375
29327
  for (const ln of layout.nodes) {
29376
29328
  const node = nodeMap.get(ln.label);
@@ -29421,8 +29373,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29421
29373
  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);
29422
29374
  const desc = node.description;
29423
29375
  if (desc && desc.length > 0 && !hideDescriptions) {
29424
- const MAX_LABEL_LINES3 = 3;
29425
- const fitted = fitLabelToHeader(node.label, ln.width, MAX_LABEL_LINES3);
29376
+ const MAX_LABEL_LINES4 = 3;
29377
+ const fitted = fitLabelToHeader(node.label, ln.width, MAX_LABEL_LINES4);
29426
29378
  const labelLines = fitted.lines;
29427
29379
  const labelLineH = fitted.fontSize * 1.3;
29428
29380
  const labelTotalH = labelLines.length * labelLineH;
@@ -29556,6 +29508,35 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29556
29508
  }
29557
29509
  }
29558
29510
  }
29511
+ const labelLayer = diagramG.append("g").attr("class", "bl-edge-labels");
29512
+ const labelLineHeight = sEdgeLabelFontSize * 1.3;
29513
+ for (const le of layout.edges) {
29514
+ if (!le.labelLines || le.labelLines.length === 0) continue;
29515
+ if (le.labelX === void 0 || le.labelY === void 0) continue;
29516
+ const lx = le.labelX;
29517
+ const ly = le.labelY;
29518
+ if (hidden.size > 0) {
29519
+ let isHidden = false;
29520
+ for (const [groupKey, hiddenVals] of hidden) {
29521
+ const val = le.metadata[groupKey];
29522
+ if (val && hiddenVals.has(val.toLowerCase())) {
29523
+ isHidden = true;
29524
+ break;
29525
+ }
29526
+ }
29527
+ if (isHidden) continue;
29528
+ }
29529
+ const lw = le.labelWidth ?? 0;
29530
+ const lh = le.labelHeight ?? sEdgeLabelFontSize + 6;
29531
+ const lines = le.labelLines;
29532
+ const labelG = labelLayer.append("g").attr("class", "bl-edge-label").attr("data-line-number", String(le.lineNumber));
29533
+ 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);
29534
+ 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);
29535
+ const firstY = ly - (lines.length - 1) / 2 * labelLineHeight + sEdgeLabelFontSize / 3;
29536
+ lines.forEach((line11, k) => {
29537
+ text.append("tspan").attr("x", lx).attr("y", firstY + k * labelLineHeight).text(line11);
29538
+ });
29539
+ }
29559
29540
  const hasDescriptions = parsed.nodes.some(
29560
29541
  (n) => n.description && n.description.length > 0
29561
29542
  );
@@ -29672,6 +29653,222 @@ var init_renderer6 = __esm({
29672
29653
  }
29673
29654
  });
29674
29655
 
29656
+ // src/boxes-and-lines/label-placement.ts
29657
+ function measureEdgeLabel(label, fontSize = EDGE_LABEL_FONT_SIZE6, maxWidth = LABEL_MAX_WIDTH) {
29658
+ let lines = wrapTextToWidth(label, fontSize, maxWidth, { hardBreak: true });
29659
+ if (lines.length > MAX_LABEL_LINES) {
29660
+ const kept = lines.slice(0, MAX_LABEL_LINES - 1);
29661
+ const rest = lines.slice(MAX_LABEL_LINES - 1).join(" ");
29662
+ kept.push(truncateText(rest, fontSize, maxWidth));
29663
+ lines = kept;
29664
+ }
29665
+ let textW = 0;
29666
+ for (const ln of lines) {
29667
+ const w = measureText(ln, fontSize);
29668
+ if (w > textW) textW = w;
29669
+ }
29670
+ return {
29671
+ lines,
29672
+ width: textW + 2 * H_PAD,
29673
+ height: lines.length * fontSize * LABEL_LINE_HEIGHT + 2 * V_PAD
29674
+ };
29675
+ }
29676
+ function edgePolylineMidpoint(points) {
29677
+ return pointAtArcFraction(points, 0.5).p;
29678
+ }
29679
+ function pointAtArcFraction(points, t) {
29680
+ if (points.length === 0) return { p: { x: 0, y: 0 }, nx: 0, ny: 1 };
29681
+ if (points.length === 1)
29682
+ return { p: { x: points[0].x, y: points[0].y }, nx: 0, ny: 1 };
29683
+ const segLen = [];
29684
+ let total = 0;
29685
+ for (let k = 1; k < points.length; k++) {
29686
+ const len = Math.hypot(
29687
+ points[k].x - points[k - 1].x,
29688
+ points[k].y - points[k - 1].y
29689
+ );
29690
+ segLen.push(len);
29691
+ total += len;
29692
+ }
29693
+ let want = total * Math.max(0, Math.min(1, t));
29694
+ for (let k = 1; k < points.length; k++) {
29695
+ const len = segLen[k - 1];
29696
+ if (want <= len || k === points.length - 1) {
29697
+ const f = len === 0 ? 0 : Math.min(1, want / len);
29698
+ const ax = points[k - 1].x;
29699
+ const ay = points[k - 1].y;
29700
+ const bx = points[k].x;
29701
+ const by = points[k].y;
29702
+ const dx = bx - ax;
29703
+ const dy = by - ay;
29704
+ const dlen = Math.hypot(dx, dy) || 1;
29705
+ return {
29706
+ p: { x: ax + dx * f, y: ay + dy * f },
29707
+ nx: -dy / dlen,
29708
+ ny: dx / dlen
29709
+ };
29710
+ }
29711
+ want -= len;
29712
+ }
29713
+ const last = points[points.length - 1];
29714
+ return { p: { x: last.x, y: last.y }, nx: 0, ny: 1 };
29715
+ }
29716
+ function rectFromCenter(cx, cy, w, h) {
29717
+ return {
29718
+ minX: cx - w / 2,
29719
+ minY: cy - h / 2,
29720
+ maxX: cx + w / 2,
29721
+ maxY: cy + h / 2
29722
+ };
29723
+ }
29724
+ function overlapsAny(cx, cy, w, h, obstacles, pad2) {
29725
+ const minX = cx - w / 2 - pad2;
29726
+ const minY = cy - h / 2 - pad2;
29727
+ const maxX = cx + w / 2 + pad2;
29728
+ const maxY = cy + h / 2 + pad2;
29729
+ for (const o of obstacles) {
29730
+ if (minX < o.maxX && maxX > o.minX && minY < o.maxY && maxY > o.minY)
29731
+ return true;
29732
+ }
29733
+ return false;
29734
+ }
29735
+ function slideFractions() {
29736
+ const out = [0.5];
29737
+ for (let k = 1; k <= SLIDE_SAMPLES; k++) {
29738
+ const off = k * 0.08;
29739
+ if (0.5 - off >= 0.12) out.push(0.5 - off);
29740
+ if (0.5 + off <= 0.88) out.push(0.5 + off);
29741
+ }
29742
+ return out;
29743
+ }
29744
+ function findClearPosition(w, h, points, obstacles) {
29745
+ const ts = slideFractions();
29746
+ for (const t of ts) {
29747
+ const { p } = pointAtArcFraction(points, t);
29748
+ if (!overlapsAny(p.x, p.y, w, h, obstacles, BOX_CLEAR_PAD)) return p;
29749
+ }
29750
+ for (let mag = PERP_STEP; mag <= PERP_MAX; mag += PERP_STEP) {
29751
+ for (const t of ts) {
29752
+ const { p, nx, ny } = pointAtArcFraction(points, t);
29753
+ for (const sign of [-1, 1]) {
29754
+ const x = p.x + nx * mag * sign;
29755
+ const y = p.y + ny * mag * sign;
29756
+ if (!overlapsAny(x, y, w, h, obstacles, BOX_CLEAR_PAD)) return { x, y };
29757
+ }
29758
+ }
29759
+ }
29760
+ return null;
29761
+ }
29762
+ function resolveLabelOverlaps(boxes) {
29763
+ const MAX_PASSES = 8;
29764
+ const PAD = 4;
29765
+ for (let pass = 0; pass < MAX_PASSES; pass++) {
29766
+ let moved = false;
29767
+ for (let i = 0; i < boxes.length; i++) {
29768
+ for (let j = i + 1; j < boxes.length; j++) {
29769
+ const a = boxes[i];
29770
+ const b = boxes[j];
29771
+ const dx = Math.abs(a.cx - b.cx);
29772
+ const dy = Math.abs(a.cy - b.cy);
29773
+ const overlapX = (a.w + b.w) / 2 + PAD - dx;
29774
+ const overlapY = (a.h + b.h) / 2 + PAD - dy;
29775
+ if (overlapX > 0 && overlapY > 0) {
29776
+ const shift = overlapY / 2 + 1;
29777
+ if (a.cy < b.cy) {
29778
+ a.cy -= shift;
29779
+ b.cy += shift;
29780
+ } else {
29781
+ a.cy += shift;
29782
+ b.cy -= shift;
29783
+ }
29784
+ moved = true;
29785
+ }
29786
+ }
29787
+ }
29788
+ if (!moved) break;
29789
+ }
29790
+ }
29791
+ function placeEdgeLabels(layout, opts) {
29792
+ const fontSize = opts?.fontSize ?? EDGE_LABEL_FONT_SIZE6;
29793
+ const maxWidth = opts?.maxWidth ?? LABEL_MAX_WIDTH;
29794
+ const obstacles = [];
29795
+ for (const n of layout.nodes)
29796
+ obstacles.push(rectFromCenter(n.x, n.y, n.width, n.height));
29797
+ for (const g of layout.groups)
29798
+ if (g.collapsed)
29799
+ obstacles.push(rectFromCenter(g.x, g.y, g.width, g.height));
29800
+ const boxes = [];
29801
+ layout.edges.forEach((e, idx) => {
29802
+ if (!e.label || e.points.length < 2) return;
29803
+ const m = measureEdgeLabel(e.label, fontSize, maxWidth);
29804
+ const mid = edgePolylineMidpoint(e.points);
29805
+ let cy = mid.y;
29806
+ if (e.parallelCount > 1 && e.yOffset !== 0)
29807
+ cy += (e.yOffset < 0 ? -1 : 1) * (m.height / 2);
29808
+ boxes.push({
29809
+ edgeIdx: idx,
29810
+ cx: mid.x,
29811
+ cy,
29812
+ w: m.width,
29813
+ h: m.height,
29814
+ lines: m.lines,
29815
+ resolved: true
29816
+ });
29817
+ });
29818
+ for (const box of boxes) {
29819
+ if (!overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
29820
+ continue;
29821
+ const e = layout.edges[box.edgeIdx];
29822
+ const clear = findClearPosition(box.w, box.h, e.points, obstacles);
29823
+ if (clear) {
29824
+ box.cx = clear.x;
29825
+ box.cy = clear.y;
29826
+ } else {
29827
+ box.resolved = false;
29828
+ }
29829
+ }
29830
+ resolveLabelOverlaps(boxes);
29831
+ for (const box of boxes) {
29832
+ if (overlapsAny(box.cx, box.cy, box.w, box.h, obstacles, BOX_CLEAR_PAD))
29833
+ box.resolved = false;
29834
+ }
29835
+ const boxByIdx = /* @__PURE__ */ new Map();
29836
+ for (const b of boxes) boxByIdx.set(b.edgeIdx, b);
29837
+ const edges = layout.edges.map((e, idx) => {
29838
+ const b = boxByIdx.get(idx);
29839
+ if (!b) return e;
29840
+ return {
29841
+ ...e,
29842
+ labelX: b.cx,
29843
+ labelY: b.cy,
29844
+ labelWidth: b.w,
29845
+ labelHeight: b.h,
29846
+ labelLines: b.lines
29847
+ };
29848
+ });
29849
+ return {
29850
+ layout: { ...layout, edges },
29851
+ unresolved: boxes.filter((b) => !b.resolved).map((b) => b.edgeIdx)
29852
+ };
29853
+ }
29854
+ 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;
29855
+ var init_label_placement = __esm({
29856
+ "src/boxes-and-lines/label-placement.ts"() {
29857
+ "use strict";
29858
+ init_text_measure();
29859
+ EDGE_LABEL_FONT_SIZE6 = 11;
29860
+ LABEL_MAX_WIDTH = 160;
29861
+ MAX_LABEL_LINES = 3;
29862
+ LABEL_LINE_HEIGHT = 1.3;
29863
+ H_PAD = 6;
29864
+ V_PAD = 3;
29865
+ BOX_CLEAR_PAD = 4;
29866
+ PERP_STEP = 8;
29867
+ PERP_MAX = 40;
29868
+ SLIDE_SAMPLES = 9;
29869
+ }
29870
+ });
29871
+
29675
29872
  // src/boxes-and-lines/layout-layered.ts
29676
29873
  function rng(s) {
29677
29874
  return () => {
@@ -29831,7 +30028,7 @@ function layeredCandidates(parsed, sizes, opts) {
29831
30028
  for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
29832
30029
  return c;
29833
30030
  };
29834
- const median = (vals) => {
30031
+ const median2 = (vals) => {
29835
30032
  if (vals.length === 0) return -1;
29836
30033
  vals.sort((x, y) => x - y);
29837
30034
  const m = Math.floor(vals.length / 2);
@@ -29846,7 +30043,7 @@ function layeredCandidates(parsed, sizes, opts) {
29846
30043
  const neigh = useUp ? up : down;
29847
30044
  const med = /* @__PURE__ */ new Map();
29848
30045
  for (const id of ids)
29849
- med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30046
+ med.set(id, median2(neigh.get(id).map((x) => orderOf.get(x))));
29850
30047
  const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
29851
30048
  const out = new Array(ids.length).fill(null);
29852
30049
  for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
@@ -30176,6 +30373,695 @@ var init_layout_layered = __esm({
30176
30373
  }
30177
30374
  });
30178
30375
 
30376
+ // src/boxes-and-lines/layout-grouped.ts
30377
+ function rng2(s) {
30378
+ return () => {
30379
+ s |= 0;
30380
+ s = s + 1831565813 | 0;
30381
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
30382
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
30383
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
30384
+ };
30385
+ }
30386
+ function median(vals) {
30387
+ if (vals.length === 0) return -1;
30388
+ vals.sort((x, y) => x - y);
30389
+ const m = Math.floor(vals.length / 2);
30390
+ if (vals.length % 2 === 1) return vals[m];
30391
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
30392
+ const left = vals[m - 1] - vals[0];
30393
+ const right = vals[vals.length - 1] - vals[m];
30394
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
30395
+ }
30396
+ function acyclicOrient(ids, edges) {
30397
+ const adj = /* @__PURE__ */ new Map();
30398
+ for (const l of ids) adj.set(l, []);
30399
+ for (const e of edges) adj.get(e.from)?.push({ to: e.to, idx: e.idx });
30400
+ const reversed = /* @__PURE__ */ new Set();
30401
+ const state = /* @__PURE__ */ new Map();
30402
+ for (const l of ids) state.set(l, 0);
30403
+ const stack = [];
30404
+ const visit = (root) => {
30405
+ stack.push(root);
30406
+ const it = /* @__PURE__ */ new Map();
30407
+ while (stack.length) {
30408
+ const u = stack[stack.length - 1];
30409
+ if (state.get(u) === 0) state.set(u, 1);
30410
+ const list = adj.get(u);
30411
+ let i = it.get(u) ?? 0;
30412
+ let descended = false;
30413
+ for (; i < list.length; i++) {
30414
+ const { to, idx } = list[i];
30415
+ const st = state.get(to);
30416
+ if (st === 1) reversed.add(idx);
30417
+ else if (st === 0) {
30418
+ it.set(u, i + 1);
30419
+ stack.push(to);
30420
+ descended = true;
30421
+ break;
30422
+ }
30423
+ }
30424
+ if (!descended) {
30425
+ it.set(u, list.length);
30426
+ state.set(u, 2);
30427
+ stack.pop();
30428
+ }
30429
+ }
30430
+ };
30431
+ for (const l of ids) if (state.get(l) === 0) visit(l);
30432
+ const dag = edges.map(
30433
+ (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 }
30434
+ );
30435
+ return { dag };
30436
+ }
30437
+ function longestPath(ids, dag) {
30438
+ const out = /* @__PURE__ */ new Map();
30439
+ const inDeg = /* @__PURE__ */ new Map();
30440
+ for (const l of ids) {
30441
+ out.set(l, []);
30442
+ inDeg.set(l, 0);
30443
+ }
30444
+ for (const d of dag) {
30445
+ out.get(d.from).push(d.to);
30446
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
30447
+ }
30448
+ const rank = /* @__PURE__ */ new Map();
30449
+ for (const l of ids) rank.set(l, 0);
30450
+ const queue = ids.filter((l) => (inDeg.get(l) ?? 0) === 0);
30451
+ const indeg2 = new Map(inDeg);
30452
+ let seen = 0;
30453
+ while (queue.length) {
30454
+ const u = queue.shift();
30455
+ seen++;
30456
+ for (const v of out.get(u)) {
30457
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
30458
+ indeg2.set(v, indeg2.get(v) - 1);
30459
+ if (indeg2.get(v) === 0) queue.push(v);
30460
+ }
30461
+ }
30462
+ return seen === ids.length ? rank : null;
30463
+ }
30464
+ function groupedTierCandidates(parsed, sizes, opts) {
30465
+ if (parsed.groups.length === 0) return [];
30466
+ const nCount = parsed.nodes.length;
30467
+ if (nCount < 3 || nCount > 60) return [];
30468
+ const isTB = parsed.direction === "TB";
30469
+ const labels = parsed.nodes.map((n) => n.label);
30470
+ const labelSet = new Set(labels);
30471
+ for (const e of parsed.edges)
30472
+ if (e.source.startsWith("__group_") || e.target.startsWith("__group_"))
30473
+ return [];
30474
+ const groupByLabel = new Map(parsed.groups.map((g) => [g.label, g]));
30475
+ const directGroupOf = /* @__PURE__ */ new Map();
30476
+ for (const grp of parsed.groups)
30477
+ for (const c of grp.children)
30478
+ if (labelSet.has(c)) directGroupOf.set(c, grp.label);
30479
+ const topGroupOf = (groupLabel) => {
30480
+ let cur = groupLabel;
30481
+ const guard = /* @__PURE__ */ new Set();
30482
+ while (true) {
30483
+ const g = groupByLabel.get(cur);
30484
+ if (!g?.parentGroup || guard.has(cur)) return cur;
30485
+ guard.add(cur);
30486
+ cur = g.parentGroup;
30487
+ }
30488
+ };
30489
+ const pathOf = (label) => {
30490
+ const direct = directGroupOf.get(label);
30491
+ if (!direct) return [];
30492
+ const chain = [direct];
30493
+ let cur = direct;
30494
+ const guard = /* @__PURE__ */ new Set([direct]);
30495
+ while (true) {
30496
+ const g = groupByLabel.get(cur);
30497
+ if (!g?.parentGroup || guard.has(g.parentGroup)) break;
30498
+ chain.push(g.parentGroup);
30499
+ guard.add(g.parentGroup);
30500
+ cur = g.parentGroup;
30501
+ }
30502
+ return chain.reverse();
30503
+ };
30504
+ const SINGLE = "\0node\0";
30505
+ const unitOf = (label) => {
30506
+ const direct = directGroupOf.get(label);
30507
+ return direct ? topGroupOf(direct) : SINGLE + label;
30508
+ };
30509
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
30510
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
30511
+ );
30512
+ if (edges.length === 0) return [];
30513
+ const unitOfNode = /* @__PURE__ */ new Map();
30514
+ for (const l of labels) unitOfNode.set(l, unitOf(l));
30515
+ const units = Array.from(new Set(labels.map((l) => unitOfNode.get(l))));
30516
+ const interUnit = edges.filter(({ e }) => unitOfNode.get(e.source) !== unitOfNode.get(e.target)).map(({ e, i }) => ({
30517
+ from: unitOfNode.get(e.source),
30518
+ to: unitOfNode.get(e.target),
30519
+ idx: i
30520
+ }));
30521
+ const { dag: unitDag } = acyclicOrient(units, interUnit);
30522
+ const tier = longestPath(
30523
+ units,
30524
+ unitDag.map((d) => ({ from: d.from, to: d.to }))
30525
+ );
30526
+ if (!tier) return [];
30527
+ const localRank = /* @__PURE__ */ new Map();
30528
+ const unitMembers = /* @__PURE__ */ new Map();
30529
+ for (const l of labels) {
30530
+ const u = unitOfNode.get(l);
30531
+ (unitMembers.get(u) ?? unitMembers.set(u, []).get(u)).push(l);
30532
+ }
30533
+ for (const [u, members] of unitMembers) {
30534
+ const memberSet = new Set(members);
30535
+ const intra = edges.filter(
30536
+ ({ e }) => unitOfNode.get(e.source) === u && unitOfNode.get(e.target) === u && memberSet.has(e.source) && memberSet.has(e.target)
30537
+ ).map(({ e, i }) => ({ from: e.source, to: e.target, idx: i }));
30538
+ const { dag } = acyclicOrient(members, intra);
30539
+ const lr = longestPath(
30540
+ members,
30541
+ dag.map((d) => ({ from: d.from, to: d.to }))
30542
+ );
30543
+ if (!lr) return [];
30544
+ for (const m of members) localRank.set(m, lr.get(m));
30545
+ }
30546
+ const maxTier = Math.max(...units.map((u) => tier.get(u)));
30547
+ const bandHeight = Array.from({ length: maxTier + 1 }, () => 1);
30548
+ for (const u of units) {
30549
+ const h = Math.max(
30550
+ ...unitMembers.get(u).map((m) => localRank.get(m) + 1)
30551
+ );
30552
+ const t = tier.get(u);
30553
+ bandHeight[t] = Math.max(bandHeight[t], h);
30554
+ }
30555
+ const tierBase = [];
30556
+ let accBase = 0;
30557
+ for (let t = 0; t <= maxTier; t++) {
30558
+ tierBase[t] = accBase;
30559
+ accBase += bandHeight[t];
30560
+ }
30561
+ const tierStartRanks = new Set(tierBase.slice(1));
30562
+ const globalRank = /* @__PURE__ */ new Map();
30563
+ for (const l of labels)
30564
+ globalRank.set(
30565
+ l,
30566
+ tierBase[tier.get(unitOfNode.get(l))] + localRank.get(l)
30567
+ );
30568
+ const maxRank = Math.max(...labels.map((l) => globalRank.get(l)));
30569
+ const node = /* @__PURE__ */ new Map();
30570
+ for (const n of parsed.nodes) {
30571
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30572
+ node.set(n.label, {
30573
+ id: n.label,
30574
+ dummy: false,
30575
+ rank: globalRank.get(n.label),
30576
+ cross: 0,
30577
+ thick: isTB ? s.width : s.height,
30578
+ depth: isTB ? s.height : s.width,
30579
+ realW: s.width,
30580
+ realH: s.height,
30581
+ unit: unitOfNode.get(n.label),
30582
+ path: pathOf(n.label)
30583
+ });
30584
+ }
30585
+ const chains = [];
30586
+ const segs = [];
30587
+ const backEdges = [];
30588
+ const flatEdges = [];
30589
+ for (const { e, i } of edges) {
30590
+ const rs = globalRank.get(e.source);
30591
+ const rt = globalRank.get(e.target);
30592
+ if (rs === rt) {
30593
+ flatEdges.push({ edgeIdx: i, a: e.source, b: e.target });
30594
+ continue;
30595
+ }
30596
+ if (rt < rs) {
30597
+ backEdges.push({ edgeIdx: i, src: e.source, tgt: e.target });
30598
+ continue;
30599
+ }
30600
+ const chain = [e.source];
30601
+ let prev = e.source;
30602
+ for (let r = rs + 1; r < rt; r++) {
30603
+ const id = `__d${i}_${r}`;
30604
+ node.set(id, {
30605
+ id,
30606
+ dummy: true,
30607
+ rank: r,
30608
+ cross: 0,
30609
+ thick: DUMMY_THICK2,
30610
+ depth: 0,
30611
+ realW: 0,
30612
+ realH: 0,
30613
+ unit: SINGLE + id,
30614
+ path: []
30615
+ });
30616
+ segs.push({ a: prev, b: id });
30617
+ chain.push(id);
30618
+ prev = id;
30619
+ }
30620
+ segs.push({ a: prev, b: e.target });
30621
+ chain.push(e.target);
30622
+ chains.push({ edgeIdx: i, chain });
30623
+ }
30624
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
30625
+ for (const n of node.values()) ranks[n.rank].push(n.id);
30626
+ const up = /* @__PURE__ */ new Map();
30627
+ const down = /* @__PURE__ */ new Map();
30628
+ for (const id of node.keys()) {
30629
+ up.set(id, []);
30630
+ down.set(id, []);
30631
+ }
30632
+ for (const s of segs) {
30633
+ down.get(s.a).push(s.b);
30634
+ up.get(s.b).push(s.a);
30635
+ }
30636
+ const orderOf = /* @__PURE__ */ new Map();
30637
+ const gapCrossings = (r) => {
30638
+ const lower = ranks[r + 1];
30639
+ if (!lower) return 0;
30640
+ const es = [];
30641
+ for (const a of ranks[r]) {
30642
+ const pa = orderOf.get(a);
30643
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
30644
+ }
30645
+ let c = 0;
30646
+ for (let i = 0; i < es.length; i++)
30647
+ for (let j = i + 1; j < es.length; j++) {
30648
+ const A = es[i], B = es[j];
30649
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
30650
+ }
30651
+ return c;
30652
+ };
30653
+ const totalCrossings = () => {
30654
+ let c = 0;
30655
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
30656
+ return c;
30657
+ };
30658
+ const wmedianRank = (r, useUp) => {
30659
+ const ids = ranks[r];
30660
+ const neigh = useUp ? up : down;
30661
+ const med = /* @__PURE__ */ new Map();
30662
+ for (const id of ids)
30663
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30664
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
30665
+ const out = new Array(ids.length).fill(null);
30666
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
30667
+ let mi = 0;
30668
+ for (let slot = 0; slot < out.length; slot++)
30669
+ if (out[slot] === null) out[slot] = movable[mi++];
30670
+ const final = out;
30671
+ final.forEach((id, i) => orderOf.set(id, i));
30672
+ ranks[r] = final;
30673
+ };
30674
+ const transpose = () => {
30675
+ let improved = true;
30676
+ let guard = 0;
30677
+ while (improved && guard++ < 12) {
30678
+ improved = false;
30679
+ for (let r = 0; r <= maxRank; r++) {
30680
+ const ids = ranks[r];
30681
+ for (let i = 0; i < ids.length - 1; i++) {
30682
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30683
+ const a = ids[i], b = ids[i + 1];
30684
+ ids[i] = b;
30685
+ ids[i + 1] = a;
30686
+ orderOf.set(b, i);
30687
+ orderOf.set(a, i + 1);
30688
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30689
+ if (after < before) improved = true;
30690
+ else {
30691
+ ids[i] = a;
30692
+ ids[i + 1] = b;
30693
+ orderOf.set(a, i);
30694
+ orderOf.set(b, i + 1);
30695
+ }
30696
+ }
30697
+ }
30698
+ }
30699
+ };
30700
+ const initOrder = (seed) => {
30701
+ const r = seed === 0 ? null : rng2(seed);
30702
+ for (let rr = 0; rr <= maxRank; rr++) {
30703
+ const ids = ranks[rr].slice();
30704
+ if (r)
30705
+ for (let i = ids.length - 1; i > 0; i--) {
30706
+ const j = Math.floor(r() * (i + 1));
30707
+ [ids[i], ids[j]] = [ids[j], ids[i]];
30708
+ }
30709
+ ranks[rr] = ids;
30710
+ ids.forEach((id, i) => orderOf.set(id, i));
30711
+ }
30712
+ };
30713
+ const restarts = opts?.restarts ?? (nCount <= 16 ? 20 : nCount <= 30 ? 12 : 6);
30714
+ const keepK = opts?.keepK ?? 3;
30715
+ const found = [];
30716
+ for (let s = 0; s < restarts; s++) {
30717
+ initOrder(s);
30718
+ let best = totalCrossings();
30719
+ let bestSnap = new Map(orderOf);
30720
+ for (let iter = 0; iter < 8; iter++) {
30721
+ if (iter % 2 === 0)
30722
+ for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
30723
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
30724
+ transpose();
30725
+ const c = totalCrossings();
30726
+ if (c < best) {
30727
+ best = c;
30728
+ bestSnap = new Map(orderOf);
30729
+ }
30730
+ if (c === 0) break;
30731
+ }
30732
+ for (const [k, v] of bestSnap) orderOf.set(k, v);
30733
+ for (let r = 0; r <= maxRank; r++)
30734
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
30735
+ const sig = ranks.map((r) => r.join(",")).join("|");
30736
+ if (!found.some((f) => f.sig === sig))
30737
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
30738
+ }
30739
+ found.sort((a, b) => a.cross - b.cross);
30740
+ const keep = found.slice(0, keepK);
30741
+ if (keep.length === 0) return [];
30742
+ const results = [];
30743
+ for (const cand of keep) {
30744
+ for (const [k, v] of cand.order) orderOf.set(k, v);
30745
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
30746
+ for (const n of node.values()) rk[n.rank].push(n.id);
30747
+ for (let r = 0; r <= maxRank; r++)
30748
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
30749
+ results.push(realize(rk));
30750
+ }
30751
+ return results;
30752
+ function weight(a, b) {
30753
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY2;
30754
+ if (a.dummy || b.dummy) return W_REAL_DUMMY2;
30755
+ return W_REAL_REAL2;
30756
+ }
30757
+ function placeWithSeparation(ids, desired, gap) {
30758
+ const n = ids.length;
30759
+ if (n === 0) return;
30760
+ const off = [0];
30761
+ for (let i = 1; i < n; i++) off[i] = off[i - 1] + gap[i];
30762
+ const d = desired.map((v, i) => v - off[i]);
30763
+ const blocks = [];
30764
+ for (let i = 0; i < n; i++) {
30765
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
30766
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
30767
+ const prev = blocks.pop();
30768
+ const count = prev.count + b.count;
30769
+ const sumOffset = prev.sumOffset + b.sumOffset;
30770
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
30771
+ }
30772
+ blocks.push(b);
30773
+ }
30774
+ const xs = new Array(n);
30775
+ for (const b of blocks)
30776
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
30777
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
30778
+ }
30779
+ function gapBetween(a, b) {
30780
+ const base = a.thick / 2 + b.thick / 2 + NODESEP2;
30781
+ if (a.dummy || b.dummy) return base;
30782
+ if (a.unit !== b.unit) return base + 2 * GROUP_PAD + GROUP_GAP;
30783
+ const da = a.path[a.path.length - 1];
30784
+ const db = b.path[b.path.length - 1];
30785
+ if (da !== db && (da || db)) return base + 2 * GROUP_PAD;
30786
+ return base;
30787
+ }
30788
+ function realize(rk) {
30789
+ const bandDepth = rk.map(
30790
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
30791
+ );
30792
+ const bandCenter = [];
30793
+ let acc = MARGIN4;
30794
+ for (let r = 0; r <= maxRank; r++) {
30795
+ if (r > 0) acc += RANKSEP2 + (tierStartRanks.has(r) ? TIER_GAP : 0);
30796
+ bandCenter[r] = acc + bandDepth[r] / 2;
30797
+ acc += bandDepth[r];
30798
+ }
30799
+ const gapsFor = (ids) => ids.map(
30800
+ (id, i) => i === 0 ? 0 : gapBetween(node.get(ids[i - 1]), node.get(id))
30801
+ );
30802
+ for (let r = 0; r <= maxRank; r++) {
30803
+ const ids = rk[r];
30804
+ const gap = gapsFor(ids);
30805
+ let x = MARGIN4;
30806
+ for (let i = 0; i < ids.length; i++) {
30807
+ const n = node.get(ids[i]);
30808
+ x += gap[i];
30809
+ n.cross = i === 0 ? MARGIN4 + n.thick / 2 : x;
30810
+ }
30811
+ }
30812
+ const ITER = 18;
30813
+ for (let it = 0; it < ITER; it++) {
30814
+ const downPass = it % 2 === 0;
30815
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
30816
+ for (const r of order) {
30817
+ const ids = rk[r];
30818
+ const desired = ids.map((id) => {
30819
+ const n = node.get(id);
30820
+ let sw = 0, sx2 = 0;
30821
+ for (const nb of up.get(id)) {
30822
+ const w = weight(n, node.get(nb));
30823
+ sw += w;
30824
+ sx2 += w * node.get(nb).cross;
30825
+ }
30826
+ for (const nb of down.get(id)) {
30827
+ const w = weight(n, node.get(nb));
30828
+ sw += w;
30829
+ sx2 += w * node.get(nb).cross;
30830
+ }
30831
+ return sw > 0 ? sx2 / sw : n.cross;
30832
+ });
30833
+ placeWithSeparation(ids, desired, gapsFor(ids));
30834
+ }
30835
+ }
30836
+ let minC = Infinity;
30837
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
30838
+ const shiftC = MARGIN4 - minC;
30839
+ for (const n of node.values()) n.cross += shiftC;
30840
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
30841
+ const layoutNodes = parsed.nodes.map((pn) => {
30842
+ const n = node.get(pn.label);
30843
+ const c = coord(n);
30844
+ return {
30845
+ label: pn.label,
30846
+ x: c.x,
30847
+ y: c.y,
30848
+ width: n.realW,
30849
+ height: n.realH
30850
+ };
30851
+ });
30852
+ const chainById = /* @__PURE__ */ new Map();
30853
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
30854
+ let minCross = Infinity, maxCross = -Infinity;
30855
+ for (const n of node.values()) {
30856
+ if (n.dummy) continue;
30857
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
30858
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
30859
+ }
30860
+ const GAP = 34;
30861
+ const LANE = 28;
30862
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
30863
+ const sideOf = (b) => {
30864
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
30865
+ return d >= 0 ? 1 : -1;
30866
+ };
30867
+ const backSorted = backEdges.map((b) => ({
30868
+ ...b,
30869
+ side: sideOf(b),
30870
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
30871
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
30872
+ const sideCounts = /* @__PURE__ */ new Map();
30873
+ for (const b of backSorted)
30874
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
30875
+ const laneCounter = /* @__PURE__ */ new Map();
30876
+ const backPoints = /* @__PURE__ */ new Map();
30877
+ for (const b of backSorted) {
30878
+ const s = b.side;
30879
+ const k = laneCounter.get(s) ?? 0;
30880
+ laneCounter.set(s, k + 1);
30881
+ const K = sideCounts.get(s);
30882
+ const src = node.get(b.src);
30883
+ const tgt = node.get(b.tgt);
30884
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
30885
+ const srcCtr = bandCenter[src.rank];
30886
+ const tgtCtr = bandCenter[tgt.rank];
30887
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
30888
+ const srcR = srcCtr - frac * faceLim(src.depth);
30889
+ const tgtR = tgtCtr + frac * faceLim(tgt.depth);
30890
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30891
+ const off = 10;
30892
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
30893
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
30894
+ backPoints.set(b.edgeIdx, [
30895
+ m(src.cross, srcCtr),
30896
+ m(srcEdgeC, srcR),
30897
+ m(laneC, srcR),
30898
+ m(laneC, tgtR),
30899
+ m(tgtEdgeC, tgtR),
30900
+ m(tgt.cross, tgtCtr)
30901
+ ]);
30902
+ }
30903
+ const flatPoints = /* @__PURE__ */ new Map();
30904
+ for (const f of flatEdges) {
30905
+ const a = node.get(f.a);
30906
+ const b = node.get(f.b);
30907
+ const ctr = bandCenter[a.rank];
30908
+ const bow = ctr - (a.depth / 2 + 26);
30909
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30910
+ flatPoints.set(f.edgeIdx, [
30911
+ m(a.cross, ctr),
30912
+ m(a.cross, bow),
30913
+ m(b.cross, bow),
30914
+ m(b.cross, ctr)
30915
+ ]);
30916
+ }
30917
+ const layoutEdges = [];
30918
+ for (let i = 0; i < parsed.edges.length; i++) {
30919
+ const e = parsed.edges[i];
30920
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
30921
+ let points;
30922
+ if (e.source === e.target) {
30923
+ const c = coord(node.get(e.source));
30924
+ points = [c, c];
30925
+ } else if (backPoints.has(i)) points = backPoints.get(i);
30926
+ else if (flatPoints.has(i)) points = flatPoints.get(i);
30927
+ else {
30928
+ const chain = chainById.get(i);
30929
+ if (!chain) continue;
30930
+ points = chain.map((id) => coord(node.get(id)));
30931
+ }
30932
+ layoutEdges.push({
30933
+ source: e.source,
30934
+ target: e.target,
30935
+ ...e.label !== void 0 && { label: e.label },
30936
+ bidirectional: e.bidirectional,
30937
+ lineNumber: e.lineNumber,
30938
+ points,
30939
+ yOffset: 0,
30940
+ parallelCount: 1,
30941
+ metadata: e.metadata
30942
+ });
30943
+ }
30944
+ const nodeBox = new Map(
30945
+ layoutNodes.map((n) => [
30946
+ n.label,
30947
+ {
30948
+ l: n.x - n.width / 2,
30949
+ r: n.x + n.width / 2,
30950
+ t: n.y - n.height / 2,
30951
+ b: n.y + n.height / 2
30952
+ }
30953
+ ])
30954
+ );
30955
+ const depthOf = (label) => {
30956
+ let d = 0;
30957
+ let cur = label;
30958
+ const guard = /* @__PURE__ */ new Set();
30959
+ while (cur && !guard.has(cur)) {
30960
+ guard.add(cur);
30961
+ cur = groupByLabel.get(cur)?.parentGroup;
30962
+ if (cur) d++;
30963
+ }
30964
+ return d;
30965
+ };
30966
+ const childGroups = /* @__PURE__ */ new Map();
30967
+ for (const g of parsed.groups)
30968
+ if (g.parentGroup)
30969
+ (childGroups.get(g.parentGroup) ?? childGroups.set(g.parentGroup, []).get(g.parentGroup)).push(g.label);
30970
+ const rectByGroup = /* @__PURE__ */ new Map();
30971
+ const ordered = [...parsed.groups].sort(
30972
+ (a, b) => depthOf(b.label) - depthOf(a.label)
30973
+ );
30974
+ const layoutGroups = [];
30975
+ for (const grp of ordered) {
30976
+ let l = Infinity, r = -Infinity, t = Infinity, bm = -Infinity;
30977
+ const acc2 = (box) => {
30978
+ l = Math.min(l, box.l);
30979
+ r = Math.max(r, box.r);
30980
+ t = Math.min(t, box.t);
30981
+ bm = Math.max(bm, box.b);
30982
+ };
30983
+ for (const c of grp.children) {
30984
+ const nb = nodeBox.get(c);
30985
+ if (nb) acc2(nb);
30986
+ }
30987
+ for (const sub of childGroups.get(grp.label) ?? []) {
30988
+ const sr = rectByGroup.get(sub);
30989
+ if (sr) acc2(sr);
30990
+ }
30991
+ if (!Number.isFinite(l)) continue;
30992
+ const x0 = l - GROUP_PAD;
30993
+ const x1 = r + GROUP_PAD;
30994
+ const y0 = t - GROUP_LABEL_ZONE2;
30995
+ const y1 = bm + GROUP_PAD;
30996
+ rectByGroup.set(grp.label, { l: x0, r: x1, t: y0, b: y1 });
30997
+ layoutGroups.push({
30998
+ label: grp.label,
30999
+ lineNumber: grp.lineNumber,
31000
+ x: (x0 + x1) / 2,
31001
+ y: (y0 + y1) / 2,
31002
+ width: x1 - x0,
31003
+ height: y1 - y0,
31004
+ collapsed: false,
31005
+ childCount: grp.children.length
31006
+ });
31007
+ }
31008
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
31009
+ const ext = (x0, y0, x1, y1) => {
31010
+ minX = Math.min(minX, x0);
31011
+ minY = Math.min(minY, y0);
31012
+ maxX = Math.max(maxX, x1);
31013
+ maxY = Math.max(maxY, y1);
31014
+ };
31015
+ for (const n of layoutNodes)
31016
+ ext(
31017
+ n.x - n.width / 2,
31018
+ n.y - n.height / 2,
31019
+ n.x + n.width / 2,
31020
+ n.y + n.height / 2
31021
+ );
31022
+ for (const e of layoutEdges)
31023
+ for (const p of e.points) ext(p.x, p.y, p.x, p.y);
31024
+ for (const g of layoutGroups)
31025
+ ext(
31026
+ g.x - g.width / 2,
31027
+ g.y - g.height / 2,
31028
+ g.x + g.width / 2,
31029
+ g.y + g.height / 2
31030
+ );
31031
+ const sx = MARGIN4 - minX;
31032
+ const sy = MARGIN4 - minY;
31033
+ const shift = sx !== 0 || sy !== 0;
31034
+ return {
31035
+ nodes: shift ? layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : layoutNodes,
31036
+ edges: shift ? layoutEdges.map((e) => ({
31037
+ ...e,
31038
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
31039
+ })) : layoutEdges,
31040
+ groups: shift ? layoutGroups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : layoutGroups,
31041
+ width: maxX + sx + MARGIN4,
31042
+ height: maxY + sy + MARGIN4
31043
+ };
31044
+ }
31045
+ }
31046
+ var NODESEP2, RANKSEP2, DUMMY_THICK2, MARGIN4, GROUP_PAD, GROUP_LABEL_ZONE2, GROUP_GAP, TIER_GAP, W_REAL_REAL2, W_REAL_DUMMY2, W_DUMMY_DUMMY2;
31047
+ var init_layout_grouped = __esm({
31048
+ "src/boxes-and-lines/layout-grouped.ts"() {
31049
+ "use strict";
31050
+ init_layout5();
31051
+ NODESEP2 = 50;
31052
+ RANKSEP2 = 60;
31053
+ DUMMY_THICK2 = 10;
31054
+ MARGIN4 = 40;
31055
+ GROUP_PAD = 16;
31056
+ GROUP_LABEL_ZONE2 = 32;
31057
+ GROUP_GAP = 24;
31058
+ TIER_GAP = GROUP_LABEL_ZONE2 + 2 * GROUP_PAD;
31059
+ W_REAL_REAL2 = 1;
31060
+ W_REAL_DUMMY2 = 2;
31061
+ W_DUMMY_DUMMY2 = 8;
31062
+ }
31063
+ });
31064
+
30179
31065
  // src/boxes-and-lines/layout-search.ts
30180
31066
  var layout_search_exports = {};
30181
31067
  __export(layout_search_exports, {
@@ -30191,7 +31077,7 @@ __export(layout_search_exports, {
30191
31077
  layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
30192
31078
  separateGroupBands: () => separateGroupBands
30193
31079
  });
30194
- function rng2(s) {
31080
+ function rng3(s) {
30195
31081
  return () => {
30196
31082
  s |= 0;
30197
31083
  s = s + 1831565813 | 0;
@@ -30546,7 +31432,7 @@ function detectGroupOverlaps(layout, opts) {
30546
31432
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30547
31433
  const rend = raw.map((a, i) => {
30548
31434
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30549
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31435
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30550
31436
  });
30551
31437
  const hit = /* @__PURE__ */ new Set();
30552
31438
  for (let i = 0; i < raw.length; i++)
@@ -30573,7 +31459,7 @@ function countGroupOverlaps(layout, opts) {
30573
31459
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30574
31460
  const rend = raw.map((a, i) => {
30575
31461
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30576
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31462
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30577
31463
  });
30578
31464
  let count = 0;
30579
31465
  for (let i = 0; i < raw.length; i++)
@@ -30616,7 +31502,7 @@ function separateGroupBands(layout, parsed) {
30616
31502
  let lo = (axis === "y" ? g.y : g.x) - half2;
30617
31503
  const hi = (axis === "y" ? g.y : g.x) + half2;
30618
31504
  const isParent = parsed.groups.some((c) => c.parentGroup === label);
30619
- if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
31505
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE3;
30620
31506
  return { label, lo, hi, c: (lo + hi) / 2 };
30621
31507
  });
30622
31508
  bands.sort((a, b) => a.c - b.c);
@@ -30687,7 +31573,7 @@ function separateGroupBands(layout, parsed) {
30687
31573
  acc(n.x + n.width / 2, n.y + n.height / 2);
30688
31574
  }
30689
31575
  for (const g of groups) {
30690
- acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
31576
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE3);
30691
31577
  acc(g.x + g.width / 2, g.y + g.height / 2);
30692
31578
  }
30693
31579
  for (const e of edges) for (const p of e.points) acc(p.x, p.y);
@@ -30770,6 +31656,7 @@ function edgeLength(layout) {
30770
31656
  }
30771
31657
  async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30772
31658
  const hideDescriptions = opts?.hideDescriptions ?? false;
31659
+ const reserveEdgeLabels = opts?.reserveEdgeLabels ?? false;
30773
31660
  const onProgress = opts?.onProgress;
30774
31661
  const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30775
31662
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
@@ -30951,7 +31838,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30951
31838
  }
30952
31839
  ];
30953
31840
  });
30954
- const GROUP_PAD = 16;
31841
+ const GROUP_PAD2 = 16;
30955
31842
  const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
30956
31843
  const groups = [];
30957
31844
  for (const grp of parsed.groups) {
@@ -30965,10 +31852,10 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30965
31852
  gy1 = Math.max(gy1, n2.y + n2.height / 2);
30966
31853
  }
30967
31854
  if (!Number.isFinite(gx0)) continue;
30968
- const x0 = gx0 - GROUP_PAD;
30969
- const x1 = gx1 + GROUP_PAD;
30970
- const y0 = gy0 - GROUP_LABEL_ZONE2;
30971
- const y1 = gy1 + GROUP_PAD;
31855
+ const x0 = gx0 - GROUP_PAD2;
31856
+ const x1 = gx1 + GROUP_PAD2;
31857
+ const y0 = gy0 - GROUP_LABEL_ZONE3;
31858
+ const y1 = gy1 + GROUP_PAD2;
30972
31859
  groups.push({
30973
31860
  label: grp.label,
30974
31861
  lineNumber: grp.lineNumber,
@@ -31025,7 +31912,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31025
31912
  };
31026
31913
  }
31027
31914
  function place(cfg) {
31028
- const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
31915
+ const r = cfg.seed === void 0 ? null : rng3(cfg.seed + 1);
31029
31916
  const ord = (a) => r ? shuffle(a, r) : a.slice();
31030
31917
  const g = new import_dagre4.default.graphlib.Graph({ compound: true, multigraph: true });
31031
31918
  g.setGraph({
@@ -31062,8 +31949,14 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31062
31949
  for (const e of ord(parsed.edges)) {
31063
31950
  const s = srcRep(e.source);
31064
31951
  const t = tgtRep(e.target);
31065
- if (s !== t && g.hasNode(s) && g.hasNode(t))
31066
- g.setEdge(s, t, {}, edgeKey(e));
31952
+ if (s !== t && g.hasNode(s) && g.hasNode(t)) {
31953
+ let edgeLabel = {};
31954
+ if (reserveEdgeLabels && e.label) {
31955
+ const m = measureEdgeLabel(e.label, EDGE_LABEL_FONT_SIZE6);
31956
+ edgeLabel = { width: m.width, height: m.height, labelpos: "c" };
31957
+ }
31958
+ g.setEdge(s, t, edgeLabel, edgeKey(e));
31959
+ }
31067
31960
  }
31068
31961
  import_dagre4.default.layout(g);
31069
31962
  const nodes = parsed.nodes.map((n2) => {
@@ -31212,8 +32105,15 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31212
32105
  layered = layeredCandidates(parsed, sizes);
31213
32106
  } catch {
31214
32107
  }
32108
+ if (parsed.groups.length > 0 && collapsedGroupLabels.size === 0) {
32109
+ try {
32110
+ layered = layered.concat(groupedTierCandidates(parsed, sizes));
32111
+ } catch {
32112
+ }
32113
+ }
31215
32114
  const fastKey = /* @__PURE__ */ new Map();
31216
- for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
32115
+ for (const lay of pool)
32116
+ fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31217
32117
  pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
31218
32118
  const refineK = Math.min(REFINE_K, pool.length);
31219
32119
  let best = pool[0];
@@ -31278,7 +32178,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31278
32178
  }
31279
32179
  return best;
31280
32180
  }
31281
- var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
32181
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE3;
31282
32182
  var init_layout_search = __esm({
31283
32183
  "src/boxes-and-lines/layout-search.ts"() {
31284
32184
  "use strict";
@@ -31286,6 +32186,8 @@ var init_layout_search = __esm({
31286
32186
  import_d3_shape = require("d3-shape");
31287
32187
  init_layout5();
31288
32188
  init_layout_layered();
32189
+ init_layout_grouped();
32190
+ init_label_placement();
31289
32191
  DEFAULT_LAMBDA = 4;
31290
32192
  ESCALATE_THRESHOLD = 4;
31291
32193
  ESCALATE_MAX_N = 45;
@@ -31294,7 +32196,7 @@ var init_layout_search = __esm({
31294
32196
  splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
31295
32197
  PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31296
32198
  FLAT_CACHE = /* @__PURE__ */ new WeakMap();
31297
- GROUP_LABEL_ZONE2 = 32;
32199
+ GROUP_LABEL_ZONE3 = 32;
31298
32200
  }
31299
32201
  });
31300
32202
 
@@ -31344,9 +32246,9 @@ function estimateLabelLines(label, nodeWidth = NODE_WIDTH) {
31344
32246
  current = word;
31345
32247
  }
31346
32248
  }
31347
- if (lines <= MAX_LABEL_LINES) return Math.min(lines, MAX_LABEL_LINES);
32249
+ if (lines <= MAX_LABEL_LINES2) return Math.min(lines, MAX_LABEL_LINES2);
31348
32250
  }
31349
- return MAX_LABEL_LINES;
32251
+ return MAX_LABEL_LINES2;
31350
32252
  }
31351
32253
  function computeNodeSize(node, reserveValueRow) {
31352
32254
  if (!node.description || node.description.length === 0) {
@@ -31354,7 +32256,7 @@ function computeNodeSize(node, reserveValueRow) {
31354
32256
  }
31355
32257
  const w = DESC_NODE_WIDTH;
31356
32258
  const labelLines = estimateLabelLines(node.label, w);
31357
- const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT + LABEL_PAD;
32259
+ const labelHeight = labelLines * 13 * LABEL_LINE_HEIGHT2 + LABEL_PAD;
31358
32260
  const maxTextWidth = w - 24;
31359
32261
  let totalRenderedLines = 0;
31360
32262
  for (const line11 of node.description) {
@@ -31378,7 +32280,7 @@ function computeNodeSize(node, reserveValueRow) {
31378
32280
  }
31379
32281
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
31380
32282
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
31381
- const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
32283
+ const searchOpts = {
31382
32284
  ...layoutOptions?.hideDescriptions !== void 0 && {
31383
32285
  hideDescriptions: layoutOptions.hideDescriptions
31384
32286
  },
@@ -31388,12 +32290,23 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
31388
32290
  ...layoutOptions?.onProgress !== void 0 && {
31389
32291
  onProgress: layoutOptions.onProgress
31390
32292
  }
31391
- });
31392
- return attachNotes(
31393
- applyParallelEdgeOffsets(searched),
32293
+ };
32294
+ const searched = await layoutBoxesAndLinesSearch2(
31394
32295
  parsed,
31395
- layoutOptions?.collapsedNotes
31396
- );
32296
+ collapseInfo,
32297
+ searchOpts
32298
+ );
32299
+ let placed = placeEdgeLabels(applyParallelEdgeOffsets(searched));
32300
+ if (placed.unresolved.length > 0) {
32301
+ const relaid = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
32302
+ ...searchOpts,
32303
+ reserveEdgeLabels: true
32304
+ });
32305
+ const relaidPlaced = placeEdgeLabels(applyParallelEdgeOffsets(relaid));
32306
+ if (relaidPlaced.unresolved.length < placed.unresolved.length)
32307
+ placed = relaidPlaced;
32308
+ }
32309
+ return attachNotes(placed.layout, parsed, layoutOptions?.collapsedNotes);
31397
32310
  }
31398
32311
  function attachNotes(layout, parsed, collapsedNotes) {
31399
32312
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -31461,6 +32374,7 @@ function attachNotes(layout, parsed, collapsedNotes) {
31461
32374
  points: e.points.map((pt) => ({ x: pt.x + shiftX, y: pt.y + shiftY })),
31462
32375
  ...e.labelX !== void 0 && { labelX: e.labelX + shiftX },
31463
32376
  ...e.labelY !== void 0 && { labelY: e.labelY + shiftY }
32377
+ // labelWidth/labelHeight/labelLines are shift-invariant — carried via spread.
31464
32378
  })) : layout.edges;
31465
32379
  const finalGroups = shifted ? layout.groups.map((grp) => ({
31466
32380
  ...grp,
@@ -31471,8 +32385,8 @@ function attachNotes(layout, parsed, collapsedNotes) {
31471
32385
  nodes: finalNodes,
31472
32386
  edges: finalEdges,
31473
32387
  groups: finalGroups,
31474
- width: bbMaxX + shiftX + MARGIN4,
31475
- height: bbMaxY + shiftY + MARGIN4
32388
+ width: bbMaxX + shiftX + MARGIN5,
32389
+ height: bbMaxY + shiftY + MARGIN5
31476
32390
  };
31477
32391
  }
31478
32392
  function applyParallelEdgeOffsets(layout) {
@@ -31505,13 +32419,14 @@ function applyParallelEdgeOffsets(layout) {
31505
32419
  }))
31506
32420
  };
31507
32421
  }
31508
- 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;
32422
+ 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;
31509
32423
  var init_layout5 = __esm({
31510
32424
  "src/boxes-and-lines/layout.ts"() {
31511
32425
  "use strict";
31512
32426
  init_text_measure();
32427
+ init_label_placement();
31513
32428
  init_notes();
31514
- MARGIN4 = 40;
32429
+ MARGIN5 = 40;
31515
32430
  MAX_PARALLEL_EDGES = 5;
31516
32431
  PARALLEL_SPACING = 22;
31517
32432
  PHI = 1.618;
@@ -31523,8 +32438,8 @@ var init_layout5 = __esm({
31523
32438
  DESC_PADDING = 8;
31524
32439
  SEPARATOR_GAP4 = 4;
31525
32440
  MAX_DESC_LINES2 = 6;
31526
- MAX_LABEL_LINES = 3;
31527
- LABEL_LINE_HEIGHT = 1.3;
32441
+ MAX_LABEL_LINES2 = 3;
32442
+ LABEL_LINE_HEIGHT2 = 1.3;
31528
32443
  LABEL_PAD = 12;
31529
32444
  VALUE_ROW_FONT = 11;
31530
32445
  VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
@@ -31543,7 +32458,7 @@ function tokenize(text) {
31543
32458
  return tokens;
31544
32459
  }
31545
32460
  function tryWrap(tokens, maxWidth, fontSize, maxLines) {
31546
- const availWidth = maxWidth - H_PAD;
32461
+ const availWidth = maxWidth - H_PAD2;
31547
32462
  const lines = [];
31548
32463
  let currentLine = "";
31549
32464
  for (const token of tokens) {
@@ -31568,7 +32483,7 @@ function tryWrap(tokens, maxWidth, fontSize, maxLines) {
31568
32483
  return lines;
31569
32484
  }
31570
32485
  function truncateLastLine(lines, maxWidth, fontSize) {
31571
- const availWidth = maxWidth - H_PAD;
32486
+ const availWidth = maxWidth - H_PAD2;
31572
32487
  const result = [...lines];
31573
32488
  const last = result[result.length - 1];
31574
32489
  if (measureText(last, fontSize) > availWidth) {
@@ -31576,7 +32491,7 @@ function truncateLastLine(lines, maxWidth, fontSize) {
31576
32491
  }
31577
32492
  return result;
31578
32493
  }
31579
- function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES2) {
32494
+ function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABEL_LINES3) {
31580
32495
  if (!text) return { lines: [""], fontSize: baseFontSize };
31581
32496
  const tokens = tokenize(text);
31582
32497
  for (let fs = baseFontSize; fs >= minFontSize; fs--) {
@@ -31591,7 +32506,7 @@ function wrapText(text, maxWidth, baseFontSize, minFontSize, maxLines = MAX_LABE
31591
32506
  if (allLines.length > maxLines) {
31592
32507
  const last = truncated[truncated.length - 1];
31593
32508
  if (!last.endsWith("\u2026")) {
31594
- const availWidth = maxWidth - H_PAD;
32509
+ const availWidth = maxWidth - H_PAD2;
31595
32510
  if (measureText(last + "\u2026", minFontSize) > availWidth) {
31596
32511
  truncated[truncated.length - 1] = truncateText(
31597
32512
  last,
@@ -31618,7 +32533,7 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
31618
32533
  nodeWidth,
31619
32534
  baseFontSize,
31620
32535
  MIN_FONT_SIZE,
31621
- MAX_LABEL_LINES2
32536
+ MAX_LABEL_LINES3
31622
32537
  );
31623
32538
  const descLines = [];
31624
32539
  let descFontSize = DESC_FONT_SIZE3;
@@ -31646,14 +32561,14 @@ function computeNodeText(label, description, depth, nodeWidth, hideDescriptions)
31646
32561
  descFontSize
31647
32562
  };
31648
32563
  }
31649
- var H_PAD, MAX_LABEL_LINES2, MAX_DESC_LINES3, ROOT_FONT_SIZE, MIN_FONT_SIZE, FONT_STEP, DESC_FONT_SIZE3;
32564
+ var H_PAD2, MAX_LABEL_LINES3, MAX_DESC_LINES3, ROOT_FONT_SIZE, MIN_FONT_SIZE, FONT_STEP, DESC_FONT_SIZE3;
31650
32565
  var init_text_wrap = __esm({
31651
32566
  "src/mindmap/text-wrap.ts"() {
31652
32567
  "use strict";
31653
32568
  init_description_helpers();
31654
32569
  init_text_measure();
31655
- H_PAD = 16;
31656
- MAX_LABEL_LINES2 = 3;
32570
+ H_PAD2 = 16;
32571
+ MAX_LABEL_LINES3 = 3;
31657
32572
  MAX_DESC_LINES3 = 2;
31658
32573
  ROOT_FONT_SIZE = 17;
31659
32574
  MIN_FONT_SIZE = 9;
@@ -31682,7 +32597,7 @@ function layoutMindmap(parsed, _palette, options) {
31682
32597
  leafWidth: ctx.structural(LEAF_WIDTH),
31683
32598
  hGap: ctx.aesthetic(H_GAP2),
31684
32599
  vGap: ctx.aesthetic(V_GAP2),
31685
- margin: ctx.aesthetic(MARGIN5),
32600
+ margin: ctx.aesthetic(MARGIN6),
31686
32601
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
31687
32602
  };
31688
32603
  populateDepthCache(roots);
@@ -32040,7 +32955,7 @@ function nodeHeightS(node, hideDescriptions, sc) {
32040
32955
  hideDescriptions
32041
32956
  );
32042
32957
  const labelLineCount = text.labelLines.length;
32043
- const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT : LABEL_LINE_HEIGHT2 * labelLineCount;
32958
+ const labelH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT : LABEL_LINE_HEIGHT3 * labelLineCount;
32044
32959
  let h = labelH + NODE_V_PAD;
32045
32960
  if (text.descLines.length > 0) {
32046
32961
  h += DESC_LINE_HEIGHT3 * text.descLines.length + 4;
@@ -32060,7 +32975,7 @@ function populateDepthCache(roots) {
32060
32975
  };
32061
32976
  walk(roots, 0);
32062
32977
  }
32063
- 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;
32978
+ 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;
32064
32979
  var init_layout6 = __esm({
32065
32980
  "src/mindmap/layout.ts"() {
32066
32981
  "use strict";
@@ -32071,12 +32986,12 @@ var init_layout6 = __esm({
32071
32986
  DEPTH1_WIDTH = 150;
32072
32987
  LEAF_WIDTH = 120;
32073
32988
  SINGLE_LABEL_HEIGHT = 28;
32074
- LABEL_LINE_HEIGHT2 = 18;
32989
+ LABEL_LINE_HEIGHT3 = 18;
32075
32990
  DESC_LINE_HEIGHT3 = 14;
32076
32991
  NODE_V_PAD = 10;
32077
32992
  H_GAP2 = 40;
32078
32993
  V_GAP2 = 12;
32079
- MARGIN5 = 40;
32994
+ MARGIN6 = 40;
32080
32995
  MULTI_ROOT_GAP = 60;
32081
32996
  nodeDepthCache = /* @__PURE__ */ new Map();
32082
32997
  }
@@ -32333,7 +33248,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
32333
33248
  );
32334
33249
  const nodeG = mainG.append("g").attr("class", "mindmap-node").attr("data-line-number", node.lineNumber);
32335
33250
  if (activeTagGroup) {
32336
- const tagKey = activeTagGroup.toLowerCase();
33251
+ const tagKey = tagAttrKey(activeTagGroup);
32337
33252
  const metaValue = node.metadata[tagKey];
32338
33253
  if (metaValue) {
32339
33254
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -32362,18 +33277,18 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
32362
33277
  descFontSize
32363
33278
  } = textLayout;
32364
33279
  const labelLineCount = labelLines.length;
32365
- const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 : LABEL_LINE_HEIGHT3 * labelLineCount;
33280
+ const labelZoneH = labelLineCount <= 1 ? SINGLE_LABEL_HEIGHT2 : LABEL_LINE_HEIGHT4 * labelLineCount;
32366
33281
  const labelZoneHeight = showDesc ? labelZoneH : node.height;
32367
33282
  const centerX = node.x + node.width / 2;
32368
33283
  if (labelLineCount <= 1) {
32369
33284
  const labelY = node.y + labelZoneHeight / 2 + fontSize * 0.35;
32370
33285
  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]);
32371
33286
  } else {
32372
- const blockH = LABEL_LINE_HEIGHT3 * (labelLineCount - 1);
33287
+ const blockH = LABEL_LINE_HEIGHT4 * (labelLineCount - 1);
32373
33288
  const firstBaselineY = node.y + labelZoneHeight / 2 - blockH / 2 + fontSize * 0.35;
32374
33289
  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);
32375
33290
  for (let i = 0; i < labelLines.length; i++) {
32376
- textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i * LABEL_LINE_HEIGHT3).text(labelLines[i]);
33291
+ textEl.append("tspan").attr("x", centerX).attr("y", firstBaselineY + i * LABEL_LINE_HEIGHT4).text(labelLines[i]);
32377
33292
  }
32378
33293
  }
32379
33294
  if (labelLines.length > 1 || labelLines[0] !== node.label) {
@@ -32448,10 +33363,11 @@ function renderMindmapForExport(content, theme, palette) {
32448
33363
  return extractExportSvg(container, theme);
32449
33364
  });
32450
33365
  }
32451
- var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
33366
+ var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT4, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
32452
33367
  var init_renderer7 = __esm({
32453
33368
  "src/mindmap/renderer.ts"() {
32454
33369
  "use strict";
33370
+ init_tag_groups();
32455
33371
  d3Selection12 = __toESM(require("d3-selection"), 1);
32456
33372
  init_fonts();
32457
33373
  init_export_container();
@@ -32470,7 +33386,7 @@ var init_renderer7 = __esm({
32470
33386
  DIAGRAM_PADDING7 = 20;
32471
33387
  TITLE_HEIGHT4 = 30;
32472
33388
  SINGLE_LABEL_HEIGHT2 = 28;
32473
- LABEL_LINE_HEIGHT3 = 18;
33389
+ LABEL_LINE_HEIGHT4 = 18;
32474
33390
  DESC_LINE_HEIGHT4 = 14;
32475
33391
  NODE_RADIUS = 6;
32476
33392
  ROOT_STROKE_WIDTH = 2.5;
@@ -33662,7 +34578,7 @@ function resolveNodeColor4(el, tagGroups, activeGroupName, ancestors) {
33662
34578
  (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
33663
34579
  );
33664
34580
  if (!group) return void 0;
33665
- const key = group.name.toLowerCase();
34581
+ const key = tagAttrKey(group.name);
33666
34582
  let metaValue = el.metadata[key];
33667
34583
  if (!metaValue && ancestors) {
33668
34584
  for (const ancestor of ancestors) {
@@ -33864,8 +34780,8 @@ function layoutC4Context(parsed, activeTagGroup) {
33864
34780
  }
33865
34781
  }
33866
34782
  if (nodes.length > 0) {
33867
- const shiftX = MARGIN6 - minX;
33868
- const shiftY = MARGIN6 - minY;
34783
+ const shiftX = MARGIN7 - minX;
34784
+ const shiftY = MARGIN7 - minY;
33869
34785
  for (const node of nodes) {
33870
34786
  node.x += shiftX;
33871
34787
  node.y += shiftY;
@@ -33877,12 +34793,12 @@ function layoutC4Context(parsed, activeTagGroup) {
33877
34793
  }
33878
34794
  }
33879
34795
  }
33880
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
33881
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
34796
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN7 * 2 : 0;
34797
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN7 * 2 : 0;
33882
34798
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
33883
34799
  if (legendGroups.length > 0) {
33884
- const legendY = totalHeight + MARGIN6;
33885
- let legendX = MARGIN6;
34800
+ const legendY = totalHeight + MARGIN7;
34801
+ let legendX = MARGIN7;
33886
34802
  for (const lg of legendGroups) {
33887
34803
  lg.x = legendX;
33888
34804
  lg.y = legendY;
@@ -34214,8 +35130,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34214
35130
  if (pt.y > maxY) maxY = pt.y;
34215
35131
  }
34216
35132
  }
34217
- const shiftX = MARGIN6 - minX;
34218
- const shiftY = MARGIN6 - minY;
35133
+ const shiftX = MARGIN7 - minX;
35134
+ const shiftY = MARGIN7 - minY;
34219
35135
  for (const node of nodes) {
34220
35136
  node.x += shiftX;
34221
35137
  node.y += shiftY;
@@ -34232,12 +35148,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34232
35148
  pt.y += shiftY;
34233
35149
  }
34234
35150
  }
34235
- let totalWidth = maxX - minX + MARGIN6 * 2;
34236
- let totalHeight = maxY - minY + MARGIN6 * 2;
35151
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35152
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34237
35153
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34238
35154
  if (legendGroups.length > 0) {
34239
- const legendY = totalHeight + MARGIN6;
34240
- let legendX = MARGIN6;
35155
+ const legendY = totalHeight + MARGIN7;
35156
+ let legendX = MARGIN7;
34241
35157
  for (const lg of legendGroups) {
34242
35158
  lg.x = legendX;
34243
35159
  lg.y = legendY;
@@ -34629,8 +35545,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34629
35545
  if (pt.y > maxY) maxY = pt.y;
34630
35546
  }
34631
35547
  }
34632
- const shiftX = MARGIN6 - minX;
34633
- const shiftY = MARGIN6 - minY;
35548
+ const shiftX = MARGIN7 - minX;
35549
+ const shiftY = MARGIN7 - minY;
34634
35550
  for (const node of nodes) {
34635
35551
  node.x += shiftX;
34636
35552
  node.y += shiftY;
@@ -34647,12 +35563,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34647
35563
  pt.y += shiftY;
34648
35564
  }
34649
35565
  }
34650
- let totalWidth = maxX - minX + MARGIN6 * 2;
34651
- let totalHeight = maxY - minY + MARGIN6 * 2;
35566
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35567
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34652
35568
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34653
35569
  if (legendGroups.length > 0) {
34654
- const legendY = totalHeight + MARGIN6;
34655
- let legendX = MARGIN6;
35570
+ const legendY = totalHeight + MARGIN7;
35571
+ let legendX = MARGIN7;
34656
35572
  for (const lg of legendGroups) {
34657
35573
  lg.x = legendX;
34658
35574
  lg.y = legendY;
@@ -34940,8 +35856,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34940
35856
  if (pt.y > maxY) maxY = pt.y;
34941
35857
  }
34942
35858
  }
34943
- const shiftX = MARGIN6 - minX;
34944
- const shiftY = MARGIN6 - minY;
35859
+ const shiftX = MARGIN7 - minX;
35860
+ const shiftY = MARGIN7 - minY;
34945
35861
  for (const node of nodes) {
34946
35862
  node.x += shiftX;
34947
35863
  node.y += shiftY;
@@ -34956,12 +35872,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34956
35872
  pt.y += shiftY;
34957
35873
  }
34958
35874
  }
34959
- let totalWidth = maxX - minX + MARGIN6 * 2;
34960
- let totalHeight = maxY - minY + MARGIN6 * 2;
35875
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35876
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34961
35877
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34962
35878
  if (legendGroups.length > 0) {
34963
- const legendY = totalHeight + MARGIN6;
34964
- let legendX = MARGIN6;
35879
+ const legendY = totalHeight + MARGIN7;
35880
+ let legendX = MARGIN7;
34965
35881
  for (const lg of legendGroups) {
34966
35882
  lg.x = legendX;
34967
35883
  lg.y = legendY;
@@ -34981,10 +35897,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34981
35897
  height: totalHeight
34982
35898
  };
34983
35899
  }
34984
- var import_dagre5, 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;
35900
+ var import_dagre5, 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;
34985
35901
  var init_layout8 = __esm({
34986
35902
  "src/c4/layout.ts"() {
34987
35903
  "use strict";
35904
+ init_tag_groups();
34988
35905
  import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
34989
35906
  init_legend_constants();
34990
35907
  init_text_measure();
@@ -35002,7 +35919,7 @@ var init_layout8 = __esm({
35002
35919
  CARD_H_PAD3 = 20;
35003
35920
  META_LINE_HEIGHT4 = 16;
35004
35921
  META_FONT_SIZE4 = 11;
35005
- MARGIN6 = 40;
35922
+ MARGIN7 = 40;
35006
35923
  BOUNDARY_PAD = 40;
35007
35924
  GROUP_BOUNDARY_PAD = 24;
35008
35925
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -35136,7 +36053,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35136
36053
  const labelText = edge.label ?? "";
35137
36054
  const techText = edge.technology ? `[${edge.technology}]` : "";
35138
36055
  const textW = Math.max(
35139
- measureText(labelText, EDGE_LABEL_FONT_SIZE6),
36056
+ measureText(labelText, EDGE_LABEL_FONT_SIZE7),
35140
36057
  measureText(techText, TECH_FONT_SIZE)
35141
36058
  );
35142
36059
  const bgW = textW + 12;
@@ -35147,7 +36064,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35147
36064
  textY = midPt.y - 4;
35148
36065
  }
35149
36066
  if (labelText) {
35150
- 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);
36067
+ 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);
35151
36068
  }
35152
36069
  if (techText) {
35153
36070
  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);
@@ -35157,7 +36074,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35157
36074
  for (const node of layout.nodes) {
35158
36075
  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);
35159
36076
  if (activeTagGroup) {
35160
- const tagKey = activeTagGroup.toLowerCase();
36077
+ const tagKey = tagAttrKey(activeTagGroup);
35161
36078
  const tagValue = node.metadata[tagKey];
35162
36079
  if (tagValue) {
35163
36080
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35331,7 +36248,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
35331
36248
  const labelText = edge.label ?? "";
35332
36249
  const techText = edge.technology ? `[${edge.technology}]` : "";
35333
36250
  const textW = Math.max(
35334
- measureText(labelText, EDGE_LABEL_FONT_SIZE6),
36251
+ measureText(labelText, EDGE_LABEL_FONT_SIZE7),
35335
36252
  measureText(techText, TECH_FONT_SIZE)
35336
36253
  );
35337
36254
  const bgW = textW + 12;
@@ -35349,7 +36266,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
35349
36266
  });
35350
36267
  }
35351
36268
  }
35352
- placeEdgeLabels(pendingLabels, edges, obstacleRects);
36269
+ placeEdgeLabels2(pendingLabels, edges, obstacleRects);
35353
36270
  for (const lbl of pendingLabels) {
35354
36271
  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");
35355
36272
  let textY = lbl.y;
@@ -35357,7 +36274,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
35357
36274
  textY = lbl.y - 4;
35358
36275
  }
35359
36276
  if (lbl.labelText) {
35360
- 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);
36277
+ 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);
35361
36278
  }
35362
36279
  if (lbl.techText) {
35363
36280
  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);
@@ -35438,7 +36355,7 @@ function tangentAt(points, t) {
35438
36355
  }
35439
36356
  return { x: 0, y: 1 };
35440
36357
  }
35441
- function placeEdgeLabels(labels, edges, obstacleRects) {
36358
+ function placeEdgeLabels2(labels, edges, obstacleRects) {
35442
36359
  if (labels.length === 0) return;
35443
36360
  const allPaths = edges.map((e) => e.points);
35444
36361
  const placedRects = [];
@@ -35716,7 +36633,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35716
36633
  for (const node of layout.nodes) {
35717
36634
  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);
35718
36635
  if (activeTagGroup) {
35719
- const tagKey = activeTagGroup.toLowerCase();
36636
+ const tagKey = tagAttrKey(activeTagGroup);
35720
36637
  const tagValue = node.metadata[tagKey];
35721
36638
  if (tagValue) {
35722
36639
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35976,10 +36893,11 @@ function renderC4DeploymentForExport(content, theme, palette) {
35976
36893
  document.body.removeChild(el);
35977
36894
  }
35978
36895
  }
35979
- var d3Selection14, d3Shape6, 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;
36896
+ var d3Selection14, d3Shape6, 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;
35980
36897
  var init_renderer9 = __esm({
35981
36898
  "src/c4/renderer.ts"() {
35982
36899
  "use strict";
36900
+ init_tag_groups();
35983
36901
  d3Selection14 = __toESM(require("d3-selection"), 1);
35984
36902
  d3Shape6 = __toESM(require("d3-shape"), 1);
35985
36903
  init_fonts();
@@ -36000,7 +36918,7 @@ var init_renderer9 = __esm({
36000
36918
  NAME_FONT_SIZE2 = 14;
36001
36919
  DESC_FONT_SIZE5 = 11;
36002
36920
  DESC_LINE_HEIGHT6 = 16;
36003
- EDGE_LABEL_FONT_SIZE6 = 11;
36921
+ EDGE_LABEL_FONT_SIZE7 = 11;
36004
36922
  TECH_FONT_SIZE = 10;
36005
36923
  CARD_H_PAD4 = 20;
36006
36924
  CARD_V_PAD4 = 14;
@@ -36624,7 +37542,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
36624
37542
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
36625
37543
  const sTitleY = ctx.structural(TITLE_Y);
36626
37544
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
36627
- const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE7);
37545
+ const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE8);
36628
37546
  const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
36629
37547
  const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
36630
37548
  const sArrowheadW = ctx.structural(ARROWHEAD_W3);
@@ -36850,7 +37768,7 @@ function renderFlowchartForExport(content, theme, palette) {
36850
37768
  document.body.removeChild(container);
36851
37769
  }
36852
37770
  }
36853
- var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
37771
+ var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE8, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
36854
37772
  var init_flowchart_renderer = __esm({
36855
37773
  "src/graph/flowchart-renderer.ts"() {
36856
37774
  "use strict";
@@ -36870,7 +37788,7 @@ var init_flowchart_renderer = __esm({
36870
37788
  DIAGRAM_PADDING9 = 20;
36871
37789
  MAX_SCALE6 = 3;
36872
37790
  NODE_FONT_SIZE2 = 13;
36873
- EDGE_LABEL_FONT_SIZE7 = 11;
37791
+ EDGE_LABEL_FONT_SIZE8 = 11;
36874
37792
  ARROWHEAD_W3 = 10;
36875
37793
  ARROWHEAD_H3 = 7;
36876
37794
  IO_SKEW = 15;
@@ -38044,7 +38962,7 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
38044
38962
  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);
38045
38963
  if (crossOverlap <= 0) continue;
38046
38964
  anyOverlap = true;
38047
- const shift = primaryOverlap + GROUP_GAP;
38965
+ const shift = primaryOverlap + GROUP_GAP2;
38048
38966
  const aCenter = isLR ? ga.y + ga.height / 2 : ga.x + ga.width / 2;
38049
38967
  const bCenter = isLR ? gb.y + gb.height / 2 : gb.x + gb.width / 2;
38050
38968
  const groupToShift = aCenter <= bCenter ? gb : ga;
@@ -38304,7 +39222,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
38304
39222
  const midIdx = Math.floor(edge.points.length / 2);
38305
39223
  const midPt = edge.points[midIdx];
38306
39224
  if (midPt) {
38307
- const halfWidth = (measureText(edge.label, EDGE_LABEL_FONT_SIZE8) + 8) / 2;
39225
+ const halfWidth = (measureText(edge.label, EDGE_LABEL_FONT_SIZE9) + 8) / 2;
38308
39226
  if (midPt.x - halfWidth < minX) minX = midPt.x - halfWidth;
38309
39227
  if (midPt.x + halfWidth > maxX) maxX = midPt.x + halfWidth;
38310
39228
  }
@@ -38338,7 +39256,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
38338
39256
  height: totalHeight
38339
39257
  };
38340
39258
  }
38341
- var import_dagre7, 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;
39259
+ var import_dagre7, 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;
38342
39260
  var init_layout10 = __esm({
38343
39261
  "src/infra/layout.ts"() {
38344
39262
  "use strict";
@@ -38353,7 +39271,7 @@ var init_layout10 = __esm({
38353
39271
  COLLAPSE_BAR_HEIGHT3 = 6;
38354
39272
  NODE_FONT_SIZE3 = 13;
38355
39273
  META_FONT_SIZE5 = 10;
38356
- EDGE_LABEL_FONT_SIZE8 = 11;
39274
+ EDGE_LABEL_FONT_SIZE9 = 11;
38357
39275
  PADDING_X3 = 24;
38358
39276
  GROUP_PADDING2 = 20;
38359
39277
  GROUP_HEADER_HEIGHT = 24;
@@ -38394,7 +39312,7 @@ var init_layout10 = __esm({
38394
39312
  "retention-hours": "retention",
38395
39313
  partitions: "partitions"
38396
39314
  };
38397
- GROUP_GAP = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
39315
+ GROUP_GAP2 = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
38398
39316
  }
38399
39317
  });
38400
39318
 
@@ -38458,7 +39376,7 @@ function buildScaledConstants(ctx) {
38458
39376
  sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
38459
39377
  sMetaFontSize: ctx.text(META_FONT_SIZE6),
38460
39378
  sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
38461
- sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE9),
39379
+ sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE10),
38462
39380
  sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
38463
39381
  sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
38464
39382
  sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
@@ -39187,7 +40105,7 @@ function renderEdgeLabels(svg, edges, nodes, groups, palette, _isDark, animate,
39187
40105
  if (!midPt) continue;
39188
40106
  const labelText = edge.label;
39189
40107
  const g = svg.append("g").attr("class", animate ? "infra-edge-label" : "");
39190
- const textWidth = measureText(labelText, EDGE_LABEL_FONT_SIZE9) + 8;
40108
+ const textWidth = measureText(labelText, EDGE_LABEL_FONT_SIZE10) + 8;
39191
40109
  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);
39192
40110
  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);
39193
40111
  if (animate) {
@@ -39201,7 +40119,7 @@ function resolveActiveTagStroke(node, activeGroup, tagGroups, palette) {
39201
40119
  (t) => t.name.toLowerCase() === activeGroup.toLowerCase()
39202
40120
  );
39203
40121
  if (!tg) return null;
39204
- const tagKey = (tg.alias ?? tg.name).toLowerCase();
40122
+ const tagKey = tagAttrKey(tg.alias ?? tg.name);
39205
40123
  const tagVal = node.tags[tagKey];
39206
40124
  if (!tagVal) return null;
39207
40125
  const tv = tg.values.find(
@@ -39530,7 +40448,7 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
39530
40448
  groups.push({
39531
40449
  name: tg.name,
39532
40450
  type: "tag",
39533
- tagKey: (tg.alias ?? tg.name).toLowerCase(),
40451
+ tagKey: tagAttrKey(tg.alias ?? tg.name),
39534
40452
  entries,
39535
40453
  width: LEGEND_CAPSULE_PAD * 2 + pillWidth2 + 4 + entriesWidth2,
39536
40454
  minifiedWidth: pillWidth2
@@ -39578,7 +40496,7 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
39578
40496
  });
39579
40497
  legendG.selectAll("[data-legend-group]").classed("infra-legend-group", true);
39580
40498
  for (const group of legendGroups) {
39581
- const groupKey = group.name.toLowerCase();
40499
+ const groupKey = tagAttrKey(group.name);
39582
40500
  for (const entry of group.entries) {
39583
40501
  const entryEl = legendG.select(
39584
40502
  `[data-legend-group="${groupKey}"] [data-legend-entry="${entry.value.toLowerCase()}"]`
@@ -39797,10 +40715,11 @@ function parseAndLayoutInfra(content) {
39797
40715
  const layout = layoutInfra(computed);
39798
40716
  return { parsed, computed, layout };
39799
40717
  }
39800
- var d3Selection16, d3Shape8, 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;
40718
+ var d3Selection16, d3Shape8, 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;
39801
40719
  var init_renderer10 = __esm({
39802
40720
  "src/infra/renderer.ts"() {
39803
40721
  "use strict";
40722
+ init_tag_groups();
39804
40723
  d3Selection16 = __toESM(require("d3-selection"), 1);
39805
40724
  d3Shape8 = __toESM(require("d3-shape"), 1);
39806
40725
  init_fonts();
@@ -39822,7 +40741,7 @@ var init_renderer10 = __esm({
39822
40741
  NODE_FONT_SIZE4 = 13;
39823
40742
  META_FONT_SIZE6 = 10;
39824
40743
  META_LINE_HEIGHT6 = 14;
39825
- EDGE_LABEL_FONT_SIZE9 = 11;
40744
+ EDGE_LABEL_FONT_SIZE10 = 11;
39826
40745
  GROUP_LABEL_FONT_SIZE2 = 14;
39827
40746
  NODE_BORDER_RADIUS = 8;
39828
40747
  OVERLOAD_STROKE_WIDTH = 3;
@@ -39899,17 +40818,17 @@ function mulberry32(seed) {
39899
40818
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
39900
40819
  };
39901
40820
  }
39902
- function standardNormal(rng3) {
40821
+ function standardNormal(rng4) {
39903
40822
  let u = 0;
39904
40823
  let v = 0;
39905
- while (u === 0) u = rng3();
39906
- while (v === 0) v = rng3();
40824
+ while (u === 0) u = rng4();
40825
+ while (v === 0) v = rng4();
39907
40826
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
39908
40827
  }
39909
- function gamma(shape, rng3) {
40828
+ function gamma(shape, rng4) {
39910
40829
  if (shape < 1) {
39911
- const u = rng3();
39912
- return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
40830
+ const u = rng4();
40831
+ return gamma(shape + 1, rng4) * Math.pow(u, 1 / shape);
39913
40832
  }
39914
40833
  const d = shape - 1 / 3;
39915
40834
  const c = 1 / Math.sqrt(9 * d);
@@ -39917,29 +40836,29 @@ function gamma(shape, rng3) {
39917
40836
  let x;
39918
40837
  let v;
39919
40838
  do {
39920
- x = standardNormal(rng3);
40839
+ x = standardNormal(rng4);
39921
40840
  v = 1 + c * x;
39922
40841
  } while (v <= 0);
39923
40842
  v = v * v * v;
39924
- const u = rng3();
40843
+ const u = rng4();
39925
40844
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
39926
40845
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
39927
40846
  }
39928
40847
  }
39929
- function sampleBeta(alpha, beta, rng3) {
39930
- const x = gamma(alpha, rng3);
39931
- const y = gamma(beta, rng3);
40848
+ function sampleBeta(alpha, beta, rng4) {
40849
+ const x = gamma(alpha, rng4);
40850
+ const y = gamma(beta, rng4);
39932
40851
  return x / (x + y);
39933
40852
  }
39934
- function sampleBetaPert(o, m, p, rng3) {
40853
+ function sampleBetaPert(o, m, p, rng4) {
39935
40854
  if (o === p) return m;
39936
40855
  const range = p - o;
39937
40856
  const alpha = 1 + 4 * (m - o) / range;
39938
40857
  const beta = 1 + 4 * (p - m) / range;
39939
- return o + sampleBeta(alpha, beta, rng3) * range;
40858
+ return o + sampleBeta(alpha, beta, rng4) * range;
39940
40859
  }
39941
40860
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
39942
- const rng3 = mulberry32(opts.seed);
40861
+ const rng4 = mulberry32(opts.seed);
39943
40862
  const expById = /* @__PURE__ */ new Map();
39944
40863
  for (const e of expanded) expById.set(e.id, e);
39945
40864
  const completionTimes = new Array(opts.trials);
@@ -39962,7 +40881,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
39962
40881
  } else if (exp.o === exp.p) {
39963
40882
  duration = exp.m;
39964
40883
  } else {
39965
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
40884
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng4);
39966
40885
  }
39967
40886
  let esLower = 0;
39968
40887
  let efLower = -Infinity;
@@ -44247,7 +45166,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44247
45166
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(containerWidth, idealWidth);
44248
45167
  const sBarH = ctx.structural(BAR_H);
44249
45168
  const sRowGap = ctx.structural(ROW_GAP);
44250
- const sGroupGap = ctx.structural(GROUP_GAP2);
45169
+ const sGroupGap = ctx.structural(GROUP_GAP3);
44251
45170
  const sMilestoneSize = ctx.structural(MILESTONE_SIZE);
44252
45171
  const sBottomMargin = ctx.aesthetic(BOTTOM_MARGIN);
44253
45172
  const sRightMargin = ctx.aesthetic(RIGHT_MARGIN);
@@ -44419,7 +45338,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44419
45338
  (tg) => tg.name.toLowerCase() === currentSwimlaneGroup.toLowerCase()
44420
45339
  );
44421
45340
  if (tagGroup) {
44422
- const tagKey = tagGroup.name.toLowerCase();
45341
+ const tagKey = tagAttrKey(tagGroup.name);
44423
45342
  for (const rt of resolved.tasks) {
44424
45343
  let value = rt.effectiveMetadata[tagKey];
44425
45344
  if (!value && tagGroup.defaultValue) value = tagGroup.defaultValue;
@@ -45160,7 +46079,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45160
46079
  const usedValues = /* @__PURE__ */ new Map();
45161
46080
  if (resolvedTasks) {
45162
46081
  for (const group of visibleGroups) {
45163
- const key = group.name.toLowerCase();
46082
+ const key = tagAttrKey(group.name);
45164
46083
  const used = /* @__PURE__ */ new Set();
45165
46084
  for (const rt of resolvedTasks) {
45166
46085
  const val = rt.effectiveMetadata[key];
@@ -45171,7 +46090,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45171
46090
  }
45172
46091
  const filteredEntries = /* @__PURE__ */ new Map();
45173
46092
  for (const group of visibleGroups) {
45174
- const key = group.name.toLowerCase();
46093
+ const key = tagAttrKey(group.name);
45175
46094
  const used = usedValues.get(key);
45176
46095
  if (used && used.size > 0) {
45177
46096
  filteredEntries.set(
@@ -45192,7 +46111,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45192
46111
  const pillW = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD + iconReserve;
45193
46112
  let groupW = pillW;
45194
46113
  if (isActive) {
45195
- const entries = filteredEntries.get(group.name.toLowerCase()) ?? group.entries;
46114
+ const entries = filteredEntries.get(tagAttrKey(group.name)) ?? group.entries;
45196
46115
  let entriesW = 0;
45197
46116
  for (const entry of entries) {
45198
46117
  entriesW += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + measureLegendText(entry.value, LEGEND_ENTRY_FONT_SIZE) + LEGEND_ENTRY_TRAIL;
@@ -45249,7 +46168,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45249
46168
  },
45250
46169
  ...onControlsToggle !== void 0 && { onControlsToggle },
45251
46170
  onEntryHover: (groupName, entryValue) => {
45252
- const tagKey = groupName.toLowerCase();
46171
+ const tagKey = tagAttrKey(groupName);
45253
46172
  if (entryValue) {
45254
46173
  const ev = entryValue.toLowerCase();
45255
46174
  chartG.selectAll(".gantt-task").each(function() {
@@ -45939,7 +46858,7 @@ function buildTagLaneRowList(resolved, swimlaneGroup, collapsedLanes) {
45939
46858
  (g) => g.name.toLowerCase() === swimlaneGroup.toLowerCase()
45940
46859
  );
45941
46860
  if (!tagGroup) return null;
45942
- const tagKey = tagGroup.name.toLowerCase();
46861
+ const tagKey = tagAttrKey(tagGroup.name);
45943
46862
  const rows = [];
45944
46863
  const buckets = /* @__PURE__ */ new Map();
45945
46864
  const unbucketed = [];
@@ -46123,10 +47042,11 @@ function renderTimeScaleHorizontal(g, scale, _innerWidth, innerHeight, textColor
46123
47042
  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);
46124
47043
  }
46125
47044
  }
46126
- var d3Scale5, d3Selection18, 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;
47045
+ var d3Scale5, d3Selection18, 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;
46127
47046
  var init_renderer12 = __esm({
46128
47047
  "src/gantt/renderer.ts"() {
46129
47048
  "use strict";
47049
+ init_tag_groups();
46130
47050
  d3Scale5 = __toESM(require("d3-scale"), 1);
46131
47051
  d3Selection18 = __toESM(require("d3-selection"), 1);
46132
47052
  init_fonts();
@@ -46143,7 +47063,7 @@ var init_renderer12 = __esm({
46143
47063
  init_title_constants();
46144
47064
  BAR_H = 22;
46145
47065
  ROW_GAP = 6;
46146
- GROUP_GAP2 = 14;
47066
+ GROUP_GAP3 = 14;
46147
47067
  MILESTONE_SIZE = 10;
46148
47068
  MIN_LEFT_MARGIN = 120;
46149
47069
  BOTTOM_MARGIN = 40;
@@ -46209,7 +47129,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
46209
47129
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
46210
47130
  const sTitleY = ctx.structural(TITLE_Y);
46211
47131
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
46212
- const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE10);
47132
+ const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE11);
46213
47133
  const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
46214
47134
  const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
46215
47135
  const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
@@ -46489,7 +47409,7 @@ function renderStateForExport(content, theme, palette) {
46489
47409
  document.body.removeChild(container);
46490
47410
  }
46491
47411
  }
46492
- var d3Selection19, 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;
47412
+ var d3Selection19, 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;
46493
47413
  var init_state_renderer = __esm({
46494
47414
  "src/graph/state-renderer.ts"() {
46495
47415
  "use strict";
@@ -46509,7 +47429,7 @@ var init_state_renderer = __esm({
46509
47429
  DIAGRAM_PADDING12 = 20;
46510
47430
  MAX_SCALE7 = 3;
46511
47431
  NODE_FONT_SIZE6 = 13;
46512
- EDGE_LABEL_FONT_SIZE10 = 11;
47432
+ EDGE_LABEL_FONT_SIZE11 = 11;
46513
47433
  GROUP_LABEL_FONT_SIZE3 = 11;
46514
47434
  ARROWHEAD_W5 = 10;
46515
47435
  ARROWHEAD_H5 = 7;
@@ -48216,7 +49136,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48216
49136
  const panelWidth = 280;
48217
49137
  const textAreaWidth = panelWidth - silhouetteZone - CARD_PADDING_X3;
48218
49138
  const descLineH = 14;
48219
- const descLines = parsed.persona.description ? wrapText2(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
49139
+ const descLines = parsed.persona.description ? wrapNoteBody(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
48220
49140
  const descRowH = descLines.length > 0 ? descLines.length * descLineH + 8 : 0;
48221
49141
  const panelHeight = titleRowH + descRowH;
48222
49142
  const panelX = layout.totalWidth - PADDING2 - panelWidth;
@@ -48241,8 +49161,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48241
49161
  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);
48242
49162
  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);
48243
49163
  for (let li = 0; li < descLines.length; li++) {
48244
- const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
48245
- renderInlineText(lineEl, descLines[li], palette, FONT_SIZE_META);
49164
+ const line11 = descLines[li];
49165
+ const indent = line11.kind === "plain" ? 0 : NOTE_BULLET_INDENT;
49166
+ const lineY = panelY + titleRowH + descLineH * (li + 1);
49167
+ if (line11.kind === "bullet-first") {
49168
+ personaG.append("text").attr("x", textX).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText).text("\u2022");
49169
+ }
49170
+ const lineEl = personaG.append("text").attr("x", textX + indent).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
49171
+ renderInlineText(lineEl, line11.text, palette, FONT_SIZE_META);
48246
49172
  }
48247
49173
  if (onNavigateToLine) {
48248
49174
  personaG.style("cursor", "pointer").on("click", () => {
@@ -48281,7 +49207,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48281
49207
  return;
48282
49208
  }
48283
49209
  const isScore = groupName === "Score";
48284
- const attrName = isScore ? "data-score" : `data-tag-${groupName.toLowerCase()}`;
49210
+ const attrName = isScore ? "data-score" : `data-tag-${tagAttrKey(groupName)}`;
48285
49211
  const matches = (el) => el.getAttribute(attrName) === entryValue;
48286
49212
  svg.selectAll(".journey-step").each(function() {
48287
49213
  const hit = matches(this);
@@ -48762,7 +49688,7 @@ function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
48762
49688
  (g) => g.name.toLowerCase() === activeGroup.toLowerCase()
48763
49689
  );
48764
49690
  if (!group) return void 0;
48765
- const tagValue = step.tags[group.name.toLowerCase()];
49691
+ const tagValue = step.tags[tagAttrKey(group.name)];
48766
49692
  if (!tagValue) return void 0;
48767
49693
  const entry = group.entries.find(
48768
49694
  (e) => e.value.toLowerCase() === tagValue.toLowerCase()
@@ -48977,6 +49903,7 @@ var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3
48977
49903
  var init_renderer14 = __esm({
48978
49904
  "src/journey-map/renderer.ts"() {
48979
49905
  "use strict";
49906
+ init_tag_groups();
48980
49907
  d3 = __toESM(require("d3-selection"), 1);
48981
49908
  d3Shape10 = __toESM(require("d3-shape"), 1);
48982
49909
  init_color_utils();
@@ -48984,6 +49911,7 @@ var init_renderer14 = __esm({
48984
49911
  init_parser23();
48985
49912
  init_layout13();
48986
49913
  init_inline_markdown();
49914
+ init_note_box();
48987
49915
  init_legend_integration();
48988
49916
  init_tag_groups();
48989
49917
  init_scaling();
@@ -49323,10 +50251,10 @@ function renderedDescNodeHeight(numLines, scale) {
49323
50251
  return headerH + textBlockH + bottomPad;
49324
50252
  }
49325
50253
  function wrapEdgeLabelText(label, description, maxWidth = EDGE_LABEL_MAX_WIDTH) {
49326
- const labelLines = label ? wrapTextToWidth(label, EDGE_LABEL_FONT_SIZE11, maxWidth) : [];
50254
+ const labelLines = label ? wrapTextToWidth(label, EDGE_LABEL_FONT_SIZE12, maxWidth) : [];
49327
50255
  const descLines = [];
49328
50256
  for (const d of description) {
49329
- descLines.push(...wrapTextToWidth(d, EDGE_LABEL_FONT_SIZE11, maxWidth));
50257
+ descLines.push(...wrapTextToWidth(d, EDGE_LABEL_FONT_SIZE12, maxWidth));
49330
50258
  }
49331
50259
  return { labelLines, descLines };
49332
50260
  }
@@ -49464,9 +50392,9 @@ function computeEdgePaths(layoutNodes, parsed, cx, cy, radius, isClockwise) {
49464
50392
  const lineCount = labelLines.length + descLines.length;
49465
50393
  let labelPxW = 0;
49466
50394
  for (const l of labelLines)
49467
- labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE11));
50395
+ labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
49468
50396
  for (const l of descLines)
49469
- labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE11));
50397
+ labelPxW = Math.max(labelPxW, measureText(l, EDGE_LABEL_FONT_SIZE12));
49470
50398
  const { labelX, labelY, labelAngle } = computeEdgeLabelPosition(
49471
50399
  midAngle,
49472
50400
  radius,
@@ -49568,9 +50496,9 @@ function fitToCanvas(nodes, edges, parsed, _cx, _cy, radius, width, height, _isC
49568
50496
  );
49569
50497
  let textWidth = 0;
49570
50498
  for (const l of labelLines)
49571
- textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE11));
50499
+ textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
49572
50500
  for (const l of descLines)
49573
- textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE11));
50501
+ textWidth = Math.max(textWidth, measureText(l, EDGE_LABEL_FONT_SIZE12));
49574
50502
  if (textWidth === 0) continue;
49575
50503
  const normAngle = (le.labelAngle % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
49576
50504
  const isRight = normAngle < Math.PI * 0.4 || normAngle > Math.PI * 1.6;
@@ -49644,7 +50572,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
49644
50572
  const midAngle = srcExitAngle + dir * visibleSweep / 2;
49645
50573
  return { path, midAngle };
49646
50574
  }
49647
- 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;
50575
+ 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;
49648
50576
  var init_layout14 = __esm({
49649
50577
  "src/cycle/layout.ts"() {
49650
50578
  "use strict";
@@ -49655,7 +50583,7 @@ var init_layout14 = __esm({
49655
50583
  LABEL_FONT_SIZE4 = 13;
49656
50584
  CIRCLE_LABEL_FONT_SIZE = 16;
49657
50585
  DESC_FONT_SIZE6 = 11;
49658
- EDGE_LABEL_FONT_SIZE11 = 11;
50586
+ EDGE_LABEL_FONT_SIZE12 = 11;
49659
50587
  MIN_NODE_WIDTH4 = 70;
49660
50588
  MAX_NODE_WIDTH3 = 260;
49661
50589
  DESC_MIN_WIDTH = 140;
@@ -49800,7 +50728,7 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
49800
50728
  const scaledNodeFont = Math.max(6, Math.round(NODE_FONT_SIZE7 * layout.scale));
49801
50729
  const scaledEdgeLabelFont = Math.max(
49802
50730
  6,
49803
- Math.round(EDGE_LABEL_FONT_SIZE12 * layout.scale)
50731
+ Math.round(EDGE_LABEL_FONT_SIZE13 * layout.scale)
49804
50732
  );
49805
50733
  const CIRCLE_LABEL_FONT_SIZE2 = 16;
49806
50734
  const scaledCircleLabelFont = Math.max(
@@ -49963,7 +50891,7 @@ function ensureArrowMarker(defs, color, strokeWidth) {
49963
50891
  const mh = Math.max(1.5, mw * 0.5);
49964
50892
  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);
49965
50893
  }
49966
- var d3Selection22, NODE_FONT_SIZE7, DESC_FONT_SIZE7, EDGE_LABEL_FONT_SIZE12, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
50894
+ var d3Selection22, NODE_FONT_SIZE7, DESC_FONT_SIZE7, EDGE_LABEL_FONT_SIZE13, DESC_LINE_HEIGHT8, TITLE_AREA_HEIGHT;
49967
50895
  var init_renderer15 = __esm({
49968
50896
  "src/cycle/renderer.ts"() {
49969
50897
  "use strict";
@@ -49981,7 +50909,7 @@ var init_renderer15 = __esm({
49981
50909
  init_text_measure();
49982
50910
  NODE_FONT_SIZE7 = 13;
49983
50911
  DESC_FONT_SIZE7 = 11;
49984
- EDGE_LABEL_FONT_SIZE12 = 11;
50912
+ EDGE_LABEL_FONT_SIZE13 = 11;
49985
50913
  DESC_LINE_HEIGHT8 = 15;
49986
50914
  TITLE_AREA_HEIGHT = 50;
49987
50915
  }
@@ -51807,7 +52735,7 @@ function layoutMap(resolvedIn, data, size, opts) {
51807
52735
  (g) => g.name.toLowerCase() === groupName.toLowerCase()
51808
52736
  );
51809
52737
  if (!group) return null;
51810
- const val = tags[group.name.toLowerCase()] ?? group.defaultValue;
52738
+ const val = tags[tagAttrKey(group.name)] ?? group.defaultValue;
51811
52739
  if (!val) return null;
51812
52740
  const entry = group.entries.find(
51813
52741
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52421,7 +53349,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52421
53349
  if (directHex)
52422
53350
  return { fill: directHex, stroke: mix(directHex, palette.text, 18) };
52423
53351
  for (const group of resolved.tagGroups) {
52424
- const val = p.tags[group.name.toLowerCase()];
53352
+ const val = p.tags[tagAttrKey(group.name)];
52425
53353
  if (!val) continue;
52426
53354
  const entry = group.entries.find(
52427
53355
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52436,7 +53364,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52436
53364
  };
52437
53365
  const lineColor = (tags) => {
52438
53366
  for (const group of resolved.tagGroups) {
52439
- const val = tags[group.name.toLowerCase()];
53367
+ const val = tags[tagAttrKey(group.name)];
52440
53368
  if (!val) continue;
52441
53369
  const entry = group.entries.find(
52442
53370
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -53702,6 +54630,7 @@ var import_d3_geo2, import_topojson_client2, FIT_PAD, PATH_DIGITS, THIN_TOL, ALB
53702
54630
  var init_layout15 = __esm({
53703
54631
  "src/map/layout.ts"() {
53704
54632
  "use strict";
54633
+ init_tag_groups();
53705
54634
  import_d3_geo2 = require("d3-geo");
53706
54635
  import_topojson_client2 = require("topojson-client");
53707
54636
  init_color_utils();
@@ -56118,7 +57047,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56118
57047
  }
56119
57048
  return result;
56120
57049
  }
56121
- const groupKey = group.name.toLowerCase();
57050
+ const groupKey = tagAttrKey(group.name);
56122
57051
  const participantMeta = /* @__PURE__ */ new Map();
56123
57052
  for (const p of parsed.participants) {
56124
57053
  participantMeta.set(p.id, { ...p.metadata || {} });
@@ -56154,6 +57083,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56154
57083
  var init_tag_resolution = __esm({
56155
57084
  "src/sequence/tag-resolution.ts"() {
56156
57085
  "use strict";
57086
+ init_tag_groups();
56157
57087
  }
56158
57088
  });
56159
57089
 
@@ -56628,7 +57558,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56628
57558
  }
56629
57559
  }
56630
57560
  const getTagColor = (value) => value ? tagValueToColor.get(value.toLowerCase()) : void 0;
56631
- const tagKey = activeTagGroup?.toLowerCase();
57561
+ const tagKey = activeTagGroup ? tagAttrKey(activeTagGroup) : void 0;
56632
57562
  const hiddenMsgIndices = /* @__PURE__ */ new Set();
56633
57563
  if (collapsedSections && collapsedSections.size > 0) {
56634
57564
  const sectionGroups = groupMessagesBySection(elements, messages);
@@ -57220,11 +58150,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57220
58150
  const boxH = sBoxH + GROUP_PADDING_TOP2 + GROUP_PADDING_BOTTOM2;
57221
58151
  const groupTagValue = tagKey && group.metadata?.[tagKey];
57222
58152
  const groupTagColor = getTagColor(groupTagValue || void 0);
57223
- const fillColor = groupTagColor ? mix(
57224
- groupTagColor,
57225
- themeBaseBg(palette, isDark),
57226
- isDark ? 15 : 20
57227
- ) : isDark ? palette.surface : palette.bg;
58153
+ const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
57228
58154
  const strokeColor = groupTagColor || palette.textMuted;
57229
58155
  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");
57230
58156
  groupG.append("title").text("Click to collapse");
@@ -57274,11 +58200,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57274
58200
  );
57275
58201
  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");
57276
58202
  participantG.append("title").text("Click to expand");
57277
- const pFill = effectiveTagColor ? mix(
57278
- effectiveTagColor,
57279
- themeBaseBg(palette, isDark),
57280
- isDark ? 30 : 40
57281
- ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
58203
+ const pFill = effectiveTagColor ? mix(effectiveTagColor, themeBaseBg(palette, isDark), isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
57282
58204
  const pStroke = effectiveTagColor || palette.border;
57283
58205
  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);
57284
58206
  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);
@@ -57774,6 +58696,7 @@ var d3Selection27, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGH
57774
58696
  var init_renderer20 = __esm({
57775
58697
  "src/sequence/renderer.ts"() {
57776
58698
  "use strict";
58699
+ init_tag_groups();
57777
58700
  d3Selection27 = __toESM(require("d3-selection"), 1);
57778
58701
  init_color_utils();
57779
58702
  init_inline_markdown();
@@ -58293,6 +59216,7 @@ __export(index_exports, {
58293
59216
  palettes: () => palettes,
58294
59217
  render: () => render2,
58295
59218
  resolvePaletteOrFallback: () => resolvePaletteOrFallback,
59219
+ searchMapLocations: () => searchMapLocations,
58296
59220
  themes: () => themes,
58297
59221
  validate: () => parseDgmo
58298
59222
  });
@@ -58950,6 +59874,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
58950
59874
  init_parser3();
58951
59875
 
58952
59876
  // src/timeline/renderer.ts
59877
+ init_tag_groups();
58953
59878
  var d3Scale3 = __toESM(require("d3-scale"), 1);
58954
59879
  var d3Selection3 = __toESM(require("d3-selection"), 1);
58955
59880
  init_fonts();
@@ -59214,7 +60139,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
59214
60139
  });
59215
60140
  let tagLanes = null;
59216
60141
  if (resolvedSwimlaneTG) {
59217
- const tagKey = resolvedSwimlaneTG.toLowerCase();
60142
+ const tagKey = tagAttrKey(resolvedSwimlaneTG);
59218
60143
  const tagGroup = parsed.timelineTagGroups.find(
59219
60144
  (g) => g.name.toLowerCase() === tagKey
59220
60145
  );
@@ -59520,7 +60445,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59520
60445
  onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
59521
60446
  },
59522
60447
  onEntryHover: (groupName, entryValue) => {
59523
- const tagKey = groupName.toLowerCase();
60448
+ const tagKey = tagAttrKey(groupName);
59524
60449
  if (entryValue) {
59525
60450
  const tagVal = entryValue.toLowerCase();
59526
60451
  fadeToTagValue(mainG, tagKey, tagVal);
@@ -59539,7 +60464,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59539
60464
  }
59540
60465
  },
59541
60466
  onGroupRendered: (groupName, groupEl, isActive) => {
59542
- const groupKey = groupName.toLowerCase();
60467
+ const groupKey = tagAttrKey(groupName);
59543
60468
  groupEl.attr("data-tag-group", groupKey);
59544
60469
  if (isActive && !viewMode) {
59545
60470
  const isSwimActive = currentSwimlaneGroup?.toLowerCase() === groupKey;
@@ -62957,6 +63882,71 @@ function completeMapPlaces(query, gazetteer, opts) {
62957
63882
  });
62958
63883
  return [...cityItems, ...airportItems].slice(0, limit);
62959
63884
  }
63885
+ function searchMapLocations(query, gazetteer, opts) {
63886
+ const q = fold2(query);
63887
+ if (!q) return [];
63888
+ const limit = opts?.limit ?? 20;
63889
+ const rankOf = (hay) => hay === q ? 0 : hay.startsWith(q) ? 1 : hay.includes(q) ? 2 : -1;
63890
+ const cityRank = /* @__PURE__ */ new Map();
63891
+ const consider = (idx, rank) => {
63892
+ if (rank < 0) return;
63893
+ const prev = cityRank.get(idx);
63894
+ if (prev === void 0 || rank < prev) cityRank.set(idx, rank);
63895
+ };
63896
+ for (const [key, list] of Object.entries(gazetteer.byName)) {
63897
+ const r = rankOf(key);
63898
+ if (r >= 0) for (const i of list) consider(i, r);
63899
+ }
63900
+ for (const [key, idx] of Object.entries(gazetteer.alt)) {
63901
+ consider(idx, rankOf(key));
63902
+ }
63903
+ const cityItems = [...cityRank].filter(([i]) => gazetteer.cities[i] !== void 0).map(([i, rank]) => {
63904
+ const c = gazetteer.cities[i];
63905
+ const [, , iso, pop, name, sub] = c;
63906
+ const ambiguous = (gazetteer.byName[fold2(name)]?.length ?? 0) > 1;
63907
+ const qualifier = sub ?? iso;
63908
+ return {
63909
+ name,
63910
+ token: ambiguous ? `${name} ${qualifier}` : name,
63911
+ kind: "city",
63912
+ iso,
63913
+ ...sub !== void 0 && { sub },
63914
+ pop,
63915
+ detail: `${qualifier} \xB7 ${groupThousands(pop)}`,
63916
+ _rank: rank
63917
+ };
63918
+ });
63919
+ const airports = opts?.airports;
63920
+ const airportItems = [];
63921
+ if (airports?.airportIata) {
63922
+ for (const [code, idx] of Object.entries(airports.airportIata)) {
63923
+ const a = airports.airports[idx];
63924
+ if (!a) continue;
63925
+ const codeRank = rankOf(code);
63926
+ const nameRank = rankOf(fold2(a[4]));
63927
+ const rank = Math.min(
63928
+ codeRank < 0 ? 99 : codeRank,
63929
+ nameRank < 0 ? 99 : nameRank
63930
+ );
63931
+ if (rank > 2) continue;
63932
+ airportItems.push({
63933
+ name: a[4],
63934
+ token: code.toUpperCase(),
63935
+ kind: "airport",
63936
+ iso: a[2],
63937
+ pop: 0,
63938
+ detail: `Airport \xB7 ${a[4]}`,
63939
+ _rank: rank
63940
+ });
63941
+ }
63942
+ }
63943
+ return [...cityItems, ...airportItems].sort(
63944
+ (x, y) => x._rank - y._rank || // best match tier first
63945
+ (x.kind === y.kind ? 0 : x.kind === "city" ? -1 : 1) || // city before airport
63946
+ y.pop - x.pop || // populous city first
63947
+ (x.token < y.token ? -1 : x.token > y.token ? 1 : 0)
63948
+ ).slice(0, limit).map(({ _rank, ...rest }) => rest);
63949
+ }
62960
63950
  function completeMapRegions(query, regions, opts) {
62961
63951
  const q = fold2(query);
62962
63952
  if (!q) return [];
@@ -63051,6 +64041,7 @@ function decodeDiagramUrl2(url) {
63051
64041
  palettes,
63052
64042
  render,
63053
64043
  resolvePaletteOrFallback,
64044
+ searchMapLocations,
63054
64045
  themes,
63055
64046
  validate
63056
64047
  });