@diagrammo/dgmo 0.32.1 → 0.33.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 (57) 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 +981 -170
  6. package/dist/advanced.d.cts +35 -1
  7. package/dist/advanced.d.ts +35 -1
  8. package/dist/advanced.js +980 -170
  9. package/dist/auto.cjs +915 -171
  10. package/dist/auto.js +93 -93
  11. package/dist/auto.mjs +915 -171
  12. package/dist/cli.cjs +136 -134
  13. package/dist/index.cjs +981 -170
  14. package/dist/index.d.cts +35 -1
  15. package/dist/index.d.ts +35 -1
  16. package/dist/index.js +980 -170
  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/layout-grouped.ts +909 -0
  22. package/src/boxes-and-lines/layout-search.ts +18 -1
  23. package/src/boxes-and-lines/parser.ts +3 -2
  24. package/src/c4/layout.ts +2 -1
  25. package/src/c4/parser.ts +3 -2
  26. package/src/c4/renderer.ts +3 -2
  27. package/src/cli.ts +68 -0
  28. package/src/er/parser.ts +2 -1
  29. package/src/er/renderer.ts +2 -1
  30. package/src/gantt/parser.ts +3 -5
  31. package/src/gantt/renderer.ts +7 -6
  32. package/src/index.ts +6 -1
  33. package/src/infra/parser.ts +2 -1
  34. package/src/infra/renderer.ts +4 -3
  35. package/src/journey-map/parser.ts +27 -8
  36. package/src/journey-map/renderer.ts +26 -9
  37. package/src/kanban/parser.ts +4 -3
  38. package/src/kanban/renderer.ts +6 -5
  39. package/src/map/completion.ts +116 -0
  40. package/src/map/layout.ts +4 -3
  41. package/src/map/parser.ts +3 -2
  42. package/src/mindmap/parser.ts +4 -3
  43. package/src/mindmap/renderer.ts +2 -1
  44. package/src/org/layout.ts +3 -2
  45. package/src/org/parser.ts +3 -2
  46. package/src/org/renderer.ts +5 -4
  47. package/src/pert/parser.ts +3 -2
  48. package/src/sequence/renderer.ts +10 -12
  49. package/src/sequence/tag-resolution.ts +2 -1
  50. package/src/sitemap/layout.ts +3 -2
  51. package/src/sitemap/parser.ts +36 -2
  52. package/src/sitemap/renderer.ts +12 -6
  53. package/src/timeline/renderer.ts +4 -3
  54. package/src/utils/legend-d3.ts +4 -3
  55. package/src/utils/legend-svg.ts +3 -2
  56. package/src/utils/tag-groups.ts +28 -7
  57. package/src/visualizations/parse.ts +2 -1
package/dist/index.js CHANGED
@@ -1156,6 +1156,9 @@ var init_parsing = __esm({
1156
1156
  });
1157
1157
 
1158
1158
  // src/utils/tag-groups.ts
1159
+ function tagAttrKey(name) {
1160
+ return name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
1161
+ }
1159
1162
  function stripDefaultModifier(text) {
1160
1163
  if (/\bdefault\s*$/.test(text)) {
1161
1164
  return { text: text.replace(/\s+default\s*$/, "").trim(), isDefault: true };
@@ -1301,17 +1304,17 @@ function parseTagDeclaration(line11) {
1301
1304
  function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
1302
1305
  if (!activeGroupName) return void 0;
1303
1306
  const group = tagGroups.find(
1304
- (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
1307
+ (g) => tagAttrKey(g.name) === tagAttrKey(activeGroupName)
1305
1308
  );
1306
1309
  if (!group) return void 0;
1307
- const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
1310
+ const metaValue = metadata[tagAttrKey(group.name)] ?? (isContainer ? void 0 : group.defaultValue);
1308
1311
  if (!metaValue) return "#999999";
1309
1312
  return group.entries.find((e) => e.value.toLowerCase() === metaValue.toLowerCase())?.color ?? "#999999";
1310
1313
  }
1311
1314
  function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
1312
1315
  if (tagGroups.length === 0) return;
1313
1316
  const groupMap = /* @__PURE__ */ new Map();
1314
- for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
1317
+ for (const g of tagGroups) groupMap.set(tagAttrKey(g.name), g);
1315
1318
  for (const entity of entities) {
1316
1319
  for (const [key, value] of Object.entries(entity.metadata)) {
1317
1320
  const group = groupMap.get(key);
@@ -1348,10 +1351,10 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1348
1351
  `'none' is a reserved keyword and cannot be used as a tag group name`
1349
1352
  );
1350
1353
  }
1351
- if (!VALID_TAG_IDENT_RE.test(group.name)) {
1354
+ if (!VALID_TAG_IDENT_RE.test(tagAttrKey(group.name))) {
1352
1355
  report(
1353
1356
  group.lineNumber,
1354
- `Tag group name "${group.name}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
1357
+ `Tag group name "${group.name}" can't form a valid key \u2014 use letters, digits, underscore, or hyphen, and quote a multi-word name (e.g. tag "Trust Zone" as tz)`
1355
1358
  );
1356
1359
  }
1357
1360
  if (group.alias != null && !VALID_TAG_IDENT_RE.test(group.alias)) {
@@ -1363,7 +1366,7 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1363
1366
  }
1364
1367
  }
1365
1368
  function cascadeTagMetadata(roots, tagGroups) {
1366
- const keys = tagGroups.map((g) => g.name.toLowerCase());
1369
+ const keys = tagGroups.map((g) => tagAttrKey(g.name));
1367
1370
  if (keys.length === 0) return;
1368
1371
  const walk = (node, inherited) => {
1369
1372
  const childInherited = { ...inherited };
@@ -1384,7 +1387,7 @@ function injectDefaultTagMetadata(entities, tagGroups, skip) {
1384
1387
  for (const group of tagGroups) {
1385
1388
  if (group.defaultValue) {
1386
1389
  defaults.push({
1387
- key: group.name.toLowerCase(),
1390
+ key: tagAttrKey(group.name),
1388
1391
  value: group.defaultValue
1389
1392
  });
1390
1393
  }
@@ -2775,7 +2778,7 @@ function parseVisualizationFull(content, palette) {
2775
2778
  );
2776
2779
  for (const group of result.timelineTagGroups) {
2777
2780
  if (!group.defaultValue) continue;
2778
- const key = group.name.toLowerCase();
2781
+ const key = tagAttrKey(group.name);
2779
2782
  for (const event of result.timelineEvents) {
2780
2783
  if (!event.metadata[key]) {
2781
2784
  event.metadata[key] = group.defaultValue;
@@ -5167,7 +5170,7 @@ function renderLegendD3(container, config, state, palette, isDark, callbacks, co
5167
5170
  };
5168
5171
  }
5169
5172
  function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, callbacks) {
5170
- const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", capsule.groupName.toLowerCase()).style("cursor", "pointer");
5173
+ const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", tagAttrKey(capsule.groupName)).style("cursor", "pointer");
5171
5174
  g.append("rect").attr("width", capsule.width).attr("height", capsule.height).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
5172
5175
  const pill = capsule.pill;
5173
5176
  g.append("rect").attr("x", pill.x).attr("y", pill.y).attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", palette.bg);
@@ -5175,7 +5178,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5175
5178
  g.append("text").attr("x", pill.x + pill.width / 2).attr("y", LEGEND_HEIGHT / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.text).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(capsule.groupName);
5176
5179
  if (capsule.gradient) {
5177
5180
  const gr = capsule.gradient;
5178
- const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
5181
+ const gradId = `dgmo-legend-ramp-${tagAttrKey(capsule.groupName).replace(/[^a-z0-9]+/g, "-")}`;
5179
5182
  const def = g.append("defs").append("linearGradient").attr("id", gradId);
5180
5183
  for (const stop of valueRampStops(gr.low, gr.high, { isDark })) {
5181
5184
  def.append("stop").attr("offset", `${stop.offset * 100}%`).attr("stop-color", stop.color);
@@ -5205,7 +5208,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5205
5208
  }
5206
5209
  }
5207
5210
  function renderPill(parent, pill, palette, groupBg, callbacks) {
5208
- const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", pill.groupName.toLowerCase()).style("cursor", "pointer");
5211
+ const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", tagAttrKey(pill.groupName)).style("cursor", "pointer");
5209
5212
  g.append("rect").attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", groupBg);
5210
5213
  g.append("text").attr("x", pill.width / 2).attr("y", pill.height / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(pill.groupName);
5211
5214
  if (callbacks?.onGroupToggle) {
@@ -5311,6 +5314,7 @@ var LEGEND_TEXT_DY;
5311
5314
  var init_legend_d3 = __esm({
5312
5315
  "src/utils/legend-d3.ts"() {
5313
5316
  "use strict";
5317
+ init_tag_groups();
5314
5318
  init_legend_constants();
5315
5319
  init_legend_layout();
5316
5320
  init_color_utils();
@@ -9297,7 +9301,7 @@ function parseERDiagram(content, palette) {
9297
9301
  );
9298
9302
  for (const group of result.tagGroups) {
9299
9303
  if (!group.defaultValue) continue;
9300
- const key = group.name.toLowerCase();
9304
+ const key = tagAttrKey(group.name);
9301
9305
  for (const table of result.tables) {
9302
9306
  if (!table.metadata[key]) {
9303
9307
  const mutableMeta = tableMetadataMap.get(table.id);
@@ -9527,12 +9531,12 @@ function parseOrg(content, palette) {
9527
9531
  if (tagBlockMatch.alias) {
9528
9532
  metaAliasMap.set(
9529
9533
  normalizeName(tagBlockMatch.alias),
9530
- tagBlockMatch.name.toLowerCase()
9534
+ tagAttrKey(tagBlockMatch.name)
9531
9535
  );
9532
9536
  }
9533
9537
  metaAliasMap.set(
9534
9538
  normalizeName(tagBlockMatch.name),
9535
- tagBlockMatch.name.toLowerCase()
9539
+ tagAttrKey(tagBlockMatch.name)
9536
9540
  );
9537
9541
  result.tagGroups.push(currentTagGroup);
9538
9542
  continue;
@@ -9918,12 +9922,12 @@ function parseSitemap(content, palette) {
9918
9922
  if (tagBlockMatch.alias) {
9919
9923
  metaAliasMap.set(
9920
9924
  normalizeName(tagBlockMatch.alias),
9921
- tagBlockMatch.name.toLowerCase()
9925
+ tagAttrKey(tagBlockMatch.name)
9922
9926
  );
9923
9927
  }
9924
9928
  metaAliasMap.set(
9925
9929
  normalizeName(tagBlockMatch.name),
9926
- tagBlockMatch.name.toLowerCase()
9930
+ tagAttrKey(tagBlockMatch.name)
9927
9931
  );
9928
9932
  result.tagGroups.push(newTagGroup);
9929
9933
  continue;
@@ -10093,6 +10097,14 @@ function parseSitemap(content, palette) {
10093
10097
  continue;
10094
10098
  }
10095
10099
  }
10100
+ const enclosingContainer = nestedPageInsideContainer(indent, indentStack);
10101
+ if (enclosingContainer) {
10102
+ pushError(
10103
+ lineNumber,
10104
+ `Pages inside container '[${enclosingContainer}]' cannot have indented sub-pages. Containers hold a flat list of pages.`
10105
+ );
10106
+ continue;
10107
+ }
10096
10108
  const node = parseNodeLabel2(
10097
10109
  trimmed,
10098
10110
  lineNumber,
@@ -10240,6 +10252,16 @@ function attachNode2(node, indent, indentStack, result) {
10240
10252
  }
10241
10253
  indentStack.push({ node, indent });
10242
10254
  }
10255
+ function nestedPageInsideContainer(indent, indentStack) {
10256
+ let idx = indentStack.length - 1;
10257
+ while (idx >= 0 && indentStack[idx].indent >= indent) idx--;
10258
+ if (idx < 0) return null;
10259
+ if (indentStack[idx].node.isContainer) return null;
10260
+ for (let k = idx - 1; k >= 0; k--) {
10261
+ if (indentStack[k].node.isContainer) return indentStack[k].node.label;
10262
+ }
10263
+ return null;
10264
+ }
10243
10265
  function findParentNode(indent, indentStack) {
10244
10266
  for (let i = indentStack.length - 1; i >= 0; i--) {
10245
10267
  if (indentStack[i].indent < indent) {
@@ -10823,12 +10845,12 @@ function parsePert(content, parseOpts = {}) {
10823
10845
  if (tagBlockMatch.alias) {
10824
10846
  metaAliasMap.set(
10825
10847
  tagBlockMatch.alias.toLowerCase(),
10826
- tagBlockMatch.name.toLowerCase()
10848
+ tagAttrKey(tagBlockMatch.name)
10827
10849
  );
10828
10850
  }
10829
10851
  metaAliasMap.set(
10830
10852
  normalizeName(tagBlockMatch.name),
10831
- tagBlockMatch.name.toLowerCase()
10853
+ tagAttrKey(tagBlockMatch.name)
10832
10854
  );
10833
10855
  tagGroups.push(currentTagGroup);
10834
10856
  if (tagBlockMatch.inlineValues) {
@@ -12293,10 +12315,10 @@ function emitCapsule(capsule, palette, groupBg, pillBorder) {
12293
12315
  `<g data-legend-entry="${esc(entry.value.toLowerCase())}" data-series-name="${esc(entry.value)}" style="cursor:pointer"><circle cx="${entry.dotCx}" cy="${entry.dotCy}" r="${LEGEND_DOT_R}" fill="${esc(entry.color)}"/><text x="${entry.textX}" y="${entry.dotCy + LEGEND_ENTRY_FONT_SIZE / 2 - 1}" font-size="${LEGEND_ENTRY_FONT_SIZE}" fill="${esc(palette.textMuted)}" font-family="${esc(FONT_FAMILY)}">${esc(label)}</text></g>`
12294
12316
  );
12295
12317
  }
12296
- return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(capsule.groupName.toLowerCase())}" style="cursor:pointer">${inner.join("")}</g>`;
12318
+ return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(tagAttrKey(capsule.groupName))}" style="cursor:pointer">${inner.join("")}</g>`;
12297
12319
  }
12298
12320
  function emitPill(pill, palette, groupBg) {
12299
- return `<g transform="translate(${pill.x},${pill.y})" data-legend-group="${esc(pill.groupName.toLowerCase())}" style="cursor:pointer"><rect width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="${esc(groupBg)}"/><text x="${pill.width / 2}" y="${pill.height / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2}" font-size="${LEGEND_PILL_FONT_SIZE}" font-weight="500" fill="${esc(palette.textMuted)}" text-anchor="middle" font-family="${esc(FONT_FAMILY)}">${esc(pill.groupName)}</text></g>`;
12321
+ return `<g transform="translate(${pill.x},${pill.y})" data-legend-group="${esc(tagAttrKey(pill.groupName))}" style="cursor:pointer"><rect width="${pill.width}" height="${pill.height}" rx="${pill.height / 2}" fill="${esc(groupBg)}"/><text x="${pill.width / 2}" y="${pill.height / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2}" font-size="${LEGEND_PILL_FONT_SIZE}" font-weight="500" fill="${esc(palette.textMuted)}" text-anchor="middle" font-family="${esc(FONT_FAMILY)}">${esc(pill.groupName)}</text></g>`;
12300
12322
  }
12301
12323
  function renderLegendSvg(groups, options) {
12302
12324
  if (groups.length === 0) return { svg: "", height: 0, width: 0 };
@@ -12344,6 +12366,7 @@ function renderLegendSvg(groups, options) {
12344
12366
  var init_legend_svg = __esm({
12345
12367
  "src/utils/legend-svg.ts"() {
12346
12368
  "use strict";
12369
+ init_tag_groups();
12347
12370
  init_legend_constants();
12348
12371
  init_legend_layout();
12349
12372
  init_color_utils();
@@ -15052,12 +15075,12 @@ function parseKanban(content, palette) {
15052
15075
  if (tagBlockMatch.alias) {
15053
15076
  metaAliasMap.set(
15054
15077
  normalizeName(tagBlockMatch.alias),
15055
- tagBlockMatch.name.toLowerCase()
15078
+ tagAttrKey(tagBlockMatch.name)
15056
15079
  );
15057
15080
  }
15058
15081
  metaAliasMap.set(
15059
15082
  normalizeName(tagBlockMatch.name),
15060
- tagBlockMatch.name.toLowerCase()
15083
+ tagAttrKey(tagBlockMatch.name)
15061
15084
  );
15062
15085
  result.tagGroups.push(currentTagGroup);
15063
15086
  continue;
@@ -15246,7 +15269,7 @@ function parseKanban(content, palette) {
15246
15269
  }
15247
15270
  for (const group of result.tagGroups) {
15248
15271
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
15249
- tagValueSets.set(group.name.toLowerCase(), values);
15272
+ tagValueSets.set(tagAttrKey(group.name), values);
15250
15273
  }
15251
15274
  for (const col of result.columns) {
15252
15275
  if (col.wipLimit != null && col.cards.length > col.wipLimit) {
@@ -15508,12 +15531,12 @@ function parseC4(content, palette) {
15508
15531
  if (tagBlockMatch.alias) {
15509
15532
  metaAliasMap.set(
15510
15533
  normalizeName(tagBlockMatch.alias),
15511
- tagBlockMatch.name.toLowerCase()
15534
+ tagAttrKey(tagBlockMatch.name)
15512
15535
  );
15513
15536
  }
15514
15537
  metaAliasMap.set(
15515
15538
  normalizeName(tagBlockMatch.name),
15516
- tagBlockMatch.name.toLowerCase()
15539
+ tagAttrKey(tagBlockMatch.name)
15517
15540
  );
15518
15541
  result.tagGroups.push(currentTagGroup);
15519
15542
  continue;
@@ -17196,7 +17219,7 @@ function parseInfra(content) {
17196
17219
  }
17197
17220
  for (const tg of result.tagGroups) {
17198
17221
  if (!tg.defaultValue) continue;
17199
- const key = (tg.alias ?? tg.name).toLowerCase();
17222
+ const key = tagAttrKey(tg.alias ?? tg.name);
17200
17223
  for (const node of result.nodes) {
17201
17224
  if (node.isEdge) continue;
17202
17225
  if (!(key in node.tags)) {
@@ -17333,6 +17356,7 @@ var CONNECTION_RE, SIMPLE_CONNECTION_RE, ASYNC_CONNECTION_RE, ASYNC_SIMPLE_CONNE
17333
17356
  var init_parser15 = __esm({
17334
17357
  "src/infra/parser.ts"() {
17335
17358
  "use strict";
17359
+ init_tag_groups();
17336
17360
  init_diagnostics();
17337
17361
  init_description_helpers();
17338
17362
  init_arrows();
@@ -17878,13 +17902,10 @@ function parseGantt(content, palette) {
17878
17902
  if (tagMatch.alias) {
17879
17903
  metaAliasMap.set(
17880
17904
  normalizeName(tagMatch.alias),
17881
- tagMatch.name.toLowerCase()
17905
+ tagAttrKey(tagMatch.name)
17882
17906
  );
17883
17907
  }
17884
- metaAliasMap.set(
17885
- normalizeName(tagMatch.name),
17886
- tagMatch.name.toLowerCase()
17887
- );
17908
+ metaAliasMap.set(normalizeName(tagMatch.name), tagAttrKey(tagMatch.name));
17888
17909
  continue;
17889
17910
  }
17890
17911
  const topWorkweekMatch = line11.match(WORKWEEK_RE);
@@ -18810,7 +18831,7 @@ function parseMap(content, palette) {
18810
18831
  const aliasMap = /* @__PURE__ */ new Map();
18811
18832
  const open = { tag: null, route: null, poi: null };
18812
18833
  const registry = () => withTagAliases(MAP_REGISTRY, new Set(aliasMap.keys()));
18813
- const tagGroupNames = () => new Set(tagGroups.map((g) => g.name.toLowerCase()));
18834
+ const tagGroupNames = () => new Set(tagGroups.map((g) => tagAttrKey(g.name)));
18814
18835
  const closeBlocks = () => {
18815
18836
  open.tag = null;
18816
18837
  open.route = null;
@@ -18994,7 +19015,7 @@ function parseMap(content, palette) {
18994
19015
  entries: [],
18995
19016
  lineNumber: line11
18996
19017
  };
18997
- if (m.alias) aliasMap.set(m.alias.toLowerCase(), m.name.toLowerCase());
19018
+ if (m.alias) aliasMap.set(m.alias.toLowerCase(), tagAttrKey(m.name));
18998
19019
  tagGroups.push(group);
18999
19020
  open.tag = group;
19000
19021
  if (m.inlineValues?.length) {
@@ -19527,12 +19548,12 @@ function parseBoxesAndLines(content, palette) {
19527
19548
  if (tagBlockMatch.alias) {
19528
19549
  metaAliasMap.set(
19529
19550
  normalizeName(tagBlockMatch.alias),
19530
- tagBlockMatch.name.toLowerCase()
19551
+ tagAttrKey(tagBlockMatch.name)
19531
19552
  );
19532
19553
  }
19533
19554
  metaAliasMap.set(
19534
19555
  normalizeName(tagBlockMatch.name),
19535
- tagBlockMatch.name.toLowerCase()
19556
+ tagAttrKey(tagBlockMatch.name)
19536
19557
  );
19537
19558
  if (tagBlockMatch.inlineValues) {
19538
19559
  for (const rawVal of tagBlockMatch.inlineValues) {
@@ -20273,12 +20294,12 @@ function parseMindmap(content, palette) {
20273
20294
  if (tagBlockMatch.alias) {
20274
20295
  aliasMap.set(
20275
20296
  tagBlockMatch.alias.toLowerCase(),
20276
- tagBlockMatch.name.toLowerCase()
20297
+ tagAttrKey(tagBlockMatch.name)
20277
20298
  );
20278
20299
  }
20279
20300
  aliasMap.set(
20280
- tagBlockMatch.name.toLowerCase(),
20281
- tagBlockMatch.name.toLowerCase()
20301
+ tagAttrKey(tagBlockMatch.name),
20302
+ tagAttrKey(tagBlockMatch.name)
20282
20303
  );
20283
20304
  result.tagGroups.push(currentTagGroup);
20284
20305
  continue;
@@ -21987,7 +22008,18 @@ function parseJourneyMap(content, palette) {
21987
22008
  palette
21988
22009
  ) ?? void 0;
21989
22010
  } else {
21990
- personaName = afterKeyword;
22011
+ const { label, colorName } = peelTrailingColorName(afterKeyword);
22012
+ if (colorName) {
22013
+ personaName = label;
22014
+ personaColor = resolveColorWithDiagnostic(
22015
+ colorName,
22016
+ lineNumber,
22017
+ result.diagnostics,
22018
+ palette
22019
+ ) ?? void 0;
22020
+ } else {
22021
+ personaName = afterKeyword;
22022
+ }
21991
22023
  }
21992
22024
  }
21993
22025
  if (!personaName) {
@@ -22030,12 +22062,12 @@ function parseJourneyMap(content, palette) {
22030
22062
  if (tagBlockMatch.alias) {
22031
22063
  aliasMap.set(
22032
22064
  tagBlockMatch.alias.toLowerCase(),
22033
- tagBlockMatch.name.toLowerCase()
22065
+ tagAttrKey(tagBlockMatch.name)
22034
22066
  );
22035
22067
  }
22036
22068
  aliasMap.set(
22037
- tagBlockMatch.name.toLowerCase(),
22038
- tagBlockMatch.name.toLowerCase()
22069
+ tagAttrKey(tagBlockMatch.name),
22070
+ tagAttrKey(tagBlockMatch.name)
22039
22071
  );
22040
22072
  result.tagGroups.push(currentTagGroup);
22041
22073
  continue;
@@ -22179,7 +22211,7 @@ function parseJourneyMap(content, palette) {
22179
22211
  }
22180
22212
  for (const group of result.tagGroups) {
22181
22213
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
22182
- tagValueSets.set(group.name.toLowerCase(), values);
22214
+ tagValueSets.set(tagAttrKey(group.name), values);
22183
22215
  }
22184
22216
  const allSteps = hasPhases ? result.phases.flatMap((p) => p.steps) : result.steps;
22185
22217
  for (const step of allSteps) {
@@ -24166,7 +24198,7 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
24166
24198
  const groups = [];
24167
24199
  for (const group of tagGroups) {
24168
24200
  if (group.entries.length === 0) continue;
24169
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
24201
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
24170
24202
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
24171
24203
  if (visibleEntries.length === 0) continue;
24172
24204
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -24779,7 +24811,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
24779
24811
  const totalHeight = finalMaxY - minY + MARGIN * 2;
24780
24812
  const usedValuesByGroup = /* @__PURE__ */ new Map();
24781
24813
  for (const group of parsed.tagGroups) {
24782
- const key = group.name.toLowerCase();
24814
+ const key = tagAttrKey(group.name);
24783
24815
  const used = /* @__PURE__ */ new Set();
24784
24816
  const walk = (node) => {
24785
24817
  if (!node.isContainer && node.metadata[key]) {
@@ -24838,6 +24870,7 @@ var init_layout = __esm({
24838
24870
  "src/org/layout.ts"() {
24839
24871
  "use strict";
24840
24872
  init_tag_groups();
24873
+ init_tag_groups();
24841
24874
  init_legend_constants();
24842
24875
  init_text_measure();
24843
24876
  LABEL_FONT_SIZE = 13;
@@ -25198,14 +25231,14 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25198
25231
  const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
25199
25232
  const displayNames = /* @__PURE__ */ new Map();
25200
25233
  for (const group of parsed.tagGroups) {
25201
- displayNames.set(group.name.toLowerCase(), group.name);
25234
+ displayNames.set(tagAttrKey(group.name), group.name);
25202
25235
  }
25203
25236
  const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
25204
25237
  const colorOff = parsed.options?.["color"] === "off";
25205
25238
  for (const c of layout.containers) {
25206
25239
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
25207
25240
  if (activeTagGroup) {
25208
- const tagKey = activeTagGroup.toLowerCase();
25241
+ const tagKey = tagAttrKey(activeTagGroup);
25209
25242
  const metaValue = c.tagMetadata[tagKey];
25210
25243
  if (metaValue) {
25211
25244
  cG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25287,7 +25320,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25287
25320
  if (containerNodeIds.has(node.id)) continue;
25288
25321
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x - node.width / 2}, ${node.y})`).attr("class", "org-node").attr("data-line-number", String(node.lineNumber));
25289
25322
  if (activeTagGroup) {
25290
- const tagKey = activeTagGroup.toLowerCase();
25323
+ const tagKey = tagAttrKey(activeTagGroup);
25291
25324
  const metaValue = node.tagMetadata[tagKey];
25292
25325
  if (metaValue) {
25293
25326
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25460,7 +25493,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25460
25493
  const computedLayout = legendHandle.getLayout();
25461
25494
  if (computedLayout.activeCapsule?.addonX != null) {
25462
25495
  const capsule = computedLayout.activeCapsule;
25463
- const groupKey = capsule.groupName.toLowerCase();
25496
+ const groupKey = tagAttrKey(capsule.groupName);
25464
25497
  const isHidden = hiddenAttributes?.has(groupKey) ?? false;
25465
25498
  const activeGroupEl = legendParentBase.select(
25466
25499
  `[data-legend-group="${groupKey}"]`
@@ -25499,6 +25532,7 @@ var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCESTOR_LABEL_FON
25499
25532
  var init_renderer = __esm({
25500
25533
  "src/org/renderer.ts"() {
25501
25534
  "use strict";
25535
+ init_tag_groups();
25502
25536
  init_fonts();
25503
25537
  init_export_container();
25504
25538
  init_scaling();
@@ -25584,7 +25618,7 @@ function computeLegendGroups2(tagGroups, usedValuesByGroup) {
25584
25618
  const groups = [];
25585
25619
  for (const group of tagGroups) {
25586
25620
  if (group.entries.length === 0) continue;
25587
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
25621
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
25588
25622
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
25589
25623
  if (visibleEntries.length === 0) continue;
25590
25624
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -26064,7 +26098,7 @@ function layoutSitemap(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, e
26064
26098
  totalHeight += MARGIN2;
26065
26099
  const usedValuesByGroup = /* @__PURE__ */ new Map();
26066
26100
  for (const group of parsed.tagGroups) {
26067
- const key = group.name.toLowerCase();
26101
+ const key = tagAttrKey(group.name);
26068
26102
  const used = /* @__PURE__ */ new Set();
26069
26103
  const walk = (node) => {
26070
26104
  if (!node.isContainer && node.metadata[key]) {
@@ -26109,6 +26143,7 @@ var init_layout2 = __esm({
26109
26143
  "src/sitemap/layout.ts"() {
26110
26144
  "use strict";
26111
26145
  init_tag_groups();
26146
+ init_tag_groups();
26112
26147
  init_legend_constants();
26113
26148
  init_text_measure();
26114
26149
  LABEL_FONT_SIZE3 = 13;
@@ -26378,10 +26413,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26378
26413
  const displayNames = /* @__PURE__ */ new Map();
26379
26414
  const tagColors = /* @__PURE__ */ new Map();
26380
26415
  for (const group of parsed.tagGroups) {
26381
- displayNames.set(group.name.toLowerCase(), group.name);
26416
+ displayNames.set(tagAttrKey(group.name), group.name);
26382
26417
  for (const entry of group.entries) {
26383
26418
  tagColors.set(
26384
- `${group.name.toLowerCase()}:${entry.value.toLowerCase()}`,
26419
+ `${tagAttrKey(group.name)}:${entry.value.toLowerCase()}`,
26385
26420
  entry.color
26386
26421
  );
26387
26422
  }
@@ -26398,7 +26433,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26398
26433
  );
26399
26434
  }
26400
26435
  if (activeTagGroup) {
26401
- const tagKey = activeTagGroup.toLowerCase();
26436
+ const tagKey = tagAttrKey(activeTagGroup);
26402
26437
  const tagVal = c.tagMetadata[tagKey];
26403
26438
  if (tagVal) cG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26404
26439
  }
@@ -26461,7 +26496,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26461
26496
  nodeG.style("cursor", "pointer").on("click", () => onClickItem(node.lineNumber));
26462
26497
  }
26463
26498
  if (activeTagGroup) {
26464
- const tagKey = activeTagGroup.toLowerCase();
26499
+ const tagKey = tagAttrKey(activeTagGroup);
26465
26500
  const tagVal = node.tagMetadata[tagKey];
26466
26501
  if (tagVal) nodeG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26467
26502
  }
@@ -26588,7 +26623,7 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
26588
26623
  const computedLayout = legendHandle.getLayout();
26589
26624
  if (computedLayout.activeCapsule?.addonX != null) {
26590
26625
  const capsule = computedLayout.activeCapsule;
26591
- const groupKey = capsule.groupName.toLowerCase();
26626
+ const groupKey = tagAttrKey(capsule.groupName);
26592
26627
  const isHidden = hiddenAttributes?.has(groupKey) ?? false;
26593
26628
  const activeGroupEl = parent.select(`[data-legend-group="${groupKey}"]`);
26594
26629
  if (!activeGroupEl.empty()) {
@@ -26653,6 +26688,7 @@ var DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_
26653
26688
  var init_renderer2 = __esm({
26654
26689
  "src/sitemap/renderer.ts"() {
26655
26690
  "use strict";
26691
+ init_tag_groups();
26656
26692
  init_fonts();
26657
26693
  init_color_utils();
26658
26694
  init_inline_markdown();
@@ -26700,7 +26736,7 @@ function resolveCardTagMeta(card, tagGroups, hiddenMetaGroups) {
26700
26736
  const meta = [];
26701
26737
  for (const group of tagGroups) {
26702
26738
  if (hiddenMetaGroups?.includes(group.name.toLowerCase())) continue;
26703
- const tagValue = card.tags[group.name.toLowerCase()];
26739
+ const tagValue = card.tags[tagAttrKey(group.name)];
26704
26740
  const value = tagValue ?? group.defaultValue;
26705
26741
  if (!value) continue;
26706
26742
  const entry = group.entries.find(
@@ -26720,7 +26756,7 @@ function resolveCardTagColor(card, tagGroups, activeTagGroup) {
26720
26756
  (g) => g.name.toLowerCase() === activeTagGroup.toLowerCase()
26721
26757
  );
26722
26758
  if (!group) return card.color;
26723
- const tagValue = card.tags[group.name.toLowerCase()];
26759
+ const tagValue = card.tags[tagAttrKey(group.name)];
26724
26760
  const value = tagValue ?? group.defaultValue;
26725
26761
  if (!value) return void 0;
26726
26762
  const entry = group.entries.find(
@@ -27030,7 +27066,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
27030
27066
  );
27031
27067
  const cg = g.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27032
27068
  if (activeTagGroup) {
27033
- const tagKey = activeTagGroup.toLowerCase();
27069
+ const tagKey = tagAttrKey(activeTagGroup);
27034
27070
  const tagValue = card.tags[tagKey];
27035
27071
  const group = parsed.tagGroups.find(
27036
27072
  (tg) => tg.name.toLowerCase() === tagKey
@@ -27091,7 +27127,7 @@ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
27091
27127
  return iconG;
27092
27128
  }
27093
27129
  function bucketCardsBySwimlane(columns, swimlaneGroup) {
27094
- const tagKey = swimlaneGroup.name.toLowerCase();
27130
+ const tagKey = tagAttrKey(swimlaneGroup.name);
27095
27131
  const buckets = /* @__PURE__ */ new Map();
27096
27132
  for (const entry of swimlaneGroup.entries) {
27097
27133
  buckets.set(entry.value.toLowerCase(), {
@@ -27379,7 +27415,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
27379
27415
  );
27380
27416
  const cg = parent.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27381
27417
  if (activeTagGroup) {
27382
- const tagKey = activeTagGroup.toLowerCase();
27418
+ const tagKey = tagAttrKey(activeTagGroup);
27383
27419
  const group = tagGroups.find((tg) => tg.name.toLowerCase() === tagKey);
27384
27420
  const value = card.tags[tagKey] ?? group?.defaultValue;
27385
27421
  if (value) {
@@ -27412,6 +27448,7 @@ var DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_M
27412
27448
  var init_renderer3 = __esm({
27413
27449
  "src/kanban/renderer.ts"() {
27414
27450
  "use strict";
27451
+ init_tag_groups();
27415
27452
  init_fonts();
27416
27453
  init_color_utils();
27417
27454
  init_inline_markdown();
@@ -28393,7 +28430,7 @@ function classifyEREntities(tables, relationships) {
28393
28430
  const variance = indegreeValues.reduce((a, b) => a + (b - mean) ** 2, 0) / indegreeValues.length;
28394
28431
  const stddev = Math.sqrt(variance);
28395
28432
  const sorted = [...indegreeValues].sort((a, b) => a - b);
28396
- const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28433
+ const median2 = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28397
28434
  for (const table of tables) {
28398
28435
  const id = table.id;
28399
28436
  const cols = table.columns;
@@ -28434,7 +28471,7 @@ function classifyEREntities(tables, relationships) {
28434
28471
  const nameMatchesLookup = LOOKUP_NAME_SUFFIXES.some(
28435
28472
  (s) => nameLower.endsWith(s)
28436
28473
  );
28437
- if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median) {
28474
+ if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median2) {
28438
28475
  result.set(id, "lookup");
28439
28476
  continue;
28440
28477
  }
@@ -28675,7 +28712,7 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
28675
28712
  seriesColors2[ni % seriesColors2.length];
28676
28713
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "er-table").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
28677
28714
  if (activeTagGroup) {
28678
- const tagKey = activeTagGroup.toLowerCase();
28715
+ const tagKey = tagAttrKey(activeTagGroup);
28679
28716
  const tagValue = node.metadata[tagKey];
28680
28717
  if (tagValue) {
28681
28718
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -28854,6 +28891,7 @@ var DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABE
28854
28891
  var init_renderer5 = __esm({
28855
28892
  "src/er/renderer.ts"() {
28856
28893
  "use strict";
28894
+ init_tag_groups();
28857
28895
  init_fonts();
28858
28896
  init_color_utils();
28859
28897
  init_palettes();
@@ -29847,7 +29885,7 @@ function layeredCandidates(parsed, sizes, opts) {
29847
29885
  for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
29848
29886
  return c;
29849
29887
  };
29850
- const median = (vals) => {
29888
+ const median2 = (vals) => {
29851
29889
  if (vals.length === 0) return -1;
29852
29890
  vals.sort((x, y) => x - y);
29853
29891
  const m = Math.floor(vals.length / 2);
@@ -29862,7 +29900,7 @@ function layeredCandidates(parsed, sizes, opts) {
29862
29900
  const neigh = useUp ? up : down;
29863
29901
  const med = /* @__PURE__ */ new Map();
29864
29902
  for (const id of ids)
29865
- med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
29903
+ med.set(id, median2(neigh.get(id).map((x) => orderOf.get(x))));
29866
29904
  const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
29867
29905
  const out = new Array(ids.length).fill(null);
29868
29906
  for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
@@ -30192,6 +30230,695 @@ var init_layout_layered = __esm({
30192
30230
  }
30193
30231
  });
30194
30232
 
30233
+ // src/boxes-and-lines/layout-grouped.ts
30234
+ function rng2(s) {
30235
+ return () => {
30236
+ s |= 0;
30237
+ s = s + 1831565813 | 0;
30238
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
30239
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
30240
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
30241
+ };
30242
+ }
30243
+ function median(vals) {
30244
+ if (vals.length === 0) return -1;
30245
+ vals.sort((x, y) => x - y);
30246
+ const m = Math.floor(vals.length / 2);
30247
+ if (vals.length % 2 === 1) return vals[m];
30248
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
30249
+ const left = vals[m - 1] - vals[0];
30250
+ const right = vals[vals.length - 1] - vals[m];
30251
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
30252
+ }
30253
+ function acyclicOrient(ids, edges) {
30254
+ const adj = /* @__PURE__ */ new Map();
30255
+ for (const l of ids) adj.set(l, []);
30256
+ for (const e of edges) adj.get(e.from)?.push({ to: e.to, idx: e.idx });
30257
+ const reversed = /* @__PURE__ */ new Set();
30258
+ const state = /* @__PURE__ */ new Map();
30259
+ for (const l of ids) state.set(l, 0);
30260
+ const stack = [];
30261
+ const visit = (root) => {
30262
+ stack.push(root);
30263
+ const it = /* @__PURE__ */ new Map();
30264
+ while (stack.length) {
30265
+ const u = stack[stack.length - 1];
30266
+ if (state.get(u) === 0) state.set(u, 1);
30267
+ const list = adj.get(u);
30268
+ let i = it.get(u) ?? 0;
30269
+ let descended = false;
30270
+ for (; i < list.length; i++) {
30271
+ const { to, idx } = list[i];
30272
+ const st = state.get(to);
30273
+ if (st === 1) reversed.add(idx);
30274
+ else if (st === 0) {
30275
+ it.set(u, i + 1);
30276
+ stack.push(to);
30277
+ descended = true;
30278
+ break;
30279
+ }
30280
+ }
30281
+ if (!descended) {
30282
+ it.set(u, list.length);
30283
+ state.set(u, 2);
30284
+ stack.pop();
30285
+ }
30286
+ }
30287
+ };
30288
+ for (const l of ids) if (state.get(l) === 0) visit(l);
30289
+ const dag = edges.map(
30290
+ (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 }
30291
+ );
30292
+ return { dag };
30293
+ }
30294
+ function longestPath(ids, dag) {
30295
+ const out = /* @__PURE__ */ new Map();
30296
+ const inDeg = /* @__PURE__ */ new Map();
30297
+ for (const l of ids) {
30298
+ out.set(l, []);
30299
+ inDeg.set(l, 0);
30300
+ }
30301
+ for (const d of dag) {
30302
+ out.get(d.from).push(d.to);
30303
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
30304
+ }
30305
+ const rank = /* @__PURE__ */ new Map();
30306
+ for (const l of ids) rank.set(l, 0);
30307
+ const queue = ids.filter((l) => (inDeg.get(l) ?? 0) === 0);
30308
+ const indeg2 = new Map(inDeg);
30309
+ let seen = 0;
30310
+ while (queue.length) {
30311
+ const u = queue.shift();
30312
+ seen++;
30313
+ for (const v of out.get(u)) {
30314
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
30315
+ indeg2.set(v, indeg2.get(v) - 1);
30316
+ if (indeg2.get(v) === 0) queue.push(v);
30317
+ }
30318
+ }
30319
+ return seen === ids.length ? rank : null;
30320
+ }
30321
+ function groupedTierCandidates(parsed, sizes, opts) {
30322
+ if (parsed.groups.length === 0) return [];
30323
+ const nCount = parsed.nodes.length;
30324
+ if (nCount < 3 || nCount > 60) return [];
30325
+ const isTB = parsed.direction === "TB";
30326
+ const labels = parsed.nodes.map((n) => n.label);
30327
+ const labelSet = new Set(labels);
30328
+ for (const e of parsed.edges)
30329
+ if (e.source.startsWith("__group_") || e.target.startsWith("__group_"))
30330
+ return [];
30331
+ const groupByLabel = new Map(parsed.groups.map((g) => [g.label, g]));
30332
+ const directGroupOf = /* @__PURE__ */ new Map();
30333
+ for (const grp of parsed.groups)
30334
+ for (const c of grp.children)
30335
+ if (labelSet.has(c)) directGroupOf.set(c, grp.label);
30336
+ const topGroupOf = (groupLabel) => {
30337
+ let cur = groupLabel;
30338
+ const guard = /* @__PURE__ */ new Set();
30339
+ while (true) {
30340
+ const g = groupByLabel.get(cur);
30341
+ if (!g?.parentGroup || guard.has(cur)) return cur;
30342
+ guard.add(cur);
30343
+ cur = g.parentGroup;
30344
+ }
30345
+ };
30346
+ const pathOf = (label) => {
30347
+ const direct = directGroupOf.get(label);
30348
+ if (!direct) return [];
30349
+ const chain = [direct];
30350
+ let cur = direct;
30351
+ const guard = /* @__PURE__ */ new Set([direct]);
30352
+ while (true) {
30353
+ const g = groupByLabel.get(cur);
30354
+ if (!g?.parentGroup || guard.has(g.parentGroup)) break;
30355
+ chain.push(g.parentGroup);
30356
+ guard.add(g.parentGroup);
30357
+ cur = g.parentGroup;
30358
+ }
30359
+ return chain.reverse();
30360
+ };
30361
+ const SINGLE = "\0node\0";
30362
+ const unitOf = (label) => {
30363
+ const direct = directGroupOf.get(label);
30364
+ return direct ? topGroupOf(direct) : SINGLE + label;
30365
+ };
30366
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
30367
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
30368
+ );
30369
+ if (edges.length === 0) return [];
30370
+ const unitOfNode = /* @__PURE__ */ new Map();
30371
+ for (const l of labels) unitOfNode.set(l, unitOf(l));
30372
+ const units = Array.from(new Set(labels.map((l) => unitOfNode.get(l))));
30373
+ const interUnit = edges.filter(({ e }) => unitOfNode.get(e.source) !== unitOfNode.get(e.target)).map(({ e, i }) => ({
30374
+ from: unitOfNode.get(e.source),
30375
+ to: unitOfNode.get(e.target),
30376
+ idx: i
30377
+ }));
30378
+ const { dag: unitDag } = acyclicOrient(units, interUnit);
30379
+ const tier = longestPath(
30380
+ units,
30381
+ unitDag.map((d) => ({ from: d.from, to: d.to }))
30382
+ );
30383
+ if (!tier) return [];
30384
+ const localRank = /* @__PURE__ */ new Map();
30385
+ const unitMembers = /* @__PURE__ */ new Map();
30386
+ for (const l of labels) {
30387
+ const u = unitOfNode.get(l);
30388
+ (unitMembers.get(u) ?? unitMembers.set(u, []).get(u)).push(l);
30389
+ }
30390
+ for (const [u, members] of unitMembers) {
30391
+ const memberSet = new Set(members);
30392
+ const intra = edges.filter(
30393
+ ({ e }) => unitOfNode.get(e.source) === u && unitOfNode.get(e.target) === u && memberSet.has(e.source) && memberSet.has(e.target)
30394
+ ).map(({ e, i }) => ({ from: e.source, to: e.target, idx: i }));
30395
+ const { dag } = acyclicOrient(members, intra);
30396
+ const lr = longestPath(
30397
+ members,
30398
+ dag.map((d) => ({ from: d.from, to: d.to }))
30399
+ );
30400
+ if (!lr) return [];
30401
+ for (const m of members) localRank.set(m, lr.get(m));
30402
+ }
30403
+ const maxTier = Math.max(...units.map((u) => tier.get(u)));
30404
+ const bandHeight = Array.from({ length: maxTier + 1 }, () => 1);
30405
+ for (const u of units) {
30406
+ const h = Math.max(
30407
+ ...unitMembers.get(u).map((m) => localRank.get(m) + 1)
30408
+ );
30409
+ const t = tier.get(u);
30410
+ bandHeight[t] = Math.max(bandHeight[t], h);
30411
+ }
30412
+ const tierBase = [];
30413
+ let accBase = 0;
30414
+ for (let t = 0; t <= maxTier; t++) {
30415
+ tierBase[t] = accBase;
30416
+ accBase += bandHeight[t];
30417
+ }
30418
+ const tierStartRanks = new Set(tierBase.slice(1));
30419
+ const globalRank = /* @__PURE__ */ new Map();
30420
+ for (const l of labels)
30421
+ globalRank.set(
30422
+ l,
30423
+ tierBase[tier.get(unitOfNode.get(l))] + localRank.get(l)
30424
+ );
30425
+ const maxRank = Math.max(...labels.map((l) => globalRank.get(l)));
30426
+ const node = /* @__PURE__ */ new Map();
30427
+ for (const n of parsed.nodes) {
30428
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30429
+ node.set(n.label, {
30430
+ id: n.label,
30431
+ dummy: false,
30432
+ rank: globalRank.get(n.label),
30433
+ cross: 0,
30434
+ thick: isTB ? s.width : s.height,
30435
+ depth: isTB ? s.height : s.width,
30436
+ realW: s.width,
30437
+ realH: s.height,
30438
+ unit: unitOfNode.get(n.label),
30439
+ path: pathOf(n.label)
30440
+ });
30441
+ }
30442
+ const chains = [];
30443
+ const segs = [];
30444
+ const backEdges = [];
30445
+ const flatEdges = [];
30446
+ for (const { e, i } of edges) {
30447
+ const rs = globalRank.get(e.source);
30448
+ const rt = globalRank.get(e.target);
30449
+ if (rs === rt) {
30450
+ flatEdges.push({ edgeIdx: i, a: e.source, b: e.target });
30451
+ continue;
30452
+ }
30453
+ if (rt < rs) {
30454
+ backEdges.push({ edgeIdx: i, src: e.source, tgt: e.target });
30455
+ continue;
30456
+ }
30457
+ const chain = [e.source];
30458
+ let prev = e.source;
30459
+ for (let r = rs + 1; r < rt; r++) {
30460
+ const id = `__d${i}_${r}`;
30461
+ node.set(id, {
30462
+ id,
30463
+ dummy: true,
30464
+ rank: r,
30465
+ cross: 0,
30466
+ thick: DUMMY_THICK2,
30467
+ depth: 0,
30468
+ realW: 0,
30469
+ realH: 0,
30470
+ unit: SINGLE + id,
30471
+ path: []
30472
+ });
30473
+ segs.push({ a: prev, b: id });
30474
+ chain.push(id);
30475
+ prev = id;
30476
+ }
30477
+ segs.push({ a: prev, b: e.target });
30478
+ chain.push(e.target);
30479
+ chains.push({ edgeIdx: i, chain });
30480
+ }
30481
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
30482
+ for (const n of node.values()) ranks[n.rank].push(n.id);
30483
+ const up = /* @__PURE__ */ new Map();
30484
+ const down = /* @__PURE__ */ new Map();
30485
+ for (const id of node.keys()) {
30486
+ up.set(id, []);
30487
+ down.set(id, []);
30488
+ }
30489
+ for (const s of segs) {
30490
+ down.get(s.a).push(s.b);
30491
+ up.get(s.b).push(s.a);
30492
+ }
30493
+ const orderOf = /* @__PURE__ */ new Map();
30494
+ const gapCrossings = (r) => {
30495
+ const lower = ranks[r + 1];
30496
+ if (!lower) return 0;
30497
+ const es = [];
30498
+ for (const a of ranks[r]) {
30499
+ const pa = orderOf.get(a);
30500
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
30501
+ }
30502
+ let c = 0;
30503
+ for (let i = 0; i < es.length; i++)
30504
+ for (let j = i + 1; j < es.length; j++) {
30505
+ const A = es[i], B = es[j];
30506
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
30507
+ }
30508
+ return c;
30509
+ };
30510
+ const totalCrossings = () => {
30511
+ let c = 0;
30512
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
30513
+ return c;
30514
+ };
30515
+ const wmedianRank = (r, useUp) => {
30516
+ const ids = ranks[r];
30517
+ const neigh = useUp ? up : down;
30518
+ const med = /* @__PURE__ */ new Map();
30519
+ for (const id of ids)
30520
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30521
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
30522
+ const out = new Array(ids.length).fill(null);
30523
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
30524
+ let mi = 0;
30525
+ for (let slot = 0; slot < out.length; slot++)
30526
+ if (out[slot] === null) out[slot] = movable[mi++];
30527
+ const final = out;
30528
+ final.forEach((id, i) => orderOf.set(id, i));
30529
+ ranks[r] = final;
30530
+ };
30531
+ const transpose = () => {
30532
+ let improved = true;
30533
+ let guard = 0;
30534
+ while (improved && guard++ < 12) {
30535
+ improved = false;
30536
+ for (let r = 0; r <= maxRank; r++) {
30537
+ const ids = ranks[r];
30538
+ for (let i = 0; i < ids.length - 1; i++) {
30539
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30540
+ const a = ids[i], b = ids[i + 1];
30541
+ ids[i] = b;
30542
+ ids[i + 1] = a;
30543
+ orderOf.set(b, i);
30544
+ orderOf.set(a, i + 1);
30545
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30546
+ if (after < before) improved = true;
30547
+ else {
30548
+ ids[i] = a;
30549
+ ids[i + 1] = b;
30550
+ orderOf.set(a, i);
30551
+ orderOf.set(b, i + 1);
30552
+ }
30553
+ }
30554
+ }
30555
+ }
30556
+ };
30557
+ const initOrder = (seed) => {
30558
+ const r = seed === 0 ? null : rng2(seed);
30559
+ for (let rr = 0; rr <= maxRank; rr++) {
30560
+ const ids = ranks[rr].slice();
30561
+ if (r)
30562
+ for (let i = ids.length - 1; i > 0; i--) {
30563
+ const j = Math.floor(r() * (i + 1));
30564
+ [ids[i], ids[j]] = [ids[j], ids[i]];
30565
+ }
30566
+ ranks[rr] = ids;
30567
+ ids.forEach((id, i) => orderOf.set(id, i));
30568
+ }
30569
+ };
30570
+ const restarts = opts?.restarts ?? (nCount <= 16 ? 20 : nCount <= 30 ? 12 : 6);
30571
+ const keepK = opts?.keepK ?? 3;
30572
+ const found = [];
30573
+ for (let s = 0; s < restarts; s++) {
30574
+ initOrder(s);
30575
+ let best = totalCrossings();
30576
+ let bestSnap = new Map(orderOf);
30577
+ for (let iter = 0; iter < 8; iter++) {
30578
+ if (iter % 2 === 0)
30579
+ for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
30580
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
30581
+ transpose();
30582
+ const c = totalCrossings();
30583
+ if (c < best) {
30584
+ best = c;
30585
+ bestSnap = new Map(orderOf);
30586
+ }
30587
+ if (c === 0) break;
30588
+ }
30589
+ for (const [k, v] of bestSnap) orderOf.set(k, v);
30590
+ for (let r = 0; r <= maxRank; r++)
30591
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
30592
+ const sig = ranks.map((r) => r.join(",")).join("|");
30593
+ if (!found.some((f) => f.sig === sig))
30594
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
30595
+ }
30596
+ found.sort((a, b) => a.cross - b.cross);
30597
+ const keep = found.slice(0, keepK);
30598
+ if (keep.length === 0) return [];
30599
+ const results = [];
30600
+ for (const cand of keep) {
30601
+ for (const [k, v] of cand.order) orderOf.set(k, v);
30602
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
30603
+ for (const n of node.values()) rk[n.rank].push(n.id);
30604
+ for (let r = 0; r <= maxRank; r++)
30605
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
30606
+ results.push(realize(rk));
30607
+ }
30608
+ return results;
30609
+ function weight(a, b) {
30610
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY2;
30611
+ if (a.dummy || b.dummy) return W_REAL_DUMMY2;
30612
+ return W_REAL_REAL2;
30613
+ }
30614
+ function placeWithSeparation(ids, desired, gap) {
30615
+ const n = ids.length;
30616
+ if (n === 0) return;
30617
+ const off = [0];
30618
+ for (let i = 1; i < n; i++) off[i] = off[i - 1] + gap[i];
30619
+ const d = desired.map((v, i) => v - off[i]);
30620
+ const blocks = [];
30621
+ for (let i = 0; i < n; i++) {
30622
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
30623
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
30624
+ const prev = blocks.pop();
30625
+ const count = prev.count + b.count;
30626
+ const sumOffset = prev.sumOffset + b.sumOffset;
30627
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
30628
+ }
30629
+ blocks.push(b);
30630
+ }
30631
+ const xs = new Array(n);
30632
+ for (const b of blocks)
30633
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
30634
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
30635
+ }
30636
+ function gapBetween(a, b) {
30637
+ const base = a.thick / 2 + b.thick / 2 + NODESEP2;
30638
+ if (a.dummy || b.dummy) return base;
30639
+ if (a.unit !== b.unit) return base + 2 * GROUP_PAD + GROUP_GAP;
30640
+ const da = a.path[a.path.length - 1];
30641
+ const db = b.path[b.path.length - 1];
30642
+ if (da !== db && (da || db)) return base + 2 * GROUP_PAD;
30643
+ return base;
30644
+ }
30645
+ function realize(rk) {
30646
+ const bandDepth = rk.map(
30647
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
30648
+ );
30649
+ const bandCenter = [];
30650
+ let acc = MARGIN4;
30651
+ for (let r = 0; r <= maxRank; r++) {
30652
+ if (r > 0) acc += RANKSEP2 + (tierStartRanks.has(r) ? TIER_GAP : 0);
30653
+ bandCenter[r] = acc + bandDepth[r] / 2;
30654
+ acc += bandDepth[r];
30655
+ }
30656
+ const gapsFor = (ids) => ids.map(
30657
+ (id, i) => i === 0 ? 0 : gapBetween(node.get(ids[i - 1]), node.get(id))
30658
+ );
30659
+ for (let r = 0; r <= maxRank; r++) {
30660
+ const ids = rk[r];
30661
+ const gap = gapsFor(ids);
30662
+ let x = MARGIN4;
30663
+ for (let i = 0; i < ids.length; i++) {
30664
+ const n = node.get(ids[i]);
30665
+ x += gap[i];
30666
+ n.cross = i === 0 ? MARGIN4 + n.thick / 2 : x;
30667
+ }
30668
+ }
30669
+ const ITER = 18;
30670
+ for (let it = 0; it < ITER; it++) {
30671
+ const downPass = it % 2 === 0;
30672
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
30673
+ for (const r of order) {
30674
+ const ids = rk[r];
30675
+ const desired = ids.map((id) => {
30676
+ const n = node.get(id);
30677
+ let sw = 0, sx2 = 0;
30678
+ for (const nb of up.get(id)) {
30679
+ const w = weight(n, node.get(nb));
30680
+ sw += w;
30681
+ sx2 += w * node.get(nb).cross;
30682
+ }
30683
+ for (const nb of down.get(id)) {
30684
+ const w = weight(n, node.get(nb));
30685
+ sw += w;
30686
+ sx2 += w * node.get(nb).cross;
30687
+ }
30688
+ return sw > 0 ? sx2 / sw : n.cross;
30689
+ });
30690
+ placeWithSeparation(ids, desired, gapsFor(ids));
30691
+ }
30692
+ }
30693
+ let minC = Infinity;
30694
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
30695
+ const shiftC = MARGIN4 - minC;
30696
+ for (const n of node.values()) n.cross += shiftC;
30697
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
30698
+ const layoutNodes = parsed.nodes.map((pn) => {
30699
+ const n = node.get(pn.label);
30700
+ const c = coord(n);
30701
+ return {
30702
+ label: pn.label,
30703
+ x: c.x,
30704
+ y: c.y,
30705
+ width: n.realW,
30706
+ height: n.realH
30707
+ };
30708
+ });
30709
+ const chainById = /* @__PURE__ */ new Map();
30710
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
30711
+ let minCross = Infinity, maxCross = -Infinity;
30712
+ for (const n of node.values()) {
30713
+ if (n.dummy) continue;
30714
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
30715
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
30716
+ }
30717
+ const GAP = 34;
30718
+ const LANE = 28;
30719
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
30720
+ const sideOf = (b) => {
30721
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
30722
+ return d >= 0 ? 1 : -1;
30723
+ };
30724
+ const backSorted = backEdges.map((b) => ({
30725
+ ...b,
30726
+ side: sideOf(b),
30727
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
30728
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
30729
+ const sideCounts = /* @__PURE__ */ new Map();
30730
+ for (const b of backSorted)
30731
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
30732
+ const laneCounter = /* @__PURE__ */ new Map();
30733
+ const backPoints = /* @__PURE__ */ new Map();
30734
+ for (const b of backSorted) {
30735
+ const s = b.side;
30736
+ const k = laneCounter.get(s) ?? 0;
30737
+ laneCounter.set(s, k + 1);
30738
+ const K = sideCounts.get(s);
30739
+ const src = node.get(b.src);
30740
+ const tgt = node.get(b.tgt);
30741
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
30742
+ const srcCtr = bandCenter[src.rank];
30743
+ const tgtCtr = bandCenter[tgt.rank];
30744
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
30745
+ const srcR = srcCtr - frac * faceLim(src.depth);
30746
+ const tgtR = tgtCtr + frac * faceLim(tgt.depth);
30747
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30748
+ const off = 10;
30749
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
30750
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
30751
+ backPoints.set(b.edgeIdx, [
30752
+ m(src.cross, srcCtr),
30753
+ m(srcEdgeC, srcR),
30754
+ m(laneC, srcR),
30755
+ m(laneC, tgtR),
30756
+ m(tgtEdgeC, tgtR),
30757
+ m(tgt.cross, tgtCtr)
30758
+ ]);
30759
+ }
30760
+ const flatPoints = /* @__PURE__ */ new Map();
30761
+ for (const f of flatEdges) {
30762
+ const a = node.get(f.a);
30763
+ const b = node.get(f.b);
30764
+ const ctr = bandCenter[a.rank];
30765
+ const bow = ctr - (a.depth / 2 + 26);
30766
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30767
+ flatPoints.set(f.edgeIdx, [
30768
+ m(a.cross, ctr),
30769
+ m(a.cross, bow),
30770
+ m(b.cross, bow),
30771
+ m(b.cross, ctr)
30772
+ ]);
30773
+ }
30774
+ const layoutEdges = [];
30775
+ for (let i = 0; i < parsed.edges.length; i++) {
30776
+ const e = parsed.edges[i];
30777
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
30778
+ let points;
30779
+ if (e.source === e.target) {
30780
+ const c = coord(node.get(e.source));
30781
+ points = [c, c];
30782
+ } else if (backPoints.has(i)) points = backPoints.get(i);
30783
+ else if (flatPoints.has(i)) points = flatPoints.get(i);
30784
+ else {
30785
+ const chain = chainById.get(i);
30786
+ if (!chain) continue;
30787
+ points = chain.map((id) => coord(node.get(id)));
30788
+ }
30789
+ layoutEdges.push({
30790
+ source: e.source,
30791
+ target: e.target,
30792
+ ...e.label !== void 0 && { label: e.label },
30793
+ bidirectional: e.bidirectional,
30794
+ lineNumber: e.lineNumber,
30795
+ points,
30796
+ yOffset: 0,
30797
+ parallelCount: 1,
30798
+ metadata: e.metadata
30799
+ });
30800
+ }
30801
+ const nodeBox = new Map(
30802
+ layoutNodes.map((n) => [
30803
+ n.label,
30804
+ {
30805
+ l: n.x - n.width / 2,
30806
+ r: n.x + n.width / 2,
30807
+ t: n.y - n.height / 2,
30808
+ b: n.y + n.height / 2
30809
+ }
30810
+ ])
30811
+ );
30812
+ const depthOf = (label) => {
30813
+ let d = 0;
30814
+ let cur = label;
30815
+ const guard = /* @__PURE__ */ new Set();
30816
+ while (cur && !guard.has(cur)) {
30817
+ guard.add(cur);
30818
+ cur = groupByLabel.get(cur)?.parentGroup;
30819
+ if (cur) d++;
30820
+ }
30821
+ return d;
30822
+ };
30823
+ const childGroups = /* @__PURE__ */ new Map();
30824
+ for (const g of parsed.groups)
30825
+ if (g.parentGroup)
30826
+ (childGroups.get(g.parentGroup) ?? childGroups.set(g.parentGroup, []).get(g.parentGroup)).push(g.label);
30827
+ const rectByGroup = /* @__PURE__ */ new Map();
30828
+ const ordered = [...parsed.groups].sort(
30829
+ (a, b) => depthOf(b.label) - depthOf(a.label)
30830
+ );
30831
+ const layoutGroups = [];
30832
+ for (const grp of ordered) {
30833
+ let l = Infinity, r = -Infinity, t = Infinity, bm = -Infinity;
30834
+ const acc2 = (box) => {
30835
+ l = Math.min(l, box.l);
30836
+ r = Math.max(r, box.r);
30837
+ t = Math.min(t, box.t);
30838
+ bm = Math.max(bm, box.b);
30839
+ };
30840
+ for (const c of grp.children) {
30841
+ const nb = nodeBox.get(c);
30842
+ if (nb) acc2(nb);
30843
+ }
30844
+ for (const sub of childGroups.get(grp.label) ?? []) {
30845
+ const sr = rectByGroup.get(sub);
30846
+ if (sr) acc2(sr);
30847
+ }
30848
+ if (!Number.isFinite(l)) continue;
30849
+ const x0 = l - GROUP_PAD;
30850
+ const x1 = r + GROUP_PAD;
30851
+ const y0 = t - GROUP_LABEL_ZONE2;
30852
+ const y1 = bm + GROUP_PAD;
30853
+ rectByGroup.set(grp.label, { l: x0, r: x1, t: y0, b: y1 });
30854
+ layoutGroups.push({
30855
+ label: grp.label,
30856
+ lineNumber: grp.lineNumber,
30857
+ x: (x0 + x1) / 2,
30858
+ y: (y0 + y1) / 2,
30859
+ width: x1 - x0,
30860
+ height: y1 - y0,
30861
+ collapsed: false,
30862
+ childCount: grp.children.length
30863
+ });
30864
+ }
30865
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
30866
+ const ext = (x0, y0, x1, y1) => {
30867
+ minX = Math.min(minX, x0);
30868
+ minY = Math.min(minY, y0);
30869
+ maxX = Math.max(maxX, x1);
30870
+ maxY = Math.max(maxY, y1);
30871
+ };
30872
+ for (const n of layoutNodes)
30873
+ ext(
30874
+ n.x - n.width / 2,
30875
+ n.y - n.height / 2,
30876
+ n.x + n.width / 2,
30877
+ n.y + n.height / 2
30878
+ );
30879
+ for (const e of layoutEdges)
30880
+ for (const p of e.points) ext(p.x, p.y, p.x, p.y);
30881
+ for (const g of layoutGroups)
30882
+ ext(
30883
+ g.x - g.width / 2,
30884
+ g.y - g.height / 2,
30885
+ g.x + g.width / 2,
30886
+ g.y + g.height / 2
30887
+ );
30888
+ const sx = MARGIN4 - minX;
30889
+ const sy = MARGIN4 - minY;
30890
+ const shift = sx !== 0 || sy !== 0;
30891
+ return {
30892
+ nodes: shift ? layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : layoutNodes,
30893
+ edges: shift ? layoutEdges.map((e) => ({
30894
+ ...e,
30895
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
30896
+ })) : layoutEdges,
30897
+ groups: shift ? layoutGroups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : layoutGroups,
30898
+ width: maxX + sx + MARGIN4,
30899
+ height: maxY + sy + MARGIN4
30900
+ };
30901
+ }
30902
+ }
30903
+ var NODESEP2, RANKSEP2, DUMMY_THICK2, MARGIN4, GROUP_PAD, GROUP_LABEL_ZONE2, GROUP_GAP, TIER_GAP, W_REAL_REAL2, W_REAL_DUMMY2, W_DUMMY_DUMMY2;
30904
+ var init_layout_grouped = __esm({
30905
+ "src/boxes-and-lines/layout-grouped.ts"() {
30906
+ "use strict";
30907
+ init_layout5();
30908
+ NODESEP2 = 50;
30909
+ RANKSEP2 = 60;
30910
+ DUMMY_THICK2 = 10;
30911
+ MARGIN4 = 40;
30912
+ GROUP_PAD = 16;
30913
+ GROUP_LABEL_ZONE2 = 32;
30914
+ GROUP_GAP = 24;
30915
+ TIER_GAP = GROUP_LABEL_ZONE2 + 2 * GROUP_PAD;
30916
+ W_REAL_REAL2 = 1;
30917
+ W_REAL_DUMMY2 = 2;
30918
+ W_DUMMY_DUMMY2 = 8;
30919
+ }
30920
+ });
30921
+
30195
30922
  // src/boxes-and-lines/layout-search.ts
30196
30923
  var layout_search_exports = {};
30197
30924
  __export(layout_search_exports, {
@@ -30209,7 +30936,7 @@ __export(layout_search_exports, {
30209
30936
  });
30210
30937
  import dagre4 from "@dagrejs/dagre";
30211
30938
  import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
30212
- function rng2(s) {
30939
+ function rng3(s) {
30213
30940
  return () => {
30214
30941
  s |= 0;
30215
30942
  s = s + 1831565813 | 0;
@@ -30564,7 +31291,7 @@ function detectGroupOverlaps(layout, opts) {
30564
31291
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30565
31292
  const rend = raw.map((a, i) => {
30566
31293
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30567
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31294
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30568
31295
  });
30569
31296
  const hit = /* @__PURE__ */ new Set();
30570
31297
  for (let i = 0; i < raw.length; i++)
@@ -30591,7 +31318,7 @@ function countGroupOverlaps(layout, opts) {
30591
31318
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30592
31319
  const rend = raw.map((a, i) => {
30593
31320
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30594
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31321
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30595
31322
  });
30596
31323
  let count = 0;
30597
31324
  for (let i = 0; i < raw.length; i++)
@@ -30634,7 +31361,7 @@ function separateGroupBands(layout, parsed) {
30634
31361
  let lo = (axis === "y" ? g.y : g.x) - half2;
30635
31362
  const hi = (axis === "y" ? g.y : g.x) + half2;
30636
31363
  const isParent = parsed.groups.some((c) => c.parentGroup === label);
30637
- if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
31364
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE3;
30638
31365
  return { label, lo, hi, c: (lo + hi) / 2 };
30639
31366
  });
30640
31367
  bands.sort((a, b) => a.c - b.c);
@@ -30705,7 +31432,7 @@ function separateGroupBands(layout, parsed) {
30705
31432
  acc(n.x + n.width / 2, n.y + n.height / 2);
30706
31433
  }
30707
31434
  for (const g of groups) {
30708
- acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
31435
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE3);
30709
31436
  acc(g.x + g.width / 2, g.y + g.height / 2);
30710
31437
  }
30711
31438
  for (const e of edges) for (const p of e.points) acc(p.x, p.y);
@@ -30969,7 +31696,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30969
31696
  }
30970
31697
  ];
30971
31698
  });
30972
- const GROUP_PAD = 16;
31699
+ const GROUP_PAD2 = 16;
30973
31700
  const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
30974
31701
  const groups = [];
30975
31702
  for (const grp of parsed.groups) {
@@ -30983,10 +31710,10 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30983
31710
  gy1 = Math.max(gy1, n2.y + n2.height / 2);
30984
31711
  }
30985
31712
  if (!Number.isFinite(gx0)) continue;
30986
- const x0 = gx0 - GROUP_PAD;
30987
- const x1 = gx1 + GROUP_PAD;
30988
- const y0 = gy0 - GROUP_LABEL_ZONE2;
30989
- const y1 = gy1 + GROUP_PAD;
31713
+ const x0 = gx0 - GROUP_PAD2;
31714
+ const x1 = gx1 + GROUP_PAD2;
31715
+ const y0 = gy0 - GROUP_LABEL_ZONE3;
31716
+ const y1 = gy1 + GROUP_PAD2;
30990
31717
  groups.push({
30991
31718
  label: grp.label,
30992
31719
  lineNumber: grp.lineNumber,
@@ -31043,7 +31770,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31043
31770
  };
31044
31771
  }
31045
31772
  function place(cfg) {
31046
- const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
31773
+ const r = cfg.seed === void 0 ? null : rng3(cfg.seed + 1);
31047
31774
  const ord = (a) => r ? shuffle(a, r) : a.slice();
31048
31775
  const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
31049
31776
  g.setGraph({
@@ -31230,8 +31957,15 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31230
31957
  layered = layeredCandidates(parsed, sizes);
31231
31958
  } catch {
31232
31959
  }
31960
+ if (parsed.groups.length > 0 && collapsedGroupLabels.size === 0) {
31961
+ try {
31962
+ layered = layered.concat(groupedTierCandidates(parsed, sizes));
31963
+ } catch {
31964
+ }
31965
+ }
31233
31966
  const fastKey = /* @__PURE__ */ new Map();
31234
- for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31967
+ for (const lay of pool)
31968
+ fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31235
31969
  pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
31236
31970
  const refineK = Math.min(REFINE_K, pool.length);
31237
31971
  let best = pool[0];
@@ -31296,12 +32030,13 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31296
32030
  }
31297
32031
  return best;
31298
32032
  }
31299
- var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
32033
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE3;
31300
32034
  var init_layout_search = __esm({
31301
32035
  "src/boxes-and-lines/layout-search.ts"() {
31302
32036
  "use strict";
31303
32037
  init_layout5();
31304
32038
  init_layout_layered();
32039
+ init_layout_grouped();
31305
32040
  DEFAULT_LAMBDA = 4;
31306
32041
  ESCALATE_THRESHOLD = 4;
31307
32042
  ESCALATE_MAX_N = 45;
@@ -31310,7 +32045,7 @@ var init_layout_search = __esm({
31310
32045
  splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
31311
32046
  PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31312
32047
  FLAT_CACHE = /* @__PURE__ */ new WeakMap();
31313
- GROUP_LABEL_ZONE2 = 32;
32048
+ GROUP_LABEL_ZONE3 = 32;
31314
32049
  }
31315
32050
  });
31316
32051
 
@@ -31487,8 +32222,8 @@ function attachNotes(layout, parsed, collapsedNotes) {
31487
32222
  nodes: finalNodes,
31488
32223
  edges: finalEdges,
31489
32224
  groups: finalGroups,
31490
- width: bbMaxX + shiftX + MARGIN4,
31491
- height: bbMaxY + shiftY + MARGIN4
32225
+ width: bbMaxX + shiftX + MARGIN5,
32226
+ height: bbMaxY + shiftY + MARGIN5
31492
32227
  };
31493
32228
  }
31494
32229
  function applyParallelEdgeOffsets(layout) {
@@ -31521,13 +32256,13 @@ function applyParallelEdgeOffsets(layout) {
31521
32256
  }))
31522
32257
  };
31523
32258
  }
31524
- var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
32259
+ 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_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
31525
32260
  var init_layout5 = __esm({
31526
32261
  "src/boxes-and-lines/layout.ts"() {
31527
32262
  "use strict";
31528
32263
  init_text_measure();
31529
32264
  init_notes();
31530
- MARGIN4 = 40;
32265
+ MARGIN5 = 40;
31531
32266
  MAX_PARALLEL_EDGES = 5;
31532
32267
  PARALLEL_SPACING = 22;
31533
32268
  PHI = 1.618;
@@ -31698,7 +32433,7 @@ function layoutMindmap(parsed, _palette, options) {
31698
32433
  leafWidth: ctx.structural(LEAF_WIDTH),
31699
32434
  hGap: ctx.aesthetic(H_GAP2),
31700
32435
  vGap: ctx.aesthetic(V_GAP2),
31701
- margin: ctx.aesthetic(MARGIN5),
32436
+ margin: ctx.aesthetic(MARGIN6),
31702
32437
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
31703
32438
  };
31704
32439
  populateDepthCache(roots);
@@ -32076,7 +32811,7 @@ function populateDepthCache(roots) {
32076
32811
  };
32077
32812
  walk(roots, 0);
32078
32813
  }
32079
- var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN5, MULTI_ROOT_GAP, nodeDepthCache;
32814
+ var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN6, MULTI_ROOT_GAP, nodeDepthCache;
32080
32815
  var init_layout6 = __esm({
32081
32816
  "src/mindmap/layout.ts"() {
32082
32817
  "use strict";
@@ -32092,7 +32827,7 @@ var init_layout6 = __esm({
32092
32827
  NODE_V_PAD = 10;
32093
32828
  H_GAP2 = 40;
32094
32829
  V_GAP2 = 12;
32095
- MARGIN5 = 40;
32830
+ MARGIN6 = 40;
32096
32831
  MULTI_ROOT_GAP = 60;
32097
32832
  nodeDepthCache = /* @__PURE__ */ new Map();
32098
32833
  }
@@ -32350,7 +33085,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
32350
33085
  );
32351
33086
  const nodeG = mainG.append("g").attr("class", "mindmap-node").attr("data-line-number", node.lineNumber);
32352
33087
  if (activeTagGroup) {
32353
- const tagKey = activeTagGroup.toLowerCase();
33088
+ const tagKey = tagAttrKey(activeTagGroup);
32354
33089
  const metaValue = node.metadata[tagKey];
32355
33090
  if (metaValue) {
32356
33091
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -32469,6 +33204,7 @@ var DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, D
32469
33204
  var init_renderer7 = __esm({
32470
33205
  "src/mindmap/renderer.ts"() {
32471
33206
  "use strict";
33207
+ init_tag_groups();
32472
33208
  init_fonts();
32473
33209
  init_export_container();
32474
33210
  init_color_utils();
@@ -33679,7 +34415,7 @@ function resolveNodeColor4(el, tagGroups, activeGroupName, ancestors) {
33679
34415
  (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
33680
34416
  );
33681
34417
  if (!group) return void 0;
33682
- const key = group.name.toLowerCase();
34418
+ const key = tagAttrKey(group.name);
33683
34419
  let metaValue = el.metadata[key];
33684
34420
  if (!metaValue && ancestors) {
33685
34421
  for (const ancestor of ancestors) {
@@ -33881,8 +34617,8 @@ function layoutC4Context(parsed, activeTagGroup) {
33881
34617
  }
33882
34618
  }
33883
34619
  if (nodes.length > 0) {
33884
- const shiftX = MARGIN6 - minX;
33885
- const shiftY = MARGIN6 - minY;
34620
+ const shiftX = MARGIN7 - minX;
34621
+ const shiftY = MARGIN7 - minY;
33886
34622
  for (const node of nodes) {
33887
34623
  node.x += shiftX;
33888
34624
  node.y += shiftY;
@@ -33894,12 +34630,12 @@ function layoutC4Context(parsed, activeTagGroup) {
33894
34630
  }
33895
34631
  }
33896
34632
  }
33897
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
33898
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
34633
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN7 * 2 : 0;
34634
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN7 * 2 : 0;
33899
34635
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
33900
34636
  if (legendGroups.length > 0) {
33901
- const legendY = totalHeight + MARGIN6;
33902
- let legendX = MARGIN6;
34637
+ const legendY = totalHeight + MARGIN7;
34638
+ let legendX = MARGIN7;
33903
34639
  for (const lg of legendGroups) {
33904
34640
  lg.x = legendX;
33905
34641
  lg.y = legendY;
@@ -34231,8 +34967,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34231
34967
  if (pt.y > maxY) maxY = pt.y;
34232
34968
  }
34233
34969
  }
34234
- const shiftX = MARGIN6 - minX;
34235
- const shiftY = MARGIN6 - minY;
34970
+ const shiftX = MARGIN7 - minX;
34971
+ const shiftY = MARGIN7 - minY;
34236
34972
  for (const node of nodes) {
34237
34973
  node.x += shiftX;
34238
34974
  node.y += shiftY;
@@ -34249,12 +34985,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34249
34985
  pt.y += shiftY;
34250
34986
  }
34251
34987
  }
34252
- let totalWidth = maxX - minX + MARGIN6 * 2;
34253
- let totalHeight = maxY - minY + MARGIN6 * 2;
34988
+ let totalWidth = maxX - minX + MARGIN7 * 2;
34989
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34254
34990
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34255
34991
  if (legendGroups.length > 0) {
34256
- const legendY = totalHeight + MARGIN6;
34257
- let legendX = MARGIN6;
34992
+ const legendY = totalHeight + MARGIN7;
34993
+ let legendX = MARGIN7;
34258
34994
  for (const lg of legendGroups) {
34259
34995
  lg.x = legendX;
34260
34996
  lg.y = legendY;
@@ -34646,8 +35382,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34646
35382
  if (pt.y > maxY) maxY = pt.y;
34647
35383
  }
34648
35384
  }
34649
- const shiftX = MARGIN6 - minX;
34650
- const shiftY = MARGIN6 - minY;
35385
+ const shiftX = MARGIN7 - minX;
35386
+ const shiftY = MARGIN7 - minY;
34651
35387
  for (const node of nodes) {
34652
35388
  node.x += shiftX;
34653
35389
  node.y += shiftY;
@@ -34664,12 +35400,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34664
35400
  pt.y += shiftY;
34665
35401
  }
34666
35402
  }
34667
- let totalWidth = maxX - minX + MARGIN6 * 2;
34668
- let totalHeight = maxY - minY + MARGIN6 * 2;
35403
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35404
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34669
35405
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34670
35406
  if (legendGroups.length > 0) {
34671
- const legendY = totalHeight + MARGIN6;
34672
- let legendX = MARGIN6;
35407
+ const legendY = totalHeight + MARGIN7;
35408
+ let legendX = MARGIN7;
34673
35409
  for (const lg of legendGroups) {
34674
35410
  lg.x = legendX;
34675
35411
  lg.y = legendY;
@@ -34957,8 +35693,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34957
35693
  if (pt.y > maxY) maxY = pt.y;
34958
35694
  }
34959
35695
  }
34960
- const shiftX = MARGIN6 - minX;
34961
- const shiftY = MARGIN6 - minY;
35696
+ const shiftX = MARGIN7 - minX;
35697
+ const shiftY = MARGIN7 - minY;
34962
35698
  for (const node of nodes) {
34963
35699
  node.x += shiftX;
34964
35700
  node.y += shiftY;
@@ -34973,12 +35709,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34973
35709
  pt.y += shiftY;
34974
35710
  }
34975
35711
  }
34976
- let totalWidth = maxX - minX + MARGIN6 * 2;
34977
- let totalHeight = maxY - minY + MARGIN6 * 2;
35712
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35713
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34978
35714
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34979
35715
  if (legendGroups.length > 0) {
34980
- const legendY = totalHeight + MARGIN6;
34981
- let legendX = MARGIN6;
35716
+ const legendY = totalHeight + MARGIN7;
35717
+ let legendX = MARGIN7;
34982
35718
  for (const lg of legendGroups) {
34983
35719
  lg.x = legendX;
34984
35720
  lg.y = legendY;
@@ -34998,10 +35734,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34998
35734
  height: totalHeight
34999
35735
  };
35000
35736
  }
35001
- var gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
35737
+ var gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN7, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
35002
35738
  var init_layout8 = __esm({
35003
35739
  "src/c4/layout.ts"() {
35004
35740
  "use strict";
35741
+ init_tag_groups();
35005
35742
  init_legend_constants();
35006
35743
  init_text_measure();
35007
35744
  gNode = (g, name) => g.node(name);
@@ -35018,7 +35755,7 @@ var init_layout8 = __esm({
35018
35755
  CARD_H_PAD3 = 20;
35019
35756
  META_LINE_HEIGHT4 = 16;
35020
35757
  META_FONT_SIZE4 = 11;
35021
- MARGIN6 = 40;
35758
+ MARGIN7 = 40;
35022
35759
  BOUNDARY_PAD = 40;
35023
35760
  GROUP_BOUNDARY_PAD = 24;
35024
35761
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -35175,7 +35912,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35175
35912
  for (const node of layout.nodes) {
35176
35913
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "c4-card").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
35177
35914
  if (activeTagGroup) {
35178
- const tagKey = activeTagGroup.toLowerCase();
35915
+ const tagKey = tagAttrKey(activeTagGroup);
35179
35916
  const tagValue = node.metadata[tagKey];
35180
35917
  if (tagValue) {
35181
35918
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35734,7 +36471,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35734
36471
  for (const node of layout.nodes) {
35735
36472
  const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "c4-card").attr("data-line-number", String(node.lineNumber)).attr("data-node-id", node.id);
35736
36473
  if (activeTagGroup) {
35737
- const tagKey = activeTagGroup.toLowerCase();
36474
+ const tagKey = tagAttrKey(activeTagGroup);
35738
36475
  const tagValue = node.metadata[tagKey];
35739
36476
  if (tagValue) {
35740
36477
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35998,6 +36735,7 @@ var DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2
35998
36735
  var init_renderer9 = __esm({
35999
36736
  "src/c4/renderer.ts"() {
36000
36737
  "use strict";
36738
+ init_tag_groups();
36001
36739
  init_fonts();
36002
36740
  init_color_utils();
36003
36741
  init_inline_markdown();
@@ -38061,7 +38799,7 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
38061
38799
  const crossOverlap = isLR ? Math.min(ga.x + ga.width, gb.x + gb.width) - Math.max(ga.x, gb.x) : Math.min(ga.y + ga.height, gb.y + gb.height) - Math.max(ga.y, gb.y);
38062
38800
  if (crossOverlap <= 0) continue;
38063
38801
  anyOverlap = true;
38064
- const shift = primaryOverlap + GROUP_GAP;
38802
+ const shift = primaryOverlap + GROUP_GAP2;
38065
38803
  const aCenter = isLR ? ga.y + ga.height / 2 : ga.x + ga.width / 2;
38066
38804
  const bCenter = isLR ? gb.y + gb.height / 2 : gb.x + gb.width / 2;
38067
38805
  const groupToShift = aCenter <= bCenter ? gb : ga;
@@ -38355,7 +39093,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
38355
39093
  height: totalHeight
38356
39094
  };
38357
39095
  }
38358
- var MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
39096
+ var MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP2;
38359
39097
  var init_layout10 = __esm({
38360
39098
  "src/infra/layout.ts"() {
38361
39099
  "use strict";
@@ -38410,7 +39148,7 @@ var init_layout10 = __esm({
38410
39148
  "retention-hours": "retention",
38411
39149
  partitions: "partitions"
38412
39150
  };
38413
- GROUP_GAP = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
39151
+ GROUP_GAP2 = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
38414
39152
  }
38415
39153
  });
38416
39154
 
@@ -39219,7 +39957,7 @@ function resolveActiveTagStroke(node, activeGroup, tagGroups, palette) {
39219
39957
  (t) => t.name.toLowerCase() === activeGroup.toLowerCase()
39220
39958
  );
39221
39959
  if (!tg) return null;
39222
- const tagKey = (tg.alias ?? tg.name).toLowerCase();
39960
+ const tagKey = tagAttrKey(tg.alias ?? tg.name);
39223
39961
  const tagVal = node.tags[tagKey];
39224
39962
  if (!tagVal) return null;
39225
39963
  const tv = tg.values.find(
@@ -39548,7 +40286,7 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
39548
40286
  groups.push({
39549
40287
  name: tg.name,
39550
40288
  type: "tag",
39551
- tagKey: (tg.alias ?? tg.name).toLowerCase(),
40289
+ tagKey: tagAttrKey(tg.alias ?? tg.name),
39552
40290
  entries,
39553
40291
  width: LEGEND_CAPSULE_PAD * 2 + pillWidth2 + 4 + entriesWidth2,
39554
40292
  minifiedWidth: pillWidth2
@@ -39596,7 +40334,7 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
39596
40334
  });
39597
40335
  legendG.selectAll("[data-legend-group]").classed("infra-legend-group", true);
39598
40336
  for (const group of legendGroups) {
39599
- const groupKey = group.name.toLowerCase();
40337
+ const groupKey = tagAttrKey(group.name);
39600
40338
  for (const entry of group.entries) {
39601
40339
  const entryEl = legendG.select(
39602
40340
  `[data-legend-group="${groupKey}"] [data-legend-entry="${entry.value.toLowerCase()}"]`
@@ -39819,6 +40557,7 @@ var NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE9,
39819
40557
  var init_renderer10 = __esm({
39820
40558
  "src/infra/renderer.ts"() {
39821
40559
  "use strict";
40560
+ init_tag_groups();
39822
40561
  init_fonts();
39823
40562
  init_color_utils();
39824
40563
  init_colors();
@@ -39915,17 +40654,17 @@ function mulberry32(seed) {
39915
40654
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
39916
40655
  };
39917
40656
  }
39918
- function standardNormal(rng3) {
40657
+ function standardNormal(rng4) {
39919
40658
  let u = 0;
39920
40659
  let v = 0;
39921
- while (u === 0) u = rng3();
39922
- while (v === 0) v = rng3();
40660
+ while (u === 0) u = rng4();
40661
+ while (v === 0) v = rng4();
39923
40662
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
39924
40663
  }
39925
- function gamma(shape, rng3) {
40664
+ function gamma(shape, rng4) {
39926
40665
  if (shape < 1) {
39927
- const u = rng3();
39928
- return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
40666
+ const u = rng4();
40667
+ return gamma(shape + 1, rng4) * Math.pow(u, 1 / shape);
39929
40668
  }
39930
40669
  const d = shape - 1 / 3;
39931
40670
  const c = 1 / Math.sqrt(9 * d);
@@ -39933,29 +40672,29 @@ function gamma(shape, rng3) {
39933
40672
  let x;
39934
40673
  let v;
39935
40674
  do {
39936
- x = standardNormal(rng3);
40675
+ x = standardNormal(rng4);
39937
40676
  v = 1 + c * x;
39938
40677
  } while (v <= 0);
39939
40678
  v = v * v * v;
39940
- const u = rng3();
40679
+ const u = rng4();
39941
40680
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
39942
40681
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
39943
40682
  }
39944
40683
  }
39945
- function sampleBeta(alpha, beta, rng3) {
39946
- const x = gamma(alpha, rng3);
39947
- const y = gamma(beta, rng3);
40684
+ function sampleBeta(alpha, beta, rng4) {
40685
+ const x = gamma(alpha, rng4);
40686
+ const y = gamma(beta, rng4);
39948
40687
  return x / (x + y);
39949
40688
  }
39950
- function sampleBetaPert(o, m, p, rng3) {
40689
+ function sampleBetaPert(o, m, p, rng4) {
39951
40690
  if (o === p) return m;
39952
40691
  const range = p - o;
39953
40692
  const alpha = 1 + 4 * (m - o) / range;
39954
40693
  const beta = 1 + 4 * (p - m) / range;
39955
- return o + sampleBeta(alpha, beta, rng3) * range;
40694
+ return o + sampleBeta(alpha, beta, rng4) * range;
39956
40695
  }
39957
40696
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
39958
- const rng3 = mulberry32(opts.seed);
40697
+ const rng4 = mulberry32(opts.seed);
39959
40698
  const expById = /* @__PURE__ */ new Map();
39960
40699
  for (const e of expanded) expById.set(e.id, e);
39961
40700
  const completionTimes = new Array(opts.trials);
@@ -39978,7 +40717,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
39978
40717
  } else if (exp.o === exp.p) {
39979
40718
  duration = exp.m;
39980
40719
  } else {
39981
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
40720
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng4);
39982
40721
  }
39983
40722
  let esLower = 0;
39984
40723
  let efLower = -Infinity;
@@ -44265,7 +45004,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44265
45004
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(containerWidth, idealWidth);
44266
45005
  const sBarH = ctx.structural(BAR_H);
44267
45006
  const sRowGap = ctx.structural(ROW_GAP);
44268
- const sGroupGap = ctx.structural(GROUP_GAP2);
45007
+ const sGroupGap = ctx.structural(GROUP_GAP3);
44269
45008
  const sMilestoneSize = ctx.structural(MILESTONE_SIZE);
44270
45009
  const sBottomMargin = ctx.aesthetic(BOTTOM_MARGIN);
44271
45010
  const sRightMargin = ctx.aesthetic(RIGHT_MARGIN);
@@ -44437,7 +45176,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44437
45176
  (tg) => tg.name.toLowerCase() === currentSwimlaneGroup.toLowerCase()
44438
45177
  );
44439
45178
  if (tagGroup) {
44440
- const tagKey = tagGroup.name.toLowerCase();
45179
+ const tagKey = tagAttrKey(tagGroup.name);
44441
45180
  for (const rt of resolved.tasks) {
44442
45181
  let value = rt.effectiveMetadata[tagKey];
44443
45182
  if (!value && tagGroup.defaultValue) value = tagGroup.defaultValue;
@@ -45178,7 +45917,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45178
45917
  const usedValues = /* @__PURE__ */ new Map();
45179
45918
  if (resolvedTasks) {
45180
45919
  for (const group of visibleGroups) {
45181
- const key = group.name.toLowerCase();
45920
+ const key = tagAttrKey(group.name);
45182
45921
  const used = /* @__PURE__ */ new Set();
45183
45922
  for (const rt of resolvedTasks) {
45184
45923
  const val = rt.effectiveMetadata[key];
@@ -45189,7 +45928,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45189
45928
  }
45190
45929
  const filteredEntries = /* @__PURE__ */ new Map();
45191
45930
  for (const group of visibleGroups) {
45192
- const key = group.name.toLowerCase();
45931
+ const key = tagAttrKey(group.name);
45193
45932
  const used = usedValues.get(key);
45194
45933
  if (used && used.size > 0) {
45195
45934
  filteredEntries.set(
@@ -45210,7 +45949,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45210
45949
  const pillW = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD + iconReserve;
45211
45950
  let groupW = pillW;
45212
45951
  if (isActive) {
45213
- const entries = filteredEntries.get(group.name.toLowerCase()) ?? group.entries;
45952
+ const entries = filteredEntries.get(tagAttrKey(group.name)) ?? group.entries;
45214
45953
  let entriesW = 0;
45215
45954
  for (const entry of entries) {
45216
45955
  entriesW += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + measureLegendText(entry.value, LEGEND_ENTRY_FONT_SIZE) + LEGEND_ENTRY_TRAIL;
@@ -45267,7 +46006,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45267
46006
  },
45268
46007
  ...onControlsToggle !== void 0 && { onControlsToggle },
45269
46008
  onEntryHover: (groupName, entryValue) => {
45270
- const tagKey = groupName.toLowerCase();
46009
+ const tagKey = tagAttrKey(groupName);
45271
46010
  if (entryValue) {
45272
46011
  const ev = entryValue.toLowerCase();
45273
46012
  chartG.selectAll(".gantt-task").each(function() {
@@ -45957,7 +46696,7 @@ function buildTagLaneRowList(resolved, swimlaneGroup, collapsedLanes) {
45957
46696
  (g) => g.name.toLowerCase() === swimlaneGroup.toLowerCase()
45958
46697
  );
45959
46698
  if (!tagGroup) return null;
45960
- const tagKey = tagGroup.name.toLowerCase();
46699
+ const tagKey = tagAttrKey(tagGroup.name);
45961
46700
  const rows = [];
45962
46701
  const buckets = /* @__PURE__ */ new Map();
45963
46702
  const unbucketed = [];
@@ -46141,10 +46880,11 @@ function renderTimeScaleHorizontal(g, scale, _innerWidth, innerHeight, textColor
46141
46880
  g.append("text").attr("class", "gantt-scale-tick").attr("x", tick.pos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", textColor).attr("opacity", opacity).text(tick.label);
46142
46881
  }
46143
46882
  }
46144
- var BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
46883
+ var BAR_H, ROW_GAP, GROUP_GAP3, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
46145
46884
  var init_renderer12 = __esm({
46146
46885
  "src/gantt/renderer.ts"() {
46147
46886
  "use strict";
46887
+ init_tag_groups();
46148
46888
  init_fonts();
46149
46889
  init_palettes();
46150
46890
  init_color_utils();
@@ -46159,7 +46899,7 @@ var init_renderer12 = __esm({
46159
46899
  init_title_constants();
46160
46900
  BAR_H = 22;
46161
46901
  ROW_GAP = 6;
46162
- GROUP_GAP2 = 14;
46902
+ GROUP_GAP3 = 14;
46163
46903
  MILESTONE_SIZE = 10;
46164
46904
  MIN_LEFT_MARGIN = 120;
46165
46905
  BOTTOM_MARGIN = 40;
@@ -48234,7 +48974,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48234
48974
  const panelWidth = 280;
48235
48975
  const textAreaWidth = panelWidth - silhouetteZone - CARD_PADDING_X3;
48236
48976
  const descLineH = 14;
48237
- const descLines = parsed.persona.description ? wrapText2(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
48977
+ const descLines = parsed.persona.description ? wrapNoteBody(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
48238
48978
  const descRowH = descLines.length > 0 ? descLines.length * descLineH + 8 : 0;
48239
48979
  const panelHeight = titleRowH + descRowH;
48240
48980
  const panelX = layout.totalWidth - PADDING2 - panelWidth;
@@ -48259,8 +48999,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48259
48999
  personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", "none").attr("stroke", personaColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48260
49000
  personaG.append("text").attr("x", textX).attr("y", panelY + CARD_PADDING_Y3 + FONT_SIZE_STEP).attr("font-size", FONT_SIZE_STEP).attr("font-weight", "500").attr("fill", onPersonaText).text(parsed.persona.name);
48261
49001
  for (let li = 0; li < descLines.length; li++) {
48262
- const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
48263
- renderInlineText(lineEl, descLines[li], palette, FONT_SIZE_META);
49002
+ const line11 = descLines[li];
49003
+ const indent = line11.kind === "plain" ? 0 : NOTE_BULLET_INDENT;
49004
+ const lineY = panelY + titleRowH + descLineH * (li + 1);
49005
+ if (line11.kind === "bullet-first") {
49006
+ personaG.append("text").attr("x", textX).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText).text("\u2022");
49007
+ }
49008
+ const lineEl = personaG.append("text").attr("x", textX + indent).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
49009
+ renderInlineText(lineEl, line11.text, palette, FONT_SIZE_META);
48264
49010
  }
48265
49011
  if (onNavigateToLine) {
48266
49012
  personaG.style("cursor", "pointer").on("click", () => {
@@ -48299,7 +49045,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48299
49045
  return;
48300
49046
  }
48301
49047
  const isScore = groupName === "Score";
48302
- const attrName = isScore ? "data-score" : `data-tag-${groupName.toLowerCase()}`;
49048
+ const attrName = isScore ? "data-score" : `data-tag-${tagAttrKey(groupName)}`;
48303
49049
  const matches = (el) => el.getAttribute(attrName) === entryValue;
48304
49050
  svg.selectAll(".journey-step").each(function() {
48305
49051
  const hit = matches(this);
@@ -48780,7 +49526,7 @@ function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
48780
49526
  (g) => g.name.toLowerCase() === activeGroup.toLowerCase()
48781
49527
  );
48782
49528
  if (!group) return void 0;
48783
- const tagValue = step.tags[group.name.toLowerCase()];
49529
+ const tagValue = step.tags[tagAttrKey(group.name)];
48784
49530
  if (!tagValue) return void 0;
48785
49531
  const entry = group.entries.find(
48786
49532
  (e) => e.value.toLowerCase() === tagValue.toLowerCase()
@@ -48995,11 +49741,13 @@ var DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_H
48995
49741
  var init_renderer14 = __esm({
48996
49742
  "src/journey-map/renderer.ts"() {
48997
49743
  "use strict";
49744
+ init_tag_groups();
48998
49745
  init_color_utils();
48999
49746
  init_fonts();
49000
49747
  init_parser23();
49001
49748
  init_layout13();
49002
49749
  init_inline_markdown();
49750
+ init_note_box();
49003
49751
  init_legend_integration();
49004
49752
  init_tag_groups();
49005
49753
  init_scaling();
@@ -51834,7 +52582,7 @@ function layoutMap(resolvedIn, data, size, opts) {
51834
52582
  (g) => g.name.toLowerCase() === groupName.toLowerCase()
51835
52583
  );
51836
52584
  if (!group) return null;
51837
- const val = tags[group.name.toLowerCase()] ?? group.defaultValue;
52585
+ const val = tags[tagAttrKey(group.name)] ?? group.defaultValue;
51838
52586
  if (!val) return null;
51839
52587
  const entry = group.entries.find(
51840
52588
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52448,7 +53196,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52448
53196
  if (directHex)
52449
53197
  return { fill: directHex, stroke: mix(directHex, palette.text, 18) };
52450
53198
  for (const group of resolved.tagGroups) {
52451
- const val = p.tags[group.name.toLowerCase()];
53199
+ const val = p.tags[tagAttrKey(group.name)];
52452
53200
  if (!val) continue;
52453
53201
  const entry = group.entries.find(
52454
53202
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52463,7 +53211,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52463
53211
  };
52464
53212
  const lineColor = (tags) => {
52465
53213
  for (const group of resolved.tagGroups) {
52466
- const val = tags[group.name.toLowerCase()];
53214
+ const val = tags[tagAttrKey(group.name)];
52467
53215
  if (!val) continue;
52468
53216
  const entry = group.entries.find(
52469
53217
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -53729,6 +54477,7 @@ var FIT_PAD, PATH_DIGITS, THIN_TOL, ALBERS_SKEW_MAX, RAMP_FLOOR2, R_DEFAULT, R_M
53729
54477
  var init_layout15 = __esm({
53730
54478
  "src/map/layout.ts"() {
53731
54479
  "use strict";
54480
+ init_tag_groups();
53732
54481
  init_color_utils();
53733
54482
  init_geo();
53734
54483
  init_colorize();
@@ -56142,7 +56891,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56142
56891
  }
56143
56892
  return result;
56144
56893
  }
56145
- const groupKey = group.name.toLowerCase();
56894
+ const groupKey = tagAttrKey(group.name);
56146
56895
  const participantMeta = /* @__PURE__ */ new Map();
56147
56896
  for (const p of parsed.participants) {
56148
56897
  participantMeta.set(p.id, { ...p.metadata || {} });
@@ -56178,6 +56927,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56178
56927
  var init_tag_resolution = __esm({
56179
56928
  "src/sequence/tag-resolution.ts"() {
56180
56929
  "use strict";
56930
+ init_tag_groups();
56181
56931
  }
56182
56932
  });
56183
56933
 
@@ -56653,7 +57403,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56653
57403
  }
56654
57404
  }
56655
57405
  const getTagColor = (value) => value ? tagValueToColor.get(value.toLowerCase()) : void 0;
56656
- const tagKey = activeTagGroup?.toLowerCase();
57406
+ const tagKey = activeTagGroup ? tagAttrKey(activeTagGroup) : void 0;
56657
57407
  const hiddenMsgIndices = /* @__PURE__ */ new Set();
56658
57408
  if (collapsedSections && collapsedSections.size > 0) {
56659
57409
  const sectionGroups = groupMessagesBySection(elements, messages);
@@ -57245,11 +57995,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57245
57995
  const boxH = sBoxH + GROUP_PADDING_TOP2 + GROUP_PADDING_BOTTOM2;
57246
57996
  const groupTagValue = tagKey && group.metadata?.[tagKey];
57247
57997
  const groupTagColor = getTagColor(groupTagValue || void 0);
57248
- const fillColor = groupTagColor ? mix(
57249
- groupTagColor,
57250
- themeBaseBg(palette, isDark),
57251
- isDark ? 15 : 20
57252
- ) : isDark ? palette.surface : palette.bg;
57998
+ const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
57253
57999
  const strokeColor = groupTagColor || palette.textMuted;
57254
58000
  const groupG = svg.append("g").attr("class", "group-box-wrapper").attr("data-group-toggle", "").attr("data-group-line", String(group.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("cursor", "pointer");
57255
58001
  groupG.append("title").text("Click to collapse");
@@ -57299,11 +58045,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57299
58045
  );
57300
58046
  participantG.attr("data-group-toggle", "").attr("data-group-line", String(meta.lineNumber)).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "false").attr("cursor", "pointer");
57301
58047
  participantG.append("title").text("Click to expand");
57302
- const pFill = effectiveTagColor ? mix(
57303
- effectiveTagColor,
57304
- themeBaseBg(palette, isDark),
57305
- isDark ? 30 : 40
57306
- ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
58048
+ const pFill = effectiveTagColor ? mix(effectiveTagColor, themeBaseBg(palette, isDark), isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
57307
58049
  const pStroke = effectiveTagColor || palette.border;
57308
58050
  participantG.append("rect").attr("x", -boxW / 2).attr("y", -GROUP_PADDING_TOP2).attr("width", boxW).attr("height", fullH).attr("rx", 6).attr("fill", pFill).attr("stroke", pStroke).attr("stroke-width", 1.5);
57309
58051
  participantG.append("text").attr("x", 0).attr("y", -GROUP_PADDING_TOP2 + fullH / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", ctx.text(13)).attr("font-weight", 500).text(participant.label);
@@ -57799,6 +58541,7 @@ var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, LABEL_FONT_S
57799
58541
  var init_renderer20 = __esm({
57800
58542
  "src/sequence/renderer.ts"() {
57801
58543
  "use strict";
58544
+ init_tag_groups();
57802
58545
  init_color_utils();
57803
58546
  init_inline_markdown();
57804
58547
  init_fonts();
@@ -58953,6 +59696,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
58953
59696
  init_parser3();
58954
59697
 
58955
59698
  // src/timeline/renderer.ts
59699
+ init_tag_groups();
58956
59700
  init_fonts();
58957
59701
  init_time_ticks();
58958
59702
  init_scaling();
@@ -59217,7 +59961,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
59217
59961
  });
59218
59962
  let tagLanes = null;
59219
59963
  if (resolvedSwimlaneTG) {
59220
- const tagKey = resolvedSwimlaneTG.toLowerCase();
59964
+ const tagKey = tagAttrKey(resolvedSwimlaneTG);
59221
59965
  const tagGroup = parsed.timelineTagGroups.find(
59222
59966
  (g) => g.name.toLowerCase() === tagKey
59223
59967
  );
@@ -59523,7 +60267,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59523
60267
  onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
59524
60268
  },
59525
60269
  onEntryHover: (groupName, entryValue) => {
59526
- const tagKey = groupName.toLowerCase();
60270
+ const tagKey = tagAttrKey(groupName);
59527
60271
  if (entryValue) {
59528
60272
  const tagVal = entryValue.toLowerCase();
59529
60273
  fadeToTagValue(mainG, tagKey, tagVal);
@@ -59542,7 +60286,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59542
60286
  }
59543
60287
  },
59544
60288
  onGroupRendered: (groupName, groupEl, isActive) => {
59545
- const groupKey = groupName.toLowerCase();
60289
+ const groupKey = tagAttrKey(groupName);
59546
60290
  groupEl.attr("data-tag-group", groupKey);
59547
60291
  if (isActive && !viewMode) {
59548
60292
  const isSwimActive = currentSwimlaneGroup?.toLowerCase() === groupKey;
@@ -62960,6 +63704,71 @@ function completeMapPlaces(query, gazetteer, opts) {
62960
63704
  });
62961
63705
  return [...cityItems, ...airportItems].slice(0, limit);
62962
63706
  }
63707
+ function searchMapLocations(query, gazetteer, opts) {
63708
+ const q = fold2(query);
63709
+ if (!q) return [];
63710
+ const limit = opts?.limit ?? 20;
63711
+ const rankOf = (hay) => hay === q ? 0 : hay.startsWith(q) ? 1 : hay.includes(q) ? 2 : -1;
63712
+ const cityRank = /* @__PURE__ */ new Map();
63713
+ const consider = (idx, rank) => {
63714
+ if (rank < 0) return;
63715
+ const prev = cityRank.get(idx);
63716
+ if (prev === void 0 || rank < prev) cityRank.set(idx, rank);
63717
+ };
63718
+ for (const [key, list] of Object.entries(gazetteer.byName)) {
63719
+ const r = rankOf(key);
63720
+ if (r >= 0) for (const i of list) consider(i, r);
63721
+ }
63722
+ for (const [key, idx] of Object.entries(gazetteer.alt)) {
63723
+ consider(idx, rankOf(key));
63724
+ }
63725
+ const cityItems = [...cityRank].filter(([i]) => gazetteer.cities[i] !== void 0).map(([i, rank]) => {
63726
+ const c = gazetteer.cities[i];
63727
+ const [, , iso, pop, name, sub] = c;
63728
+ const ambiguous = (gazetteer.byName[fold2(name)]?.length ?? 0) > 1;
63729
+ const qualifier = sub ?? iso;
63730
+ return {
63731
+ name,
63732
+ token: ambiguous ? `${name} ${qualifier}` : name,
63733
+ kind: "city",
63734
+ iso,
63735
+ ...sub !== void 0 && { sub },
63736
+ pop,
63737
+ detail: `${qualifier} \xB7 ${groupThousands(pop)}`,
63738
+ _rank: rank
63739
+ };
63740
+ });
63741
+ const airports = opts?.airports;
63742
+ const airportItems = [];
63743
+ if (airports?.airportIata) {
63744
+ for (const [code, idx] of Object.entries(airports.airportIata)) {
63745
+ const a = airports.airports[idx];
63746
+ if (!a) continue;
63747
+ const codeRank = rankOf(code);
63748
+ const nameRank = rankOf(fold2(a[4]));
63749
+ const rank = Math.min(
63750
+ codeRank < 0 ? 99 : codeRank,
63751
+ nameRank < 0 ? 99 : nameRank
63752
+ );
63753
+ if (rank > 2) continue;
63754
+ airportItems.push({
63755
+ name: a[4],
63756
+ token: code.toUpperCase(),
63757
+ kind: "airport",
63758
+ iso: a[2],
63759
+ pop: 0,
63760
+ detail: `Airport \xB7 ${a[4]}`,
63761
+ _rank: rank
63762
+ });
63763
+ }
63764
+ }
63765
+ return [...cityItems, ...airportItems].sort(
63766
+ (x, y) => x._rank - y._rank || // best match tier first
63767
+ (x.kind === y.kind ? 0 : x.kind === "city" ? -1 : 1) || // city before airport
63768
+ y.pop - x.pop || // populous city first
63769
+ (x.token < y.token ? -1 : x.token > y.token ? 1 : 0)
63770
+ ).slice(0, limit).map(({ _rank, ...rest }) => rest);
63771
+ }
62963
63772
  function completeMapRegions(query, regions, opts) {
62964
63773
  const q = fold2(query);
62965
63774
  if (!q) return [];
@@ -63053,6 +63862,7 @@ export {
63053
63862
  palettes,
63054
63863
  render2 as render,
63055
63864
  resolvePaletteOrFallback,
63865
+ searchMapLocations,
63056
63866
  themes,
63057
63867
  parseDgmo as validate
63058
63868
  };