@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/advanced.cjs CHANGED
@@ -1255,6 +1255,9 @@ var init_parsing = __esm({
1255
1255
  });
1256
1256
 
1257
1257
  // src/utils/tag-groups.ts
1258
+ function tagAttrKey(name) {
1259
+ return name.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
1260
+ }
1258
1261
  function stripDefaultModifier(text) {
1259
1262
  if (/\bdefault\s*$/.test(text)) {
1260
1263
  return { text: text.replace(/\s+default\s*$/, "").trim(), isDefault: true };
@@ -1400,17 +1403,17 @@ function parseTagDeclaration(line11) {
1400
1403
  function resolveTagColor(metadata, tagGroups, activeGroupName, isContainer) {
1401
1404
  if (!activeGroupName) return void 0;
1402
1405
  const group = tagGroups.find(
1403
- (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
1406
+ (g) => tagAttrKey(g.name) === tagAttrKey(activeGroupName)
1404
1407
  );
1405
1408
  if (!group) return void 0;
1406
- const metaValue = metadata[group.name.toLowerCase()] ?? (isContainer ? void 0 : group.defaultValue);
1409
+ const metaValue = metadata[tagAttrKey(group.name)] ?? (isContainer ? void 0 : group.defaultValue);
1407
1410
  if (!metaValue) return "#999999";
1408
1411
  return group.entries.find((e) => e.value.toLowerCase() === metaValue.toLowerCase())?.color ?? "#999999";
1409
1412
  }
1410
1413
  function validateTagValues(entities, tagGroups, pushWarning, suggestFn) {
1411
1414
  if (tagGroups.length === 0) return;
1412
1415
  const groupMap = /* @__PURE__ */ new Map();
1413
- for (const g of tagGroups) groupMap.set(g.name.toLowerCase(), g);
1416
+ for (const g of tagGroups) groupMap.set(tagAttrKey(g.name), g);
1414
1417
  for (const entity of entities) {
1415
1418
  for (const [key, value] of Object.entries(entity.metadata)) {
1416
1419
  const group = groupMap.get(key);
@@ -1447,10 +1450,10 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1447
1450
  `'none' is a reserved keyword and cannot be used as a tag group name`
1448
1451
  );
1449
1452
  }
1450
- if (!VALID_TAG_IDENT_RE.test(group.name)) {
1453
+ if (!VALID_TAG_IDENT_RE.test(tagAttrKey(group.name))) {
1451
1454
  report(
1452
1455
  group.lineNumber,
1453
- `Tag group name "${group.name}" contains invalid characters \u2014 use a single identifier (letters, digits, underscore, hyphen)`
1456
+ `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)`
1454
1457
  );
1455
1458
  }
1456
1459
  if (group.alias != null && !VALID_TAG_IDENT_RE.test(group.alias)) {
@@ -1462,7 +1465,7 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1462
1465
  }
1463
1466
  }
1464
1467
  function cascadeTagMetadata(roots, tagGroups) {
1465
- const keys = tagGroups.map((g) => g.name.toLowerCase());
1468
+ const keys = tagGroups.map((g) => tagAttrKey(g.name));
1466
1469
  if (keys.length === 0) return;
1467
1470
  const walk = (node, inherited) => {
1468
1471
  const childInherited = { ...inherited };
@@ -1483,7 +1486,7 @@ function injectDefaultTagMetadata(entities, tagGroups, skip) {
1483
1486
  for (const group of tagGroups) {
1484
1487
  if (group.defaultValue) {
1485
1488
  defaults.push({
1486
- key: group.name.toLowerCase(),
1489
+ key: tagAttrKey(group.name),
1487
1490
  value: group.defaultValue
1488
1491
  });
1489
1492
  }
@@ -2874,7 +2877,7 @@ function parseVisualizationFull(content, palette) {
2874
2877
  );
2875
2878
  for (const group of result.timelineTagGroups) {
2876
2879
  if (!group.defaultValue) continue;
2877
- const key = group.name.toLowerCase();
2880
+ const key = tagAttrKey(group.name);
2878
2881
  for (const event of result.timelineEvents) {
2879
2882
  if (!event.metadata[key]) {
2880
2883
  event.metadata[key] = group.defaultValue;
@@ -5270,7 +5273,7 @@ function renderLegendD3(container, config, state, palette, isDark, callbacks, co
5270
5273
  };
5271
5274
  }
5272
5275
  function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, callbacks) {
5273
- const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", capsule.groupName.toLowerCase()).style("cursor", "pointer");
5276
+ const g = parent.append("g").attr("transform", `translate(${capsule.x},${capsule.y})`).attr("data-legend-group", tagAttrKey(capsule.groupName)).style("cursor", "pointer");
5274
5277
  g.append("rect").attr("width", capsule.width).attr("height", capsule.height).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
5275
5278
  const pill = capsule.pill;
5276
5279
  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);
@@ -5278,7 +5281,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5278
5281
  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);
5279
5282
  if (capsule.gradient) {
5280
5283
  const gr = capsule.gradient;
5281
- const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
5284
+ const gradId = `dgmo-legend-ramp-${tagAttrKey(capsule.groupName).replace(/[^a-z0-9]+/g, "-")}`;
5282
5285
  const def = g.append("defs").append("linearGradient").attr("id", gradId);
5283
5286
  for (const stop of valueRampStops(gr.low, gr.high, { isDark })) {
5284
5287
  def.append("stop").attr("offset", `${stop.offset * 100}%`).attr("stop-color", stop.color);
@@ -5308,7 +5311,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, isDark, ca
5308
5311
  }
5309
5312
  }
5310
5313
  function renderPill(parent, pill, palette, groupBg, callbacks) {
5311
- const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", pill.groupName.toLowerCase()).style("cursor", "pointer");
5314
+ const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", tagAttrKey(pill.groupName)).style("cursor", "pointer");
5312
5315
  g.append("rect").attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", groupBg);
5313
5316
  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);
5314
5317
  if (callbacks?.onGroupToggle) {
@@ -5414,6 +5417,7 @@ var LEGEND_TEXT_DY;
5414
5417
  var init_legend_d3 = __esm({
5415
5418
  "src/utils/legend-d3.ts"() {
5416
5419
  "use strict";
5420
+ init_tag_groups();
5417
5421
  init_legend_constants();
5418
5422
  init_legend_layout();
5419
5423
  init_color_utils();
@@ -9335,7 +9339,7 @@ function parseERDiagram(content, palette) {
9335
9339
  );
9336
9340
  for (const group of result.tagGroups) {
9337
9341
  if (!group.defaultValue) continue;
9338
- const key = group.name.toLowerCase();
9342
+ const key = tagAttrKey(group.name);
9339
9343
  for (const table of result.tables) {
9340
9344
  if (!table.metadata[key]) {
9341
9345
  const mutableMeta = tableMetadataMap.get(table.id);
@@ -9565,12 +9569,12 @@ function parseOrg(content, palette) {
9565
9569
  if (tagBlockMatch.alias) {
9566
9570
  metaAliasMap.set(
9567
9571
  normalizeName(tagBlockMatch.alias),
9568
- tagBlockMatch.name.toLowerCase()
9572
+ tagAttrKey(tagBlockMatch.name)
9569
9573
  );
9570
9574
  }
9571
9575
  metaAliasMap.set(
9572
9576
  normalizeName(tagBlockMatch.name),
9573
- tagBlockMatch.name.toLowerCase()
9577
+ tagAttrKey(tagBlockMatch.name)
9574
9578
  );
9575
9579
  result.tagGroups.push(currentTagGroup);
9576
9580
  continue;
@@ -9956,12 +9960,12 @@ function parseSitemap(content, palette) {
9956
9960
  if (tagBlockMatch.alias) {
9957
9961
  metaAliasMap.set(
9958
9962
  normalizeName(tagBlockMatch.alias),
9959
- tagBlockMatch.name.toLowerCase()
9963
+ tagAttrKey(tagBlockMatch.name)
9960
9964
  );
9961
9965
  }
9962
9966
  metaAliasMap.set(
9963
9967
  normalizeName(tagBlockMatch.name),
9964
- tagBlockMatch.name.toLowerCase()
9968
+ tagAttrKey(tagBlockMatch.name)
9965
9969
  );
9966
9970
  result.tagGroups.push(newTagGroup);
9967
9971
  continue;
@@ -10131,6 +10135,14 @@ function parseSitemap(content, palette) {
10131
10135
  continue;
10132
10136
  }
10133
10137
  }
10138
+ const enclosingContainer = nestedPageInsideContainer(indent, indentStack);
10139
+ if (enclosingContainer) {
10140
+ pushError(
10141
+ lineNumber,
10142
+ `Pages inside container '[${enclosingContainer}]' cannot have indented sub-pages. Containers hold a flat list of pages.`
10143
+ );
10144
+ continue;
10145
+ }
10134
10146
  const node = parseNodeLabel2(
10135
10147
  trimmed,
10136
10148
  lineNumber,
@@ -10278,6 +10290,16 @@ function attachNode2(node, indent, indentStack, result) {
10278
10290
  }
10279
10291
  indentStack.push({ node, indent });
10280
10292
  }
10293
+ function nestedPageInsideContainer(indent, indentStack) {
10294
+ let idx = indentStack.length - 1;
10295
+ while (idx >= 0 && indentStack[idx].indent >= indent) idx--;
10296
+ if (idx < 0) return null;
10297
+ if (indentStack[idx].node.isContainer) return null;
10298
+ for (let k = idx - 1; k >= 0; k--) {
10299
+ if (indentStack[k].node.isContainer) return indentStack[k].node.label;
10300
+ }
10301
+ return null;
10302
+ }
10281
10303
  function findParentNode(indent, indentStack) {
10282
10304
  for (let i = indentStack.length - 1; i >= 0; i--) {
10283
10305
  if (indentStack[i].indent < indent) {
@@ -10861,12 +10883,12 @@ function parsePert(content, parseOpts = {}) {
10861
10883
  if (tagBlockMatch.alias) {
10862
10884
  metaAliasMap.set(
10863
10885
  tagBlockMatch.alias.toLowerCase(),
10864
- tagBlockMatch.name.toLowerCase()
10886
+ tagAttrKey(tagBlockMatch.name)
10865
10887
  );
10866
10888
  }
10867
10889
  metaAliasMap.set(
10868
10890
  normalizeName(tagBlockMatch.name),
10869
- tagBlockMatch.name.toLowerCase()
10891
+ tagAttrKey(tagBlockMatch.name)
10870
10892
  );
10871
10893
  tagGroups.push(currentTagGroup);
10872
10894
  if (tagBlockMatch.inlineValues) {
@@ -12331,10 +12353,10 @@ function emitCapsule(capsule, palette, groupBg, pillBorder) {
12331
12353
  `<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>`
12332
12354
  );
12333
12355
  }
12334
- return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(capsule.groupName.toLowerCase())}" style="cursor:pointer">${inner.join("")}</g>`;
12356
+ return `<g transform="translate(${capsule.x},${capsule.y})" data-legend-group="${esc(tagAttrKey(capsule.groupName))}" style="cursor:pointer">${inner.join("")}</g>`;
12335
12357
  }
12336
12358
  function emitPill(pill, palette, groupBg) {
12337
- 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>`;
12359
+ 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>`;
12338
12360
  }
12339
12361
  function renderLegendSvg(groups, options) {
12340
12362
  if (groups.length === 0) return { svg: "", height: 0, width: 0 };
@@ -12395,6 +12417,7 @@ function renderLegendSvgFromConfig(config, state, palette, containerWidth) {
12395
12417
  var init_legend_svg = __esm({
12396
12418
  "src/utils/legend-svg.ts"() {
12397
12419
  "use strict";
12420
+ init_tag_groups();
12398
12421
  init_legend_constants();
12399
12422
  init_legend_layout();
12400
12423
  init_color_utils();
@@ -15085,12 +15108,12 @@ function parseKanban(content, palette) {
15085
15108
  if (tagBlockMatch.alias) {
15086
15109
  metaAliasMap.set(
15087
15110
  normalizeName(tagBlockMatch.alias),
15088
- tagBlockMatch.name.toLowerCase()
15111
+ tagAttrKey(tagBlockMatch.name)
15089
15112
  );
15090
15113
  }
15091
15114
  metaAliasMap.set(
15092
15115
  normalizeName(tagBlockMatch.name),
15093
- tagBlockMatch.name.toLowerCase()
15116
+ tagAttrKey(tagBlockMatch.name)
15094
15117
  );
15095
15118
  result.tagGroups.push(currentTagGroup);
15096
15119
  continue;
@@ -15279,7 +15302,7 @@ function parseKanban(content, palette) {
15279
15302
  }
15280
15303
  for (const group of result.tagGroups) {
15281
15304
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
15282
- tagValueSets.set(group.name.toLowerCase(), values);
15305
+ tagValueSets.set(tagAttrKey(group.name), values);
15283
15306
  }
15284
15307
  for (const col of result.columns) {
15285
15308
  if (col.wipLimit != null && col.cards.length > col.wipLimit) {
@@ -15541,12 +15564,12 @@ function parseC4(content, palette) {
15541
15564
  if (tagBlockMatch.alias) {
15542
15565
  metaAliasMap.set(
15543
15566
  normalizeName(tagBlockMatch.alias),
15544
- tagBlockMatch.name.toLowerCase()
15567
+ tagAttrKey(tagBlockMatch.name)
15545
15568
  );
15546
15569
  }
15547
15570
  metaAliasMap.set(
15548
15571
  normalizeName(tagBlockMatch.name),
15549
- tagBlockMatch.name.toLowerCase()
15572
+ tagAttrKey(tagBlockMatch.name)
15550
15573
  );
15551
15574
  result.tagGroups.push(currentTagGroup);
15552
15575
  continue;
@@ -17229,7 +17252,7 @@ function parseInfra(content) {
17229
17252
  }
17230
17253
  for (const tg of result.tagGroups) {
17231
17254
  if (!tg.defaultValue) continue;
17232
- const key = (tg.alias ?? tg.name).toLowerCase();
17255
+ const key = tagAttrKey(tg.alias ?? tg.name);
17233
17256
  for (const node of result.nodes) {
17234
17257
  if (node.isEdge) continue;
17235
17258
  if (!(key in node.tags)) {
@@ -17366,6 +17389,7 @@ var CONNECTION_RE, SIMPLE_CONNECTION_RE, ASYNC_CONNECTION_RE, ASYNC_SIMPLE_CONNE
17366
17389
  var init_parser15 = __esm({
17367
17390
  "src/infra/parser.ts"() {
17368
17391
  "use strict";
17392
+ init_tag_groups();
17369
17393
  init_diagnostics();
17370
17394
  init_description_helpers();
17371
17395
  init_arrows();
@@ -17911,13 +17935,10 @@ function parseGantt(content, palette) {
17911
17935
  if (tagMatch.alias) {
17912
17936
  metaAliasMap.set(
17913
17937
  normalizeName(tagMatch.alias),
17914
- tagMatch.name.toLowerCase()
17938
+ tagAttrKey(tagMatch.name)
17915
17939
  );
17916
17940
  }
17917
- metaAliasMap.set(
17918
- normalizeName(tagMatch.name),
17919
- tagMatch.name.toLowerCase()
17920
- );
17941
+ metaAliasMap.set(normalizeName(tagMatch.name), tagAttrKey(tagMatch.name));
17921
17942
  continue;
17922
17943
  }
17923
17944
  const topWorkweekMatch = line11.match(WORKWEEK_RE);
@@ -18843,7 +18864,7 @@ function parseMap(content, palette) {
18843
18864
  const aliasMap = /* @__PURE__ */ new Map();
18844
18865
  const open = { tag: null, route: null, poi: null };
18845
18866
  const registry = () => withTagAliases(MAP_REGISTRY, new Set(aliasMap.keys()));
18846
- const tagGroupNames = () => new Set(tagGroups.map((g) => g.name.toLowerCase()));
18867
+ const tagGroupNames = () => new Set(tagGroups.map((g) => tagAttrKey(g.name)));
18847
18868
  const closeBlocks = () => {
18848
18869
  open.tag = null;
18849
18870
  open.route = null;
@@ -19027,7 +19048,7 @@ function parseMap(content, palette) {
19027
19048
  entries: [],
19028
19049
  lineNumber: line11
19029
19050
  };
19030
- if (m.alias) aliasMap.set(m.alias.toLowerCase(), m.name.toLowerCase());
19051
+ if (m.alias) aliasMap.set(m.alias.toLowerCase(), tagAttrKey(m.name));
19031
19052
  tagGroups.push(group);
19032
19053
  open.tag = group;
19033
19054
  if (m.inlineValues?.length) {
@@ -19560,12 +19581,12 @@ function parseBoxesAndLines(content, palette) {
19560
19581
  if (tagBlockMatch.alias) {
19561
19582
  metaAliasMap.set(
19562
19583
  normalizeName(tagBlockMatch.alias),
19563
- tagBlockMatch.name.toLowerCase()
19584
+ tagAttrKey(tagBlockMatch.name)
19564
19585
  );
19565
19586
  }
19566
19587
  metaAliasMap.set(
19567
19588
  normalizeName(tagBlockMatch.name),
19568
- tagBlockMatch.name.toLowerCase()
19589
+ tagAttrKey(tagBlockMatch.name)
19569
19590
  );
19570
19591
  if (tagBlockMatch.inlineValues) {
19571
19592
  for (const rawVal of tagBlockMatch.inlineValues) {
@@ -20306,12 +20327,12 @@ function parseMindmap(content, palette) {
20306
20327
  if (tagBlockMatch.alias) {
20307
20328
  aliasMap.set(
20308
20329
  tagBlockMatch.alias.toLowerCase(),
20309
- tagBlockMatch.name.toLowerCase()
20330
+ tagAttrKey(tagBlockMatch.name)
20310
20331
  );
20311
20332
  }
20312
20333
  aliasMap.set(
20313
- tagBlockMatch.name.toLowerCase(),
20314
- tagBlockMatch.name.toLowerCase()
20334
+ tagAttrKey(tagBlockMatch.name),
20335
+ tagAttrKey(tagBlockMatch.name)
20315
20336
  );
20316
20337
  result.tagGroups.push(currentTagGroup);
20317
20338
  continue;
@@ -22020,7 +22041,18 @@ function parseJourneyMap(content, palette) {
22020
22041
  palette
22021
22042
  ) ?? void 0;
22022
22043
  } else {
22023
- personaName = afterKeyword;
22044
+ const { label, colorName } = peelTrailingColorName(afterKeyword);
22045
+ if (colorName) {
22046
+ personaName = label;
22047
+ personaColor = resolveColorWithDiagnostic(
22048
+ colorName,
22049
+ lineNumber,
22050
+ result.diagnostics,
22051
+ palette
22052
+ ) ?? void 0;
22053
+ } else {
22054
+ personaName = afterKeyword;
22055
+ }
22024
22056
  }
22025
22057
  }
22026
22058
  if (!personaName) {
@@ -22063,12 +22095,12 @@ function parseJourneyMap(content, palette) {
22063
22095
  if (tagBlockMatch.alias) {
22064
22096
  aliasMap.set(
22065
22097
  tagBlockMatch.alias.toLowerCase(),
22066
- tagBlockMatch.name.toLowerCase()
22098
+ tagAttrKey(tagBlockMatch.name)
22067
22099
  );
22068
22100
  }
22069
22101
  aliasMap.set(
22070
- tagBlockMatch.name.toLowerCase(),
22071
- tagBlockMatch.name.toLowerCase()
22102
+ tagAttrKey(tagBlockMatch.name),
22103
+ tagAttrKey(tagBlockMatch.name)
22072
22104
  );
22073
22105
  result.tagGroups.push(currentTagGroup);
22074
22106
  continue;
@@ -22212,7 +22244,7 @@ function parseJourneyMap(content, palette) {
22212
22244
  }
22213
22245
  for (const group of result.tagGroups) {
22214
22246
  const values = new Set(group.entries.map((e) => e.value.toLowerCase()));
22215
- tagValueSets.set(group.name.toLowerCase(), values);
22247
+ tagValueSets.set(tagAttrKey(group.name), values);
22216
22248
  }
22217
22249
  const allSteps = hasPhases ? result.phases.flatMap((p) => p.steps) : result.steps;
22218
22250
  for (const step of allSteps) {
@@ -24198,7 +24230,7 @@ function computeLegendGroups(tagGroups, showEyeIcons, usedValuesByGroup) {
24198
24230
  const groups = [];
24199
24231
  for (const group of tagGroups) {
24200
24232
  if (group.entries.length === 0) continue;
24201
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
24233
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
24202
24234
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
24203
24235
  if (visibleEntries.length === 0) continue;
24204
24236
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -24811,7 +24843,7 @@ function layoutOrg(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, expan
24811
24843
  const totalHeight = finalMaxY - minY + MARGIN * 2;
24812
24844
  const usedValuesByGroup = /* @__PURE__ */ new Map();
24813
24845
  for (const group of parsed.tagGroups) {
24814
- const key = group.name.toLowerCase();
24846
+ const key = tagAttrKey(group.name);
24815
24847
  const used = /* @__PURE__ */ new Set();
24816
24848
  const walk = (node) => {
24817
24849
  if (!node.isContainer && node.metadata[key]) {
@@ -24869,6 +24901,7 @@ var import_d3_hierarchy, LABEL_FONT_SIZE, META_FONT_SIZE, META_LINE_HEIGHT, HEAD
24869
24901
  var init_layout = __esm({
24870
24902
  "src/org/layout.ts"() {
24871
24903
  "use strict";
24904
+ init_tag_groups();
24872
24905
  import_d3_hierarchy = require("d3-hierarchy");
24873
24906
  init_tag_groups();
24874
24907
  init_legend_constants();
@@ -25230,14 +25263,14 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25230
25263
  const contentG = mainG.append("g").attr("transform", `translate(0, ${contentYShift})`);
25231
25264
  const displayNames = /* @__PURE__ */ new Map();
25232
25265
  for (const group of parsed.tagGroups) {
25233
- displayNames.set(group.name.toLowerCase(), group.name);
25266
+ displayNames.set(tagAttrKey(group.name), group.name);
25234
25267
  }
25235
25268
  const rootNodeIds = new Set(parsed.roots.map((r) => r.id));
25236
25269
  const colorOff = parsed.options?.["color"] === "off";
25237
25270
  for (const c of layout.containers) {
25238
25271
  const cG = contentG.append("g").attr("transform", `translate(${c.x}, ${c.y})`).attr("class", "org-container").attr("data-line-number", String(c.lineNumber));
25239
25272
  if (activeTagGroup) {
25240
- const tagKey = activeTagGroup.toLowerCase();
25273
+ const tagKey = tagAttrKey(activeTagGroup);
25241
25274
  const metaValue = c.tagMetadata[tagKey];
25242
25275
  if (metaValue) {
25243
25276
  cG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25319,7 +25352,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25319
25352
  if (containerNodeIds.has(node.id)) continue;
25320
25353
  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));
25321
25354
  if (activeTagGroup) {
25322
- const tagKey = activeTagGroup.toLowerCase();
25355
+ const tagKey = tagAttrKey(activeTagGroup);
25323
25356
  const metaValue = node.tagMetadata[tagKey];
25324
25357
  if (metaValue) {
25325
25358
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -25492,7 +25525,7 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25492
25525
  const computedLayout = legendHandle.getLayout();
25493
25526
  if (computedLayout.activeCapsule?.addonX != null) {
25494
25527
  const capsule = computedLayout.activeCapsule;
25495
- const groupKey2 = capsule.groupName.toLowerCase();
25528
+ const groupKey2 = tagAttrKey(capsule.groupName);
25496
25529
  const isHidden = hiddenAttributes?.has(groupKey2) ?? false;
25497
25530
  const activeGroupEl = legendParentBase.select(
25498
25531
  `[data-legend-group="${groupKey2}"]`
@@ -25531,6 +25564,7 @@ var d3Selection6, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCE
25531
25564
  var init_renderer = __esm({
25532
25565
  "src/org/renderer.ts"() {
25533
25566
  "use strict";
25567
+ init_tag_groups();
25534
25568
  d3Selection6 = __toESM(require("d3-selection"), 1);
25535
25569
  init_fonts();
25536
25570
  init_export_container();
@@ -25616,7 +25650,7 @@ function computeLegendGroups2(tagGroups, usedValuesByGroup) {
25616
25650
  const groups = [];
25617
25651
  for (const group of tagGroups) {
25618
25652
  if (group.entries.length === 0) continue;
25619
- const usedValues = usedValuesByGroup?.get(group.name.toLowerCase());
25653
+ const usedValues = usedValuesByGroup?.get(tagAttrKey(group.name));
25620
25654
  const visibleEntries = usedValues ? group.entries.filter((e) => usedValues.has(e.value.toLowerCase())) : group.entries;
25621
25655
  if (visibleEntries.length === 0) continue;
25622
25656
  const pillWidth2 = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD;
@@ -26096,7 +26130,7 @@ function layoutSitemap(parsed, hiddenCounts, activeTagGroup, hiddenAttributes, e
26096
26130
  totalHeight += MARGIN2;
26097
26131
  const usedValuesByGroup = /* @__PURE__ */ new Map();
26098
26132
  for (const group of parsed.tagGroups) {
26099
- const key = group.name.toLowerCase();
26133
+ const key = tagAttrKey(group.name);
26100
26134
  const used = /* @__PURE__ */ new Set();
26101
26135
  const walk = (node) => {
26102
26136
  if (!node.isContainer && node.metadata[key]) {
@@ -26140,6 +26174,7 @@ var import_dagre, LABEL_FONT_SIZE3, META_FONT_SIZE3, CONTAINER_LABEL_FONT_SIZE2,
26140
26174
  var init_layout2 = __esm({
26141
26175
  "src/sitemap/layout.ts"() {
26142
26176
  "use strict";
26177
+ init_tag_groups();
26143
26178
  import_dagre = __toESM(require("@dagrejs/dagre"), 1);
26144
26179
  init_tag_groups();
26145
26180
  init_legend_constants();
@@ -26409,10 +26444,10 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26409
26444
  const displayNames = /* @__PURE__ */ new Map();
26410
26445
  const tagColors = /* @__PURE__ */ new Map();
26411
26446
  for (const group of parsed.tagGroups) {
26412
- displayNames.set(group.name.toLowerCase(), group.name);
26447
+ displayNames.set(tagAttrKey(group.name), group.name);
26413
26448
  for (const entry of group.entries) {
26414
26449
  tagColors.set(
26415
- `${group.name.toLowerCase()}:${entry.value.toLowerCase()}`,
26450
+ `${tagAttrKey(group.name)}:${entry.value.toLowerCase()}`,
26416
26451
  entry.color
26417
26452
  );
26418
26453
  }
@@ -26429,7 +26464,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26429
26464
  );
26430
26465
  }
26431
26466
  if (activeTagGroup) {
26432
- const tagKey = activeTagGroup.toLowerCase();
26467
+ const tagKey = tagAttrKey(activeTagGroup);
26433
26468
  const tagVal = c.tagMetadata[tagKey];
26434
26469
  if (tagVal) cG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26435
26470
  }
@@ -26492,7 +26527,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
26492
26527
  nodeG.style("cursor", "pointer").on("click", () => onClickItem(node.lineNumber));
26493
26528
  }
26494
26529
  if (activeTagGroup) {
26495
- const tagKey = activeTagGroup.toLowerCase();
26530
+ const tagKey = tagAttrKey(activeTagGroup);
26496
26531
  const tagVal = node.tagMetadata[tagKey];
26497
26532
  if (tagVal) nodeG.attr(`data-tag-${tagKey}`, tagVal.toLowerCase());
26498
26533
  }
@@ -26619,7 +26654,7 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
26619
26654
  const computedLayout = legendHandle.getLayout();
26620
26655
  if (computedLayout.activeCapsule?.addonX != null) {
26621
26656
  const capsule = computedLayout.activeCapsule;
26622
- const groupKey2 = capsule.groupName.toLowerCase();
26657
+ const groupKey2 = tagAttrKey(capsule.groupName);
26623
26658
  const isHidden = hiddenAttributes?.has(groupKey2) ?? false;
26624
26659
  const activeGroupEl = parent.select(`[data-legend-group="${groupKey2}"]`);
26625
26660
  if (!activeGroupEl.empty()) {
@@ -26684,6 +26719,7 @@ var d3Selection7, d3Shape2, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHE
26684
26719
  var init_renderer2 = __esm({
26685
26720
  "src/sitemap/renderer.ts"() {
26686
26721
  "use strict";
26722
+ init_tag_groups();
26687
26723
  d3Selection7 = __toESM(require("d3-selection"), 1);
26688
26724
  d3Shape2 = __toESM(require("d3-shape"), 1);
26689
26725
  init_fonts();
@@ -26822,7 +26858,7 @@ function resolveCardTagMeta(card, tagGroups, hiddenMetaGroups) {
26822
26858
  const meta = [];
26823
26859
  for (const group of tagGroups) {
26824
26860
  if (hiddenMetaGroups?.includes(group.name.toLowerCase())) continue;
26825
- const tagValue = card.tags[group.name.toLowerCase()];
26861
+ const tagValue = card.tags[tagAttrKey(group.name)];
26826
26862
  const value = tagValue ?? group.defaultValue;
26827
26863
  if (!value) continue;
26828
26864
  const entry = group.entries.find(
@@ -26842,7 +26878,7 @@ function resolveCardTagColor(card, tagGroups, activeTagGroup) {
26842
26878
  (g) => g.name.toLowerCase() === activeTagGroup.toLowerCase()
26843
26879
  );
26844
26880
  if (!group) return card.color;
26845
- const tagValue = card.tags[group.name.toLowerCase()];
26881
+ const tagValue = card.tags[tagAttrKey(group.name)];
26846
26882
  const value = tagValue ?? group.defaultValue;
26847
26883
  if (!value) return void 0;
26848
26884
  const entry = group.entries.find(
@@ -27152,7 +27188,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
27152
27188
  );
27153
27189
  const cg = g.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27154
27190
  if (activeTagGroup) {
27155
- const tagKey = activeTagGroup.toLowerCase();
27191
+ const tagKey = tagAttrKey(activeTagGroup);
27156
27192
  const tagValue = card.tags[tagKey];
27157
27193
  const group = parsed.tagGroups.find(
27158
27194
  (tg) => tg.name.toLowerCase() === tagKey
@@ -27213,7 +27249,7 @@ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
27213
27249
  return iconG;
27214
27250
  }
27215
27251
  function bucketCardsBySwimlane(columns, swimlaneGroup) {
27216
- const tagKey = swimlaneGroup.name.toLowerCase();
27252
+ const tagKey = tagAttrKey(swimlaneGroup.name);
27217
27253
  const buckets = /* @__PURE__ */ new Map();
27218
27254
  for (const entry of swimlaneGroup.entries) {
27219
27255
  buckets.set(entry.value.toLowerCase(), {
@@ -27501,7 +27537,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
27501
27537
  );
27502
27538
  const cg = parent.append("g").attr("class", "kanban-card").attr("data-card-id", card.id).attr("data-line-number", card.lineNumber);
27503
27539
  if (activeTagGroup) {
27504
- const tagKey = activeTagGroup.toLowerCase();
27540
+ const tagKey = tagAttrKey(activeTagGroup);
27505
27541
  const group = tagGroups.find((tg) => tg.name.toLowerCase() === tagKey);
27506
27542
  const value = card.tags[tagKey] ?? group?.defaultValue;
27507
27543
  if (value) {
@@ -27534,6 +27570,7 @@ var d3Selection8, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PAD
27534
27570
  var init_renderer3 = __esm({
27535
27571
  "src/kanban/renderer.ts"() {
27536
27572
  "use strict";
27573
+ init_tag_groups();
27537
27574
  d3Selection8 = __toESM(require("d3-selection"), 1);
27538
27575
  init_fonts();
27539
27576
  init_color_utils();
@@ -28516,7 +28553,7 @@ function classifyEREntities(tables, relationships) {
28516
28553
  const variance = indegreeValues.reduce((a, b) => a + (b - mean) ** 2, 0) / indegreeValues.length;
28517
28554
  const stddev = Math.sqrt(variance);
28518
28555
  const sorted = [...indegreeValues].sort((a, b) => a - b);
28519
- const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28556
+ const median2 = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
28520
28557
  for (const table of tables) {
28521
28558
  const id = table.id;
28522
28559
  const cols = table.columns;
@@ -28557,7 +28594,7 @@ function classifyEREntities(tables, relationships) {
28557
28594
  const nameMatchesLookup = LOOKUP_NAME_SUFFIXES.some(
28558
28595
  (s) => nameLower.endsWith(s)
28559
28596
  );
28560
- if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median) {
28597
+ if (nameMatchesLookup && cols.length <= 6 && fkCount <= 1 && indegree > median2) {
28561
28598
  result.set(id, "lookup");
28562
28599
  continue;
28563
28600
  }
@@ -28796,7 +28833,7 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
28796
28833
  seriesColors2[ni % seriesColors2.length];
28797
28834
  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);
28798
28835
  if (activeTagGroup) {
28799
- const tagKey = activeTagGroup.toLowerCase();
28836
+ const tagKey = tagAttrKey(activeTagGroup);
28800
28837
  const tagValue = node.metadata[tagKey];
28801
28838
  if (tagValue) {
28802
28839
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -28975,6 +29012,7 @@ var d3Selection10, d3Shape4, DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COL
28975
29012
  var init_renderer5 = __esm({
28976
29013
  "src/er/renderer.ts"() {
28977
29014
  "use strict";
29015
+ init_tag_groups();
28978
29016
  d3Selection10 = __toESM(require("d3-selection"), 1);
28979
29017
  d3Shape4 = __toESM(require("d3-shape"), 1);
28980
29018
  init_fonts();
@@ -29970,7 +30008,7 @@ function layeredCandidates(parsed, sizes, opts) {
29970
30008
  for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
29971
30009
  return c;
29972
30010
  };
29973
- const median = (vals) => {
30011
+ const median2 = (vals) => {
29974
30012
  if (vals.length === 0) return -1;
29975
30013
  vals.sort((x, y) => x - y);
29976
30014
  const m = Math.floor(vals.length / 2);
@@ -29985,7 +30023,7 @@ function layeredCandidates(parsed, sizes, opts) {
29985
30023
  const neigh = useUp ? up : down;
29986
30024
  const med = /* @__PURE__ */ new Map();
29987
30025
  for (const id of ids)
29988
- med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30026
+ med.set(id, median2(neigh.get(id).map((x) => orderOf.get(x))));
29989
30027
  const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
29990
30028
  const out = new Array(ids.length).fill(null);
29991
30029
  for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
@@ -30315,6 +30353,695 @@ var init_layout_layered = __esm({
30315
30353
  }
30316
30354
  });
30317
30355
 
30356
+ // src/boxes-and-lines/layout-grouped.ts
30357
+ function rng2(s) {
30358
+ return () => {
30359
+ s |= 0;
30360
+ s = s + 1831565813 | 0;
30361
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
30362
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
30363
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
30364
+ };
30365
+ }
30366
+ function median(vals) {
30367
+ if (vals.length === 0) return -1;
30368
+ vals.sort((x, y) => x - y);
30369
+ const m = Math.floor(vals.length / 2);
30370
+ if (vals.length % 2 === 1) return vals[m];
30371
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
30372
+ const left = vals[m - 1] - vals[0];
30373
+ const right = vals[vals.length - 1] - vals[m];
30374
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
30375
+ }
30376
+ function acyclicOrient(ids, edges) {
30377
+ const adj = /* @__PURE__ */ new Map();
30378
+ for (const l of ids) adj.set(l, []);
30379
+ for (const e of edges) adj.get(e.from)?.push({ to: e.to, idx: e.idx });
30380
+ const reversed = /* @__PURE__ */ new Set();
30381
+ const state = /* @__PURE__ */ new Map();
30382
+ for (const l of ids) state.set(l, 0);
30383
+ const stack = [];
30384
+ const visit = (root) => {
30385
+ stack.push(root);
30386
+ const it = /* @__PURE__ */ new Map();
30387
+ while (stack.length) {
30388
+ const u = stack[stack.length - 1];
30389
+ if (state.get(u) === 0) state.set(u, 1);
30390
+ const list = adj.get(u);
30391
+ let i = it.get(u) ?? 0;
30392
+ let descended = false;
30393
+ for (; i < list.length; i++) {
30394
+ const { to, idx } = list[i];
30395
+ const st = state.get(to);
30396
+ if (st === 1) reversed.add(idx);
30397
+ else if (st === 0) {
30398
+ it.set(u, i + 1);
30399
+ stack.push(to);
30400
+ descended = true;
30401
+ break;
30402
+ }
30403
+ }
30404
+ if (!descended) {
30405
+ it.set(u, list.length);
30406
+ state.set(u, 2);
30407
+ stack.pop();
30408
+ }
30409
+ }
30410
+ };
30411
+ for (const l of ids) if (state.get(l) === 0) visit(l);
30412
+ const dag = edges.map(
30413
+ (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 }
30414
+ );
30415
+ return { dag };
30416
+ }
30417
+ function longestPath(ids, dag) {
30418
+ const out = /* @__PURE__ */ new Map();
30419
+ const inDeg = /* @__PURE__ */ new Map();
30420
+ for (const l of ids) {
30421
+ out.set(l, []);
30422
+ inDeg.set(l, 0);
30423
+ }
30424
+ for (const d of dag) {
30425
+ out.get(d.from).push(d.to);
30426
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
30427
+ }
30428
+ const rank = /* @__PURE__ */ new Map();
30429
+ for (const l of ids) rank.set(l, 0);
30430
+ const queue = ids.filter((l) => (inDeg.get(l) ?? 0) === 0);
30431
+ const indeg2 = new Map(inDeg);
30432
+ let seen = 0;
30433
+ while (queue.length) {
30434
+ const u = queue.shift();
30435
+ seen++;
30436
+ for (const v of out.get(u)) {
30437
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
30438
+ indeg2.set(v, indeg2.get(v) - 1);
30439
+ if (indeg2.get(v) === 0) queue.push(v);
30440
+ }
30441
+ }
30442
+ return seen === ids.length ? rank : null;
30443
+ }
30444
+ function groupedTierCandidates(parsed, sizes, opts) {
30445
+ if (parsed.groups.length === 0) return [];
30446
+ const nCount = parsed.nodes.length;
30447
+ if (nCount < 3 || nCount > 60) return [];
30448
+ const isTB = parsed.direction === "TB";
30449
+ const labels = parsed.nodes.map((n) => n.label);
30450
+ const labelSet = new Set(labels);
30451
+ for (const e of parsed.edges)
30452
+ if (e.source.startsWith("__group_") || e.target.startsWith("__group_"))
30453
+ return [];
30454
+ const groupByLabel = new Map(parsed.groups.map((g) => [g.label, g]));
30455
+ const directGroupOf = /* @__PURE__ */ new Map();
30456
+ for (const grp of parsed.groups)
30457
+ for (const c of grp.children)
30458
+ if (labelSet.has(c)) directGroupOf.set(c, grp.label);
30459
+ const topGroupOf = (groupLabel) => {
30460
+ let cur = groupLabel;
30461
+ const guard = /* @__PURE__ */ new Set();
30462
+ while (true) {
30463
+ const g = groupByLabel.get(cur);
30464
+ if (!g?.parentGroup || guard.has(cur)) return cur;
30465
+ guard.add(cur);
30466
+ cur = g.parentGroup;
30467
+ }
30468
+ };
30469
+ const pathOf = (label) => {
30470
+ const direct = directGroupOf.get(label);
30471
+ if (!direct) return [];
30472
+ const chain = [direct];
30473
+ let cur = direct;
30474
+ const guard = /* @__PURE__ */ new Set([direct]);
30475
+ while (true) {
30476
+ const g = groupByLabel.get(cur);
30477
+ if (!g?.parentGroup || guard.has(g.parentGroup)) break;
30478
+ chain.push(g.parentGroup);
30479
+ guard.add(g.parentGroup);
30480
+ cur = g.parentGroup;
30481
+ }
30482
+ return chain.reverse();
30483
+ };
30484
+ const SINGLE = "\0node\0";
30485
+ const unitOf = (label) => {
30486
+ const direct = directGroupOf.get(label);
30487
+ return direct ? topGroupOf(direct) : SINGLE + label;
30488
+ };
30489
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
30490
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
30491
+ );
30492
+ if (edges.length === 0) return [];
30493
+ const unitOfNode = /* @__PURE__ */ new Map();
30494
+ for (const l of labels) unitOfNode.set(l, unitOf(l));
30495
+ const units = Array.from(new Set(labels.map((l) => unitOfNode.get(l))));
30496
+ const interUnit = edges.filter(({ e }) => unitOfNode.get(e.source) !== unitOfNode.get(e.target)).map(({ e, i }) => ({
30497
+ from: unitOfNode.get(e.source),
30498
+ to: unitOfNode.get(e.target),
30499
+ idx: i
30500
+ }));
30501
+ const { dag: unitDag } = acyclicOrient(units, interUnit);
30502
+ const tier = longestPath(
30503
+ units,
30504
+ unitDag.map((d) => ({ from: d.from, to: d.to }))
30505
+ );
30506
+ if (!tier) return [];
30507
+ const localRank = /* @__PURE__ */ new Map();
30508
+ const unitMembers = /* @__PURE__ */ new Map();
30509
+ for (const l of labels) {
30510
+ const u = unitOfNode.get(l);
30511
+ (unitMembers.get(u) ?? unitMembers.set(u, []).get(u)).push(l);
30512
+ }
30513
+ for (const [u, members] of unitMembers) {
30514
+ const memberSet = new Set(members);
30515
+ const intra = edges.filter(
30516
+ ({ e }) => unitOfNode.get(e.source) === u && unitOfNode.get(e.target) === u && memberSet.has(e.source) && memberSet.has(e.target)
30517
+ ).map(({ e, i }) => ({ from: e.source, to: e.target, idx: i }));
30518
+ const { dag } = acyclicOrient(members, intra);
30519
+ const lr = longestPath(
30520
+ members,
30521
+ dag.map((d) => ({ from: d.from, to: d.to }))
30522
+ );
30523
+ if (!lr) return [];
30524
+ for (const m of members) localRank.set(m, lr.get(m));
30525
+ }
30526
+ const maxTier = Math.max(...units.map((u) => tier.get(u)));
30527
+ const bandHeight = Array.from({ length: maxTier + 1 }, () => 1);
30528
+ for (const u of units) {
30529
+ const h = Math.max(
30530
+ ...unitMembers.get(u).map((m) => localRank.get(m) + 1)
30531
+ );
30532
+ const t = tier.get(u);
30533
+ bandHeight[t] = Math.max(bandHeight[t], h);
30534
+ }
30535
+ const tierBase = [];
30536
+ let accBase = 0;
30537
+ for (let t = 0; t <= maxTier; t++) {
30538
+ tierBase[t] = accBase;
30539
+ accBase += bandHeight[t];
30540
+ }
30541
+ const tierStartRanks = new Set(tierBase.slice(1));
30542
+ const globalRank = /* @__PURE__ */ new Map();
30543
+ for (const l of labels)
30544
+ globalRank.set(
30545
+ l,
30546
+ tierBase[tier.get(unitOfNode.get(l))] + localRank.get(l)
30547
+ );
30548
+ const maxRank = Math.max(...labels.map((l) => globalRank.get(l)));
30549
+ const node = /* @__PURE__ */ new Map();
30550
+ for (const n of parsed.nodes) {
30551
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30552
+ node.set(n.label, {
30553
+ id: n.label,
30554
+ dummy: false,
30555
+ rank: globalRank.get(n.label),
30556
+ cross: 0,
30557
+ thick: isTB ? s.width : s.height,
30558
+ depth: isTB ? s.height : s.width,
30559
+ realW: s.width,
30560
+ realH: s.height,
30561
+ unit: unitOfNode.get(n.label),
30562
+ path: pathOf(n.label)
30563
+ });
30564
+ }
30565
+ const chains = [];
30566
+ const segs = [];
30567
+ const backEdges = [];
30568
+ const flatEdges = [];
30569
+ for (const { e, i } of edges) {
30570
+ const rs = globalRank.get(e.source);
30571
+ const rt = globalRank.get(e.target);
30572
+ if (rs === rt) {
30573
+ flatEdges.push({ edgeIdx: i, a: e.source, b: e.target });
30574
+ continue;
30575
+ }
30576
+ if (rt < rs) {
30577
+ backEdges.push({ edgeIdx: i, src: e.source, tgt: e.target });
30578
+ continue;
30579
+ }
30580
+ const chain = [e.source];
30581
+ let prev = e.source;
30582
+ for (let r = rs + 1; r < rt; r++) {
30583
+ const id = `__d${i}_${r}`;
30584
+ node.set(id, {
30585
+ id,
30586
+ dummy: true,
30587
+ rank: r,
30588
+ cross: 0,
30589
+ thick: DUMMY_THICK2,
30590
+ depth: 0,
30591
+ realW: 0,
30592
+ realH: 0,
30593
+ unit: SINGLE + id,
30594
+ path: []
30595
+ });
30596
+ segs.push({ a: prev, b: id });
30597
+ chain.push(id);
30598
+ prev = id;
30599
+ }
30600
+ segs.push({ a: prev, b: e.target });
30601
+ chain.push(e.target);
30602
+ chains.push({ edgeIdx: i, chain });
30603
+ }
30604
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
30605
+ for (const n of node.values()) ranks[n.rank].push(n.id);
30606
+ const up = /* @__PURE__ */ new Map();
30607
+ const down = /* @__PURE__ */ new Map();
30608
+ for (const id of node.keys()) {
30609
+ up.set(id, []);
30610
+ down.set(id, []);
30611
+ }
30612
+ for (const s of segs) {
30613
+ down.get(s.a).push(s.b);
30614
+ up.get(s.b).push(s.a);
30615
+ }
30616
+ const orderOf = /* @__PURE__ */ new Map();
30617
+ const gapCrossings = (r) => {
30618
+ const lower = ranks[r + 1];
30619
+ if (!lower) return 0;
30620
+ const es = [];
30621
+ for (const a of ranks[r]) {
30622
+ const pa = orderOf.get(a);
30623
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
30624
+ }
30625
+ let c = 0;
30626
+ for (let i = 0; i < es.length; i++)
30627
+ for (let j = i + 1; j < es.length; j++) {
30628
+ const A = es[i], B = es[j];
30629
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
30630
+ }
30631
+ return c;
30632
+ };
30633
+ const totalCrossings = () => {
30634
+ let c = 0;
30635
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
30636
+ return c;
30637
+ };
30638
+ const wmedianRank = (r, useUp) => {
30639
+ const ids = ranks[r];
30640
+ const neigh = useUp ? up : down;
30641
+ const med = /* @__PURE__ */ new Map();
30642
+ for (const id of ids)
30643
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
30644
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
30645
+ const out = new Array(ids.length).fill(null);
30646
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
30647
+ let mi = 0;
30648
+ for (let slot = 0; slot < out.length; slot++)
30649
+ if (out[slot] === null) out[slot] = movable[mi++];
30650
+ const final = out;
30651
+ final.forEach((id, i) => orderOf.set(id, i));
30652
+ ranks[r] = final;
30653
+ };
30654
+ const transpose = () => {
30655
+ let improved = true;
30656
+ let guard = 0;
30657
+ while (improved && guard++ < 12) {
30658
+ improved = false;
30659
+ for (let r = 0; r <= maxRank; r++) {
30660
+ const ids = ranks[r];
30661
+ for (let i = 0; i < ids.length - 1; i++) {
30662
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30663
+ const a = ids[i], b = ids[i + 1];
30664
+ ids[i] = b;
30665
+ ids[i + 1] = a;
30666
+ orderOf.set(b, i);
30667
+ orderOf.set(a, i + 1);
30668
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
30669
+ if (after < before) improved = true;
30670
+ else {
30671
+ ids[i] = a;
30672
+ ids[i + 1] = b;
30673
+ orderOf.set(a, i);
30674
+ orderOf.set(b, i + 1);
30675
+ }
30676
+ }
30677
+ }
30678
+ }
30679
+ };
30680
+ const initOrder = (seed) => {
30681
+ const r = seed === 0 ? null : rng2(seed);
30682
+ for (let rr = 0; rr <= maxRank; rr++) {
30683
+ const ids = ranks[rr].slice();
30684
+ if (r)
30685
+ for (let i = ids.length - 1; i > 0; i--) {
30686
+ const j = Math.floor(r() * (i + 1));
30687
+ [ids[i], ids[j]] = [ids[j], ids[i]];
30688
+ }
30689
+ ranks[rr] = ids;
30690
+ ids.forEach((id, i) => orderOf.set(id, i));
30691
+ }
30692
+ };
30693
+ const restarts = opts?.restarts ?? (nCount <= 16 ? 20 : nCount <= 30 ? 12 : 6);
30694
+ const keepK = opts?.keepK ?? 3;
30695
+ const found = [];
30696
+ for (let s = 0; s < restarts; s++) {
30697
+ initOrder(s);
30698
+ let best = totalCrossings();
30699
+ let bestSnap = new Map(orderOf);
30700
+ for (let iter = 0; iter < 8; iter++) {
30701
+ if (iter % 2 === 0)
30702
+ for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
30703
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
30704
+ transpose();
30705
+ const c = totalCrossings();
30706
+ if (c < best) {
30707
+ best = c;
30708
+ bestSnap = new Map(orderOf);
30709
+ }
30710
+ if (c === 0) break;
30711
+ }
30712
+ for (const [k, v] of bestSnap) orderOf.set(k, v);
30713
+ for (let r = 0; r <= maxRank; r++)
30714
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
30715
+ const sig = ranks.map((r) => r.join(",")).join("|");
30716
+ if (!found.some((f) => f.sig === sig))
30717
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
30718
+ }
30719
+ found.sort((a, b) => a.cross - b.cross);
30720
+ const keep = found.slice(0, keepK);
30721
+ if (keep.length === 0) return [];
30722
+ const results = [];
30723
+ for (const cand of keep) {
30724
+ for (const [k, v] of cand.order) orderOf.set(k, v);
30725
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
30726
+ for (const n of node.values()) rk[n.rank].push(n.id);
30727
+ for (let r = 0; r <= maxRank; r++)
30728
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
30729
+ results.push(realize(rk));
30730
+ }
30731
+ return results;
30732
+ function weight(a, b) {
30733
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY2;
30734
+ if (a.dummy || b.dummy) return W_REAL_DUMMY2;
30735
+ return W_REAL_REAL2;
30736
+ }
30737
+ function placeWithSeparation(ids, desired, gap) {
30738
+ const n = ids.length;
30739
+ if (n === 0) return;
30740
+ const off = [0];
30741
+ for (let i = 1; i < n; i++) off[i] = off[i - 1] + gap[i];
30742
+ const d = desired.map((v, i) => v - off[i]);
30743
+ const blocks = [];
30744
+ for (let i = 0; i < n; i++) {
30745
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
30746
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
30747
+ const prev = blocks.pop();
30748
+ const count = prev.count + b.count;
30749
+ const sumOffset = prev.sumOffset + b.sumOffset;
30750
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
30751
+ }
30752
+ blocks.push(b);
30753
+ }
30754
+ const xs = new Array(n);
30755
+ for (const b of blocks)
30756
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
30757
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
30758
+ }
30759
+ function gapBetween(a, b) {
30760
+ const base = a.thick / 2 + b.thick / 2 + NODESEP2;
30761
+ if (a.dummy || b.dummy) return base;
30762
+ if (a.unit !== b.unit) return base + 2 * GROUP_PAD + GROUP_GAP;
30763
+ const da = a.path[a.path.length - 1];
30764
+ const db = b.path[b.path.length - 1];
30765
+ if (da !== db && (da || db)) return base + 2 * GROUP_PAD;
30766
+ return base;
30767
+ }
30768
+ function realize(rk) {
30769
+ const bandDepth = rk.map(
30770
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
30771
+ );
30772
+ const bandCenter = [];
30773
+ let acc = MARGIN4;
30774
+ for (let r = 0; r <= maxRank; r++) {
30775
+ if (r > 0) acc += RANKSEP2 + (tierStartRanks.has(r) ? TIER_GAP : 0);
30776
+ bandCenter[r] = acc + bandDepth[r] / 2;
30777
+ acc += bandDepth[r];
30778
+ }
30779
+ const gapsFor = (ids) => ids.map(
30780
+ (id, i) => i === 0 ? 0 : gapBetween(node.get(ids[i - 1]), node.get(id))
30781
+ );
30782
+ for (let r = 0; r <= maxRank; r++) {
30783
+ const ids = rk[r];
30784
+ const gap = gapsFor(ids);
30785
+ let x = MARGIN4;
30786
+ for (let i = 0; i < ids.length; i++) {
30787
+ const n = node.get(ids[i]);
30788
+ x += gap[i];
30789
+ n.cross = i === 0 ? MARGIN4 + n.thick / 2 : x;
30790
+ }
30791
+ }
30792
+ const ITER = 18;
30793
+ for (let it = 0; it < ITER; it++) {
30794
+ const downPass = it % 2 === 0;
30795
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
30796
+ for (const r of order) {
30797
+ const ids = rk[r];
30798
+ const desired = ids.map((id) => {
30799
+ const n = node.get(id);
30800
+ let sw = 0, sx2 = 0;
30801
+ for (const nb of up.get(id)) {
30802
+ const w = weight(n, node.get(nb));
30803
+ sw += w;
30804
+ sx2 += w * node.get(nb).cross;
30805
+ }
30806
+ for (const nb of down.get(id)) {
30807
+ const w = weight(n, node.get(nb));
30808
+ sw += w;
30809
+ sx2 += w * node.get(nb).cross;
30810
+ }
30811
+ return sw > 0 ? sx2 / sw : n.cross;
30812
+ });
30813
+ placeWithSeparation(ids, desired, gapsFor(ids));
30814
+ }
30815
+ }
30816
+ let minC = Infinity;
30817
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
30818
+ const shiftC = MARGIN4 - minC;
30819
+ for (const n of node.values()) n.cross += shiftC;
30820
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
30821
+ const layoutNodes = parsed.nodes.map((pn) => {
30822
+ const n = node.get(pn.label);
30823
+ const c = coord(n);
30824
+ return {
30825
+ label: pn.label,
30826
+ x: c.x,
30827
+ y: c.y,
30828
+ width: n.realW,
30829
+ height: n.realH
30830
+ };
30831
+ });
30832
+ const chainById = /* @__PURE__ */ new Map();
30833
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
30834
+ let minCross = Infinity, maxCross = -Infinity;
30835
+ for (const n of node.values()) {
30836
+ if (n.dummy) continue;
30837
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
30838
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
30839
+ }
30840
+ const GAP = 34;
30841
+ const LANE = 28;
30842
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
30843
+ const sideOf = (b) => {
30844
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
30845
+ return d >= 0 ? 1 : -1;
30846
+ };
30847
+ const backSorted = backEdges.map((b) => ({
30848
+ ...b,
30849
+ side: sideOf(b),
30850
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
30851
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
30852
+ const sideCounts = /* @__PURE__ */ new Map();
30853
+ for (const b of backSorted)
30854
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
30855
+ const laneCounter = /* @__PURE__ */ new Map();
30856
+ const backPoints = /* @__PURE__ */ new Map();
30857
+ for (const b of backSorted) {
30858
+ const s = b.side;
30859
+ const k = laneCounter.get(s) ?? 0;
30860
+ laneCounter.set(s, k + 1);
30861
+ const K = sideCounts.get(s);
30862
+ const src = node.get(b.src);
30863
+ const tgt = node.get(b.tgt);
30864
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
30865
+ const srcCtr = bandCenter[src.rank];
30866
+ const tgtCtr = bandCenter[tgt.rank];
30867
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
30868
+ const srcR = srcCtr - frac * faceLim(src.depth);
30869
+ const tgtR = tgtCtr + frac * faceLim(tgt.depth);
30870
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30871
+ const off = 10;
30872
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
30873
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
30874
+ backPoints.set(b.edgeIdx, [
30875
+ m(src.cross, srcCtr),
30876
+ m(srcEdgeC, srcR),
30877
+ m(laneC, srcR),
30878
+ m(laneC, tgtR),
30879
+ m(tgtEdgeC, tgtR),
30880
+ m(tgt.cross, tgtCtr)
30881
+ ]);
30882
+ }
30883
+ const flatPoints = /* @__PURE__ */ new Map();
30884
+ for (const f of flatEdges) {
30885
+ const a = node.get(f.a);
30886
+ const b = node.get(f.b);
30887
+ const ctr = bandCenter[a.rank];
30888
+ const bow = ctr - (a.depth / 2 + 26);
30889
+ const m = (c, rr) => isTB ? { x: c, y: rr } : { x: rr, y: c };
30890
+ flatPoints.set(f.edgeIdx, [
30891
+ m(a.cross, ctr),
30892
+ m(a.cross, bow),
30893
+ m(b.cross, bow),
30894
+ m(b.cross, ctr)
30895
+ ]);
30896
+ }
30897
+ const layoutEdges = [];
30898
+ for (let i = 0; i < parsed.edges.length; i++) {
30899
+ const e = parsed.edges[i];
30900
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
30901
+ let points;
30902
+ if (e.source === e.target) {
30903
+ const c = coord(node.get(e.source));
30904
+ points = [c, c];
30905
+ } else if (backPoints.has(i)) points = backPoints.get(i);
30906
+ else if (flatPoints.has(i)) points = flatPoints.get(i);
30907
+ else {
30908
+ const chain = chainById.get(i);
30909
+ if (!chain) continue;
30910
+ points = chain.map((id) => coord(node.get(id)));
30911
+ }
30912
+ layoutEdges.push({
30913
+ source: e.source,
30914
+ target: e.target,
30915
+ ...e.label !== void 0 && { label: e.label },
30916
+ bidirectional: e.bidirectional,
30917
+ lineNumber: e.lineNumber,
30918
+ points,
30919
+ yOffset: 0,
30920
+ parallelCount: 1,
30921
+ metadata: e.metadata
30922
+ });
30923
+ }
30924
+ const nodeBox = new Map(
30925
+ layoutNodes.map((n) => [
30926
+ n.label,
30927
+ {
30928
+ l: n.x - n.width / 2,
30929
+ r: n.x + n.width / 2,
30930
+ t: n.y - n.height / 2,
30931
+ b: n.y + n.height / 2
30932
+ }
30933
+ ])
30934
+ );
30935
+ const depthOf = (label) => {
30936
+ let d = 0;
30937
+ let cur = label;
30938
+ const guard = /* @__PURE__ */ new Set();
30939
+ while (cur && !guard.has(cur)) {
30940
+ guard.add(cur);
30941
+ cur = groupByLabel.get(cur)?.parentGroup;
30942
+ if (cur) d++;
30943
+ }
30944
+ return d;
30945
+ };
30946
+ const childGroups = /* @__PURE__ */ new Map();
30947
+ for (const g of parsed.groups)
30948
+ if (g.parentGroup)
30949
+ (childGroups.get(g.parentGroup) ?? childGroups.set(g.parentGroup, []).get(g.parentGroup)).push(g.label);
30950
+ const rectByGroup = /* @__PURE__ */ new Map();
30951
+ const ordered = [...parsed.groups].sort(
30952
+ (a, b) => depthOf(b.label) - depthOf(a.label)
30953
+ );
30954
+ const layoutGroups = [];
30955
+ for (const grp of ordered) {
30956
+ let l = Infinity, r = -Infinity, t = Infinity, bm = -Infinity;
30957
+ const acc2 = (box) => {
30958
+ l = Math.min(l, box.l);
30959
+ r = Math.max(r, box.r);
30960
+ t = Math.min(t, box.t);
30961
+ bm = Math.max(bm, box.b);
30962
+ };
30963
+ for (const c of grp.children) {
30964
+ const nb = nodeBox.get(c);
30965
+ if (nb) acc2(nb);
30966
+ }
30967
+ for (const sub of childGroups.get(grp.label) ?? []) {
30968
+ const sr = rectByGroup.get(sub);
30969
+ if (sr) acc2(sr);
30970
+ }
30971
+ if (!Number.isFinite(l)) continue;
30972
+ const x0 = l - GROUP_PAD;
30973
+ const x1 = r + GROUP_PAD;
30974
+ const y0 = t - GROUP_LABEL_ZONE2;
30975
+ const y1 = bm + GROUP_PAD;
30976
+ rectByGroup.set(grp.label, { l: x0, r: x1, t: y0, b: y1 });
30977
+ layoutGroups.push({
30978
+ label: grp.label,
30979
+ lineNumber: grp.lineNumber,
30980
+ x: (x0 + x1) / 2,
30981
+ y: (y0 + y1) / 2,
30982
+ width: x1 - x0,
30983
+ height: y1 - y0,
30984
+ collapsed: false,
30985
+ childCount: grp.children.length
30986
+ });
30987
+ }
30988
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
30989
+ const ext = (x0, y0, x1, y1) => {
30990
+ minX = Math.min(minX, x0);
30991
+ minY = Math.min(minY, y0);
30992
+ maxX = Math.max(maxX, x1);
30993
+ maxY = Math.max(maxY, y1);
30994
+ };
30995
+ for (const n of layoutNodes)
30996
+ ext(
30997
+ n.x - n.width / 2,
30998
+ n.y - n.height / 2,
30999
+ n.x + n.width / 2,
31000
+ n.y + n.height / 2
31001
+ );
31002
+ for (const e of layoutEdges)
31003
+ for (const p of e.points) ext(p.x, p.y, p.x, p.y);
31004
+ for (const g of layoutGroups)
31005
+ ext(
31006
+ g.x - g.width / 2,
31007
+ g.y - g.height / 2,
31008
+ g.x + g.width / 2,
31009
+ g.y + g.height / 2
31010
+ );
31011
+ const sx = MARGIN4 - minX;
31012
+ const sy = MARGIN4 - minY;
31013
+ const shift = sx !== 0 || sy !== 0;
31014
+ return {
31015
+ nodes: shift ? layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : layoutNodes,
31016
+ edges: shift ? layoutEdges.map((e) => ({
31017
+ ...e,
31018
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
31019
+ })) : layoutEdges,
31020
+ groups: shift ? layoutGroups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : layoutGroups,
31021
+ width: maxX + sx + MARGIN4,
31022
+ height: maxY + sy + MARGIN4
31023
+ };
31024
+ }
31025
+ }
31026
+ var NODESEP2, RANKSEP2, DUMMY_THICK2, MARGIN4, GROUP_PAD, GROUP_LABEL_ZONE2, GROUP_GAP, TIER_GAP, W_REAL_REAL2, W_REAL_DUMMY2, W_DUMMY_DUMMY2;
31027
+ var init_layout_grouped = __esm({
31028
+ "src/boxes-and-lines/layout-grouped.ts"() {
31029
+ "use strict";
31030
+ init_layout5();
31031
+ NODESEP2 = 50;
31032
+ RANKSEP2 = 60;
31033
+ DUMMY_THICK2 = 10;
31034
+ MARGIN4 = 40;
31035
+ GROUP_PAD = 16;
31036
+ GROUP_LABEL_ZONE2 = 32;
31037
+ GROUP_GAP = 24;
31038
+ TIER_GAP = GROUP_LABEL_ZONE2 + 2 * GROUP_PAD;
31039
+ W_REAL_REAL2 = 1;
31040
+ W_REAL_DUMMY2 = 2;
31041
+ W_DUMMY_DUMMY2 = 8;
31042
+ }
31043
+ });
31044
+
30318
31045
  // src/boxes-and-lines/layout-search.ts
30319
31046
  var layout_search_exports = {};
30320
31047
  __export(layout_search_exports, {
@@ -30330,7 +31057,7 @@ __export(layout_search_exports, {
30330
31057
  layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
30331
31058
  separateGroupBands: () => separateGroupBands
30332
31059
  });
30333
- function rng2(s) {
31060
+ function rng3(s) {
30334
31061
  return () => {
30335
31062
  s |= 0;
30336
31063
  s = s + 1831565813 | 0;
@@ -30685,7 +31412,7 @@ function detectGroupOverlaps(layout, opts) {
30685
31412
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30686
31413
  const rend = raw.map((a, i) => {
30687
31414
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30688
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31415
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30689
31416
  });
30690
31417
  const hit = /* @__PURE__ */ new Set();
30691
31418
  for (let i = 0; i < raw.length; i++)
@@ -30712,7 +31439,7 @@ function countGroupOverlaps(layout, opts) {
30712
31439
  const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
30713
31440
  const rend = raw.map((a, i) => {
30714
31441
  const parent = raw.some((b, j) => j !== i && contains(a, b));
30715
- return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
31442
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE3 } : a;
30716
31443
  });
30717
31444
  let count = 0;
30718
31445
  for (let i = 0; i < raw.length; i++)
@@ -30755,7 +31482,7 @@ function separateGroupBands(layout, parsed) {
30755
31482
  let lo = (axis === "y" ? g.y : g.x) - half2;
30756
31483
  const hi = (axis === "y" ? g.y : g.x) + half2;
30757
31484
  const isParent = parsed.groups.some((c) => c.parentGroup === label);
30758
- if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
31485
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE3;
30759
31486
  return { label, lo, hi, c: (lo + hi) / 2 };
30760
31487
  });
30761
31488
  bands.sort((a, b) => a.c - b.c);
@@ -30826,7 +31553,7 @@ function separateGroupBands(layout, parsed) {
30826
31553
  acc(n.x + n.width / 2, n.y + n.height / 2);
30827
31554
  }
30828
31555
  for (const g of groups) {
30829
- acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
31556
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE3);
30830
31557
  acc(g.x + g.width / 2, g.y + g.height / 2);
30831
31558
  }
30832
31559
  for (const e of edges) for (const p of e.points) acc(p.x, p.y);
@@ -31090,7 +31817,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31090
31817
  }
31091
31818
  ];
31092
31819
  });
31093
- const GROUP_PAD = 16;
31820
+ const GROUP_PAD2 = 16;
31094
31821
  const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
31095
31822
  const groups = [];
31096
31823
  for (const grp of parsed.groups) {
@@ -31104,10 +31831,10 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31104
31831
  gy1 = Math.max(gy1, n2.y + n2.height / 2);
31105
31832
  }
31106
31833
  if (!Number.isFinite(gx0)) continue;
31107
- const x0 = gx0 - GROUP_PAD;
31108
- const x1 = gx1 + GROUP_PAD;
31109
- const y0 = gy0 - GROUP_LABEL_ZONE2;
31110
- const y1 = gy1 + GROUP_PAD;
31834
+ const x0 = gx0 - GROUP_PAD2;
31835
+ const x1 = gx1 + GROUP_PAD2;
31836
+ const y0 = gy0 - GROUP_LABEL_ZONE3;
31837
+ const y1 = gy1 + GROUP_PAD2;
31111
31838
  groups.push({
31112
31839
  label: grp.label,
31113
31840
  lineNumber: grp.lineNumber,
@@ -31164,7 +31891,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31164
31891
  };
31165
31892
  }
31166
31893
  function place(cfg) {
31167
- const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
31894
+ const r = cfg.seed === void 0 ? null : rng3(cfg.seed + 1);
31168
31895
  const ord = (a) => r ? shuffle(a, r) : a.slice();
31169
31896
  const g = new import_dagre4.default.graphlib.Graph({ compound: true, multigraph: true });
31170
31897
  g.setGraph({
@@ -31351,8 +32078,15 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31351
32078
  layered = layeredCandidates(parsed, sizes);
31352
32079
  } catch {
31353
32080
  }
32081
+ if (parsed.groups.length > 0 && collapsedGroupLabels.size === 0) {
32082
+ try {
32083
+ layered = layered.concat(groupedTierCandidates(parsed, sizes));
32084
+ } catch {
32085
+ }
32086
+ }
31354
32087
  const fastKey = /* @__PURE__ */ new Map();
31355
- for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
32088
+ for (const lay of pool)
32089
+ fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31356
32090
  pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
31357
32091
  const refineK = Math.min(REFINE_K, pool.length);
31358
32092
  let best = pool[0];
@@ -31417,7 +32151,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31417
32151
  }
31418
32152
  return best;
31419
32153
  }
31420
- var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
32154
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE3;
31421
32155
  var init_layout_search = __esm({
31422
32156
  "src/boxes-and-lines/layout-search.ts"() {
31423
32157
  "use strict";
@@ -31425,6 +32159,7 @@ var init_layout_search = __esm({
31425
32159
  import_d3_shape = require("d3-shape");
31426
32160
  init_layout5();
31427
32161
  init_layout_layered();
32162
+ init_layout_grouped();
31428
32163
  DEFAULT_LAMBDA = 4;
31429
32164
  ESCALATE_THRESHOLD = 4;
31430
32165
  ESCALATE_MAX_N = 45;
@@ -31433,7 +32168,7 @@ var init_layout_search = __esm({
31433
32168
  splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
31434
32169
  PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31435
32170
  FLAT_CACHE = /* @__PURE__ */ new WeakMap();
31436
- GROUP_LABEL_ZONE2 = 32;
32171
+ GROUP_LABEL_ZONE3 = 32;
31437
32172
  }
31438
32173
  });
31439
32174
 
@@ -31610,8 +32345,8 @@ function attachNotes(layout, parsed, collapsedNotes) {
31610
32345
  nodes: finalNodes,
31611
32346
  edges: finalEdges,
31612
32347
  groups: finalGroups,
31613
- width: bbMaxX + shiftX + MARGIN4,
31614
- height: bbMaxY + shiftY + MARGIN4
32348
+ width: bbMaxX + shiftX + MARGIN5,
32349
+ height: bbMaxY + shiftY + MARGIN5
31615
32350
  };
31616
32351
  }
31617
32352
  function applyParallelEdgeOffsets(layout) {
@@ -31644,13 +32379,13 @@ function applyParallelEdgeOffsets(layout) {
31644
32379
  }))
31645
32380
  };
31646
32381
  }
31647
- 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;
32382
+ 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;
31648
32383
  var init_layout5 = __esm({
31649
32384
  "src/boxes-and-lines/layout.ts"() {
31650
32385
  "use strict";
31651
32386
  init_text_measure();
31652
32387
  init_notes();
31653
- MARGIN4 = 40;
32388
+ MARGIN5 = 40;
31654
32389
  MAX_PARALLEL_EDGES = 5;
31655
32390
  PARALLEL_SPACING = 22;
31656
32391
  PHI = 1.618;
@@ -31821,7 +32556,7 @@ function layoutMindmap(parsed, _palette, options) {
31821
32556
  leafWidth: ctx.structural(LEAF_WIDTH),
31822
32557
  hGap: ctx.aesthetic(H_GAP2),
31823
32558
  vGap: ctx.aesthetic(V_GAP2),
31824
- margin: ctx.aesthetic(MARGIN5),
32559
+ margin: ctx.aesthetic(MARGIN6),
31825
32560
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
31826
32561
  };
31827
32562
  populateDepthCache(roots);
@@ -32199,7 +32934,7 @@ function populateDepthCache(roots) {
32199
32934
  };
32200
32935
  walk(roots, 0);
32201
32936
  }
32202
- 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;
32937
+ 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;
32203
32938
  var init_layout6 = __esm({
32204
32939
  "src/mindmap/layout.ts"() {
32205
32940
  "use strict";
@@ -32215,7 +32950,7 @@ var init_layout6 = __esm({
32215
32950
  NODE_V_PAD = 10;
32216
32951
  H_GAP2 = 40;
32217
32952
  V_GAP2 = 12;
32218
- MARGIN5 = 40;
32953
+ MARGIN6 = 40;
32219
32954
  MULTI_ROOT_GAP = 60;
32220
32955
  nodeDepthCache = /* @__PURE__ */ new Map();
32221
32956
  }
@@ -32472,7 +33207,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
32472
33207
  );
32473
33208
  const nodeG = mainG.append("g").attr("class", "mindmap-node").attr("data-line-number", node.lineNumber);
32474
33209
  if (activeTagGroup) {
32475
- const tagKey = activeTagGroup.toLowerCase();
33210
+ const tagKey = tagAttrKey(activeTagGroup);
32476
33211
  const metaValue = node.metadata[tagKey];
32477
33212
  if (metaValue) {
32478
33213
  nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
@@ -32591,6 +33326,7 @@ var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_
32591
33326
  var init_renderer7 = __esm({
32592
33327
  "src/mindmap/renderer.ts"() {
32593
33328
  "use strict";
33329
+ init_tag_groups();
32594
33330
  d3Selection12 = __toESM(require("d3-selection"), 1);
32595
33331
  init_fonts();
32596
33332
  init_export_container();
@@ -33801,7 +34537,7 @@ function resolveNodeColor4(el, tagGroups, activeGroupName, ancestors) {
33801
34537
  (g) => g.name.toLowerCase() === activeGroupName.toLowerCase()
33802
34538
  );
33803
34539
  if (!group) return void 0;
33804
- const key = group.name.toLowerCase();
34540
+ const key = tagAttrKey(group.name);
33805
34541
  let metaValue = el.metadata[key];
33806
34542
  if (!metaValue && ancestors) {
33807
34543
  for (const ancestor of ancestors) {
@@ -34003,8 +34739,8 @@ function layoutC4Context(parsed, activeTagGroup) {
34003
34739
  }
34004
34740
  }
34005
34741
  if (nodes.length > 0) {
34006
- const shiftX = MARGIN6 - minX;
34007
- const shiftY = MARGIN6 - minY;
34742
+ const shiftX = MARGIN7 - minX;
34743
+ const shiftY = MARGIN7 - minY;
34008
34744
  for (const node of nodes) {
34009
34745
  node.x += shiftX;
34010
34746
  node.y += shiftY;
@@ -34016,12 +34752,12 @@ function layoutC4Context(parsed, activeTagGroup) {
34016
34752
  }
34017
34753
  }
34018
34754
  }
34019
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
34020
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
34755
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN7 * 2 : 0;
34756
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN7 * 2 : 0;
34021
34757
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34022
34758
  if (legendGroups.length > 0) {
34023
- const legendY = totalHeight + MARGIN6;
34024
- let legendX = MARGIN6;
34759
+ const legendY = totalHeight + MARGIN7;
34760
+ let legendX = MARGIN7;
34025
34761
  for (const lg of legendGroups) {
34026
34762
  lg.x = legendX;
34027
34763
  lg.y = legendY;
@@ -34353,8 +35089,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34353
35089
  if (pt.y > maxY) maxY = pt.y;
34354
35090
  }
34355
35091
  }
34356
- const shiftX = MARGIN6 - minX;
34357
- const shiftY = MARGIN6 - minY;
35092
+ const shiftX = MARGIN7 - minX;
35093
+ const shiftY = MARGIN7 - minY;
34358
35094
  for (const node of nodes) {
34359
35095
  node.x += shiftX;
34360
35096
  node.y += shiftY;
@@ -34371,12 +35107,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
34371
35107
  pt.y += shiftY;
34372
35108
  }
34373
35109
  }
34374
- let totalWidth = maxX - minX + MARGIN6 * 2;
34375
- let totalHeight = maxY - minY + MARGIN6 * 2;
35110
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35111
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34376
35112
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34377
35113
  if (legendGroups.length > 0) {
34378
- const legendY = totalHeight + MARGIN6;
34379
- let legendX = MARGIN6;
35114
+ const legendY = totalHeight + MARGIN7;
35115
+ let legendX = MARGIN7;
34380
35116
  for (const lg of legendGroups) {
34381
35117
  lg.x = legendX;
34382
35118
  lg.y = legendY;
@@ -34768,8 +35504,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34768
35504
  if (pt.y > maxY) maxY = pt.y;
34769
35505
  }
34770
35506
  }
34771
- const shiftX = MARGIN6 - minX;
34772
- const shiftY = MARGIN6 - minY;
35507
+ const shiftX = MARGIN7 - minX;
35508
+ const shiftY = MARGIN7 - minY;
34773
35509
  for (const node of nodes) {
34774
35510
  node.x += shiftX;
34775
35511
  node.y += shiftY;
@@ -34786,12 +35522,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
34786
35522
  pt.y += shiftY;
34787
35523
  }
34788
35524
  }
34789
- let totalWidth = maxX - minX + MARGIN6 * 2;
34790
- let totalHeight = maxY - minY + MARGIN6 * 2;
35525
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35526
+ let totalHeight = maxY - minY + MARGIN7 * 2;
34791
35527
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
34792
35528
  if (legendGroups.length > 0) {
34793
- const legendY = totalHeight + MARGIN6;
34794
- let legendX = MARGIN6;
35529
+ const legendY = totalHeight + MARGIN7;
35530
+ let legendX = MARGIN7;
34795
35531
  for (const lg of legendGroups) {
34796
35532
  lg.x = legendX;
34797
35533
  lg.y = legendY;
@@ -35079,8 +35815,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
35079
35815
  if (pt.y > maxY) maxY = pt.y;
35080
35816
  }
35081
35817
  }
35082
- const shiftX = MARGIN6 - minX;
35083
- const shiftY = MARGIN6 - minY;
35818
+ const shiftX = MARGIN7 - minX;
35819
+ const shiftY = MARGIN7 - minY;
35084
35820
  for (const node of nodes) {
35085
35821
  node.x += shiftX;
35086
35822
  node.y += shiftY;
@@ -35095,12 +35831,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
35095
35831
  pt.y += shiftY;
35096
35832
  }
35097
35833
  }
35098
- let totalWidth = maxX - minX + MARGIN6 * 2;
35099
- let totalHeight = maxY - minY + MARGIN6 * 2;
35834
+ let totalWidth = maxX - minX + MARGIN7 * 2;
35835
+ let totalHeight = maxY - minY + MARGIN7 * 2;
35100
35836
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
35101
35837
  if (legendGroups.length > 0) {
35102
- const legendY = totalHeight + MARGIN6;
35103
- let legendX = MARGIN6;
35838
+ const legendY = totalHeight + MARGIN7;
35839
+ let legendX = MARGIN7;
35104
35840
  for (const lg of legendGroups) {
35105
35841
  lg.x = legendX;
35106
35842
  lg.y = legendY;
@@ -35120,10 +35856,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
35120
35856
  height: totalHeight
35121
35857
  };
35122
35858
  }
35123
- var import_dagre5, gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
35859
+ var import_dagre5, gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN7, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
35124
35860
  var init_layout8 = __esm({
35125
35861
  "src/c4/layout.ts"() {
35126
35862
  "use strict";
35863
+ init_tag_groups();
35127
35864
  import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
35128
35865
  init_legend_constants();
35129
35866
  init_text_measure();
@@ -35141,7 +35878,7 @@ var init_layout8 = __esm({
35141
35878
  CARD_H_PAD3 = 20;
35142
35879
  META_LINE_HEIGHT4 = 16;
35143
35880
  META_FONT_SIZE4 = 11;
35144
- MARGIN6 = 40;
35881
+ MARGIN7 = 40;
35145
35882
  BOUNDARY_PAD = 40;
35146
35883
  GROUP_BOUNDARY_PAD = 24;
35147
35884
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -35296,7 +36033,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
35296
36033
  for (const node of layout.nodes) {
35297
36034
  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);
35298
36035
  if (activeTagGroup) {
35299
- const tagKey = activeTagGroup.toLowerCase();
36036
+ const tagKey = tagAttrKey(activeTagGroup);
35300
36037
  const tagValue = node.metadata[tagKey];
35301
36038
  if (tagValue) {
35302
36039
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -35855,7 +36592,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35855
36592
  for (const node of layout.nodes) {
35856
36593
  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);
35857
36594
  if (activeTagGroup) {
35858
- const tagKey = activeTagGroup.toLowerCase();
36595
+ const tagKey = tagAttrKey(activeTagGroup);
35859
36596
  const tagValue = node.metadata[tagKey];
35860
36597
  if (tagValue) {
35861
36598
  nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
@@ -36119,6 +36856,7 @@ var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_F
36119
36856
  var init_renderer9 = __esm({
36120
36857
  "src/c4/renderer.ts"() {
36121
36858
  "use strict";
36859
+ init_tag_groups();
36122
36860
  d3Selection14 = __toESM(require("d3-selection"), 1);
36123
36861
  d3Shape6 = __toESM(require("d3-shape"), 1);
36124
36862
  init_fonts();
@@ -38183,7 +38921,7 @@ function separateGroups(groups, nodes, isLR, maxIterations = 20) {
38183
38921
  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);
38184
38922
  if (crossOverlap <= 0) continue;
38185
38923
  anyOverlap = true;
38186
- const shift = primaryOverlap + GROUP_GAP;
38924
+ const shift = primaryOverlap + GROUP_GAP2;
38187
38925
  const aCenter = isLR ? ga.y + ga.height / 2 : ga.x + ga.width / 2;
38188
38926
  const bCenter = isLR ? gb.y + gb.height / 2 : gb.x + gb.width / 2;
38189
38927
  const groupToShift = aCenter <= bCenter ? gb : ga;
@@ -38477,7 +39215,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
38477
39215
  height: totalHeight
38478
39216
  };
38479
39217
  }
38480
- var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
39218
+ var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP2;
38481
39219
  var init_layout10 = __esm({
38482
39220
  "src/infra/layout.ts"() {
38483
39221
  "use strict";
@@ -38533,7 +39271,7 @@ var init_layout10 = __esm({
38533
39271
  "retention-hours": "retention",
38534
39272
  partitions: "partitions"
38535
39273
  };
38536
- GROUP_GAP = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
39274
+ GROUP_GAP2 = GROUP_PADDING2 * 2 + GROUP_HEADER_HEIGHT;
38537
39275
  }
38538
39276
  });
38539
39277
 
@@ -39340,7 +40078,7 @@ function resolveActiveTagStroke(node, activeGroup, tagGroups, palette) {
39340
40078
  (t) => t.name.toLowerCase() === activeGroup.toLowerCase()
39341
40079
  );
39342
40080
  if (!tg) return null;
39343
- const tagKey = (tg.alias ?? tg.name).toLowerCase();
40081
+ const tagKey = tagAttrKey(tg.alias ?? tg.name);
39344
40082
  const tagVal = node.tags[tagKey];
39345
40083
  if (!tagVal) return null;
39346
40084
  const tv = tg.values.find(
@@ -39669,7 +40407,7 @@ function computeInfraLegendGroups(nodes, tagGroups, palette, edges) {
39669
40407
  groups.push({
39670
40408
  name: tg.name,
39671
40409
  type: "tag",
39672
- tagKey: (tg.alias ?? tg.name).toLowerCase(),
40410
+ tagKey: tagAttrKey(tg.alias ?? tg.name),
39673
40411
  entries,
39674
40412
  width: LEGEND_CAPSULE_PAD * 2 + pillWidth2 + 4 + entriesWidth2,
39675
40413
  minifiedWidth: pillWidth2
@@ -39717,7 +40455,7 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
39717
40455
  });
39718
40456
  legendG.selectAll("[data-legend-group]").classed("infra-legend-group", true);
39719
40457
  for (const group of legendGroups) {
39720
- const groupKey2 = group.name.toLowerCase();
40458
+ const groupKey2 = tagAttrKey(group.name);
39721
40459
  for (const entry of group.entries) {
39722
40460
  const entryEl = legendG.select(
39723
40461
  `[data-legend-group="${groupKey2}"] [data-legend-entry="${entry.value.toLowerCase()}"]`
@@ -39940,6 +40678,7 @@ var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6
39940
40678
  var init_renderer10 = __esm({
39941
40679
  "src/infra/renderer.ts"() {
39942
40680
  "use strict";
40681
+ init_tag_groups();
39943
40682
  d3Selection16 = __toESM(require("d3-selection"), 1);
39944
40683
  d3Shape8 = __toESM(require("d3-shape"), 1);
39945
40684
  init_fonts();
@@ -40038,17 +40777,17 @@ function mulberry32(seed) {
40038
40777
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
40039
40778
  };
40040
40779
  }
40041
- function standardNormal(rng3) {
40780
+ function standardNormal(rng4) {
40042
40781
  let u = 0;
40043
40782
  let v = 0;
40044
- while (u === 0) u = rng3();
40045
- while (v === 0) v = rng3();
40783
+ while (u === 0) u = rng4();
40784
+ while (v === 0) v = rng4();
40046
40785
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
40047
40786
  }
40048
- function gamma(shape, rng3) {
40787
+ function gamma(shape, rng4) {
40049
40788
  if (shape < 1) {
40050
- const u = rng3();
40051
- return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
40789
+ const u = rng4();
40790
+ return gamma(shape + 1, rng4) * Math.pow(u, 1 / shape);
40052
40791
  }
40053
40792
  const d = shape - 1 / 3;
40054
40793
  const c = 1 / Math.sqrt(9 * d);
@@ -40056,29 +40795,29 @@ function gamma(shape, rng3) {
40056
40795
  let x;
40057
40796
  let v;
40058
40797
  do {
40059
- x = standardNormal(rng3);
40798
+ x = standardNormal(rng4);
40060
40799
  v = 1 + c * x;
40061
40800
  } while (v <= 0);
40062
40801
  v = v * v * v;
40063
- const u = rng3();
40802
+ const u = rng4();
40064
40803
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
40065
40804
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
40066
40805
  }
40067
40806
  }
40068
- function sampleBeta(alpha, beta, rng3) {
40069
- const x = gamma(alpha, rng3);
40070
- const y = gamma(beta, rng3);
40807
+ function sampleBeta(alpha, beta, rng4) {
40808
+ const x = gamma(alpha, rng4);
40809
+ const y = gamma(beta, rng4);
40071
40810
  return x / (x + y);
40072
40811
  }
40073
- function sampleBetaPert(o, m, p, rng3) {
40812
+ function sampleBetaPert(o, m, p, rng4) {
40074
40813
  if (o === p) return m;
40075
40814
  const range = p - o;
40076
40815
  const alpha = 1 + 4 * (m - o) / range;
40077
40816
  const beta = 1 + 4 * (p - m) / range;
40078
- return o + sampleBeta(alpha, beta, rng3) * range;
40817
+ return o + sampleBeta(alpha, beta, rng4) * range;
40079
40818
  }
40080
40819
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
40081
- const rng3 = mulberry32(opts.seed);
40820
+ const rng4 = mulberry32(opts.seed);
40082
40821
  const expById = /* @__PURE__ */ new Map();
40083
40822
  for (const e of expanded) expById.set(e.id, e);
40084
40823
  const completionTimes = new Array(opts.trials);
@@ -40101,7 +40840,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
40101
40840
  } else if (exp.o === exp.p) {
40102
40841
  duration = exp.m;
40103
40842
  } else {
40104
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
40843
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng4);
40105
40844
  }
40106
40845
  let esLower = 0;
40107
40846
  let efLower = -Infinity;
@@ -44389,7 +45128,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44389
45128
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(containerWidth, idealWidth);
44390
45129
  const sBarH = ctx.structural(BAR_H);
44391
45130
  const sRowGap = ctx.structural(ROW_GAP);
44392
- const sGroupGap = ctx.structural(GROUP_GAP2);
45131
+ const sGroupGap = ctx.structural(GROUP_GAP3);
44393
45132
  const sMilestoneSize = ctx.structural(MILESTONE_SIZE);
44394
45133
  const sBottomMargin = ctx.aesthetic(BOTTOM_MARGIN);
44395
45134
  const sRightMargin = ctx.aesthetic(RIGHT_MARGIN);
@@ -44561,7 +45300,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
44561
45300
  (tg) => tg.name.toLowerCase() === currentSwimlaneGroup.toLowerCase()
44562
45301
  );
44563
45302
  if (tagGroup) {
44564
- const tagKey = tagGroup.name.toLowerCase();
45303
+ const tagKey = tagAttrKey(tagGroup.name);
44565
45304
  for (const rt of resolved.tasks) {
44566
45305
  let value = rt.effectiveMetadata[tagKey];
44567
45306
  if (!value && tagGroup.defaultValue) value = tagGroup.defaultValue;
@@ -45302,7 +46041,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45302
46041
  const usedValues = /* @__PURE__ */ new Map();
45303
46042
  if (resolvedTasks) {
45304
46043
  for (const group of visibleGroups) {
45305
- const key = group.name.toLowerCase();
46044
+ const key = tagAttrKey(group.name);
45306
46045
  const used = /* @__PURE__ */ new Set();
45307
46046
  for (const rt of resolvedTasks) {
45308
46047
  const val = rt.effectiveMetadata[key];
@@ -45313,7 +46052,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45313
46052
  }
45314
46053
  const filteredEntries = /* @__PURE__ */ new Map();
45315
46054
  for (const group of visibleGroups) {
45316
- const key = group.name.toLowerCase();
46055
+ const key = tagAttrKey(group.name);
45317
46056
  const used = usedValues.get(key);
45318
46057
  if (used && used.size > 0) {
45319
46058
  filteredEntries.set(
@@ -45334,7 +46073,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45334
46073
  const pillW = measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) + LEGEND_PILL_PAD + iconReserve;
45335
46074
  let groupW = pillW;
45336
46075
  if (isActive) {
45337
- const entries = filteredEntries.get(group.name.toLowerCase()) ?? group.entries;
46076
+ const entries = filteredEntries.get(tagAttrKey(group.name)) ?? group.entries;
45338
46077
  let entriesW = 0;
45339
46078
  for (const entry of entries) {
45340
46079
  entriesW += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + measureLegendText(entry.value, LEGEND_ENTRY_FONT_SIZE) + LEGEND_ENTRY_TRAIL;
@@ -45391,7 +46130,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
45391
46130
  },
45392
46131
  ...onControlsToggle !== void 0 && { onControlsToggle },
45393
46132
  onEntryHover: (groupName, entryValue) => {
45394
- const tagKey = groupName.toLowerCase();
46133
+ const tagKey = tagAttrKey(groupName);
45395
46134
  if (entryValue) {
45396
46135
  const ev = entryValue.toLowerCase();
45397
46136
  chartG.selectAll(".gantt-task").each(function() {
@@ -46081,7 +46820,7 @@ function buildTagLaneRowList(resolved, swimlaneGroup, collapsedLanes) {
46081
46820
  (g) => g.name.toLowerCase() === swimlaneGroup.toLowerCase()
46082
46821
  );
46083
46822
  if (!tagGroup) return null;
46084
- const tagKey = tagGroup.name.toLowerCase();
46823
+ const tagKey = tagAttrKey(tagGroup.name);
46085
46824
  const rows = [];
46086
46825
  const buckets = /* @__PURE__ */ new Map();
46087
46826
  const unbucketed = [];
@@ -46265,10 +47004,11 @@ function renderTimeScaleHorizontal(g, scale, _innerWidth, innerHeight, textColor
46265
47004
  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);
46266
47005
  }
46267
47006
  }
46268
- var d3Scale5, d3Selection18, BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
47007
+ var d3Scale5, d3Selection18, BAR_H, ROW_GAP, GROUP_GAP3, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, LABEL_PAD2, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, SPRINT_BAND_OPACITY, SPRINT_HOVER_OPACITY, SPRINT_BOUNDARY_OPACITY, FADE_OPACITY;
46269
47008
  var init_renderer12 = __esm({
46270
47009
  "src/gantt/renderer.ts"() {
46271
47010
  "use strict";
47011
+ init_tag_groups();
46272
47012
  d3Scale5 = __toESM(require("d3-scale"), 1);
46273
47013
  d3Selection18 = __toESM(require("d3-selection"), 1);
46274
47014
  init_fonts();
@@ -46285,7 +47025,7 @@ var init_renderer12 = __esm({
46285
47025
  init_title_constants();
46286
47026
  BAR_H = 22;
46287
47027
  ROW_GAP = 6;
46288
- GROUP_GAP2 = 14;
47028
+ GROUP_GAP3 = 14;
46289
47029
  MILESTONE_SIZE = 10;
46290
47030
  MIN_LEFT_MARGIN = 120;
46291
47031
  BOTTOM_MARGIN = 40;
@@ -48578,7 +49318,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48578
49318
  const panelWidth = 280;
48579
49319
  const textAreaWidth = panelWidth - silhouetteZone - CARD_PADDING_X3;
48580
49320
  const descLineH = 14;
48581
- const descLines = parsed.persona.description ? wrapText2(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
49321
+ const descLines = parsed.persona.description ? wrapNoteBody(parsed.persona.description, textAreaWidth, FONT_SIZE_META) : [];
48582
49322
  const descRowH = descLines.length > 0 ? descLines.length * descLineH + 8 : 0;
48583
49323
  const panelHeight = titleRowH + descRowH;
48584
49324
  const panelX = layout.totalWidth - PADDING2 - panelWidth;
@@ -48603,8 +49343,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48603
49343
  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);
48604
49344
  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);
48605
49345
  for (let li = 0; li < descLines.length; li++) {
48606
- const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
48607
- renderInlineText(lineEl, descLines[li], palette, FONT_SIZE_META);
49346
+ const line11 = descLines[li];
49347
+ const indent = line11.kind === "plain" ? 0 : NOTE_BULLET_INDENT;
49348
+ const lineY = panelY + titleRowH + descLineH * (li + 1);
49349
+ if (line11.kind === "bullet-first") {
49350
+ personaG.append("text").attr("x", textX).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText).text("\u2022");
49351
+ }
49352
+ const lineEl = personaG.append("text").attr("x", textX + indent).attr("y", lineY).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
49353
+ renderInlineText(lineEl, line11.text, palette, FONT_SIZE_META);
48608
49354
  }
48609
49355
  if (onNavigateToLine) {
48610
49356
  personaG.style("cursor", "pointer").on("click", () => {
@@ -48643,7 +49389,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
48643
49389
  return;
48644
49390
  }
48645
49391
  const isScore = groupName === "Score";
48646
- const attrName = isScore ? "data-score" : `data-tag-${groupName.toLowerCase()}`;
49392
+ const attrName = isScore ? "data-score" : `data-tag-${tagAttrKey(groupName)}`;
48647
49393
  const matches = (el) => el.getAttribute(attrName) === entryValue;
48648
49394
  svg.selectAll(".journey-step").each(function() {
48649
49395
  const hit = matches(this);
@@ -49124,7 +49870,7 @@ function resolveStepColor(step, scoreColor, activeGroup, tagGroups, _palette) {
49124
49870
  (g) => g.name.toLowerCase() === activeGroup.toLowerCase()
49125
49871
  );
49126
49872
  if (!group) return void 0;
49127
- const tagValue = step.tags[group.name.toLowerCase()];
49873
+ const tagValue = step.tags[tagAttrKey(group.name)];
49128
49874
  if (!tagValue) return void 0;
49129
49875
  const entry = group.entries.find(
49130
49876
  (e) => e.value.toLowerCase() === tagValue.toLowerCase()
@@ -49339,6 +50085,7 @@ var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3
49339
50085
  var init_renderer14 = __esm({
49340
50086
  "src/journey-map/renderer.ts"() {
49341
50087
  "use strict";
50088
+ init_tag_groups();
49342
50089
  d3 = __toESM(require("d3-selection"), 1);
49343
50090
  d3Shape10 = __toESM(require("d3-shape"), 1);
49344
50091
  init_color_utils();
@@ -49346,6 +50093,7 @@ var init_renderer14 = __esm({
49346
50093
  init_parser23();
49347
50094
  init_layout13();
49348
50095
  init_inline_markdown();
50096
+ init_note_box();
49349
50097
  init_legend_integration();
49350
50098
  init_tag_groups();
49351
50099
  init_scaling();
@@ -52169,7 +52917,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52169
52917
  (g) => g.name.toLowerCase() === groupName.toLowerCase()
52170
52918
  );
52171
52919
  if (!group) return null;
52172
- const val = tags[group.name.toLowerCase()] ?? group.defaultValue;
52920
+ const val = tags[tagAttrKey(group.name)] ?? group.defaultValue;
52173
52921
  if (!val) return null;
52174
52922
  const entry = group.entries.find(
52175
52923
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52783,7 +53531,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52783
53531
  if (directHex)
52784
53532
  return { fill: directHex, stroke: mix(directHex, palette.text, 18) };
52785
53533
  for (const group of resolved.tagGroups) {
52786
- const val = p.tags[group.name.toLowerCase()];
53534
+ const val = p.tags[tagAttrKey(group.name)];
52787
53535
  if (!val) continue;
52788
53536
  const entry = group.entries.find(
52789
53537
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -52798,7 +53546,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52798
53546
  };
52799
53547
  const lineColor = (tags) => {
52800
53548
  for (const group of resolved.tagGroups) {
52801
- const val = tags[group.name.toLowerCase()];
53549
+ const val = tags[tagAttrKey(group.name)];
52802
53550
  if (!val) continue;
52803
53551
  const entry = group.entries.find(
52804
53552
  (e) => e.value.toLowerCase() === val.toLowerCase()
@@ -54064,6 +54812,7 @@ var import_d3_geo2, import_topojson_client2, FIT_PAD, PATH_DIGITS, THIN_TOL, ALB
54064
54812
  var init_layout15 = __esm({
54065
54813
  "src/map/layout.ts"() {
54066
54814
  "use strict";
54815
+ init_tag_groups();
54067
54816
  import_d3_geo2 = require("d3-geo");
54068
54817
  import_topojson_client2 = require("topojson-client");
54069
54818
  init_color_utils();
@@ -56480,7 +57229,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56480
57229
  }
56481
57230
  return result;
56482
57231
  }
56483
- const groupKey2 = group.name.toLowerCase();
57232
+ const groupKey2 = tagAttrKey(group.name);
56484
57233
  const participantMeta = /* @__PURE__ */ new Map();
56485
57234
  for (const p of parsed.participants) {
56486
57235
  participantMeta.set(p.id, { ...p.metadata || {} });
@@ -56516,6 +57265,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
56516
57265
  var init_tag_resolution = __esm({
56517
57266
  "src/sequence/tag-resolution.ts"() {
56518
57267
  "use strict";
57268
+ init_tag_groups();
56519
57269
  }
56520
57270
  });
56521
57271
 
@@ -56990,7 +57740,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56990
57740
  }
56991
57741
  }
56992
57742
  const getTagColor = (value) => value ? tagValueToColor.get(value.toLowerCase()) : void 0;
56993
- const tagKey = activeTagGroup?.toLowerCase();
57743
+ const tagKey = activeTagGroup ? tagAttrKey(activeTagGroup) : void 0;
56994
57744
  const hiddenMsgIndices = /* @__PURE__ */ new Set();
56995
57745
  if (collapsedSections && collapsedSections.size > 0) {
56996
57746
  const sectionGroups = groupMessagesBySection(elements, messages);
@@ -57582,11 +58332,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57582
58332
  const boxH = sBoxH + GROUP_PADDING_TOP2 + GROUP_PADDING_BOTTOM2;
57583
58333
  const groupTagValue = tagKey && group.metadata?.[tagKey];
57584
58334
  const groupTagColor = getTagColor(groupTagValue || void 0);
57585
- const fillColor = groupTagColor ? mix(
57586
- groupTagColor,
57587
- themeBaseBg(palette, isDark),
57588
- isDark ? 15 : 20
57589
- ) : isDark ? palette.surface : palette.bg;
58335
+ const fillColor = groupTagColor ? mix(groupTagColor, themeBaseBg(palette, isDark), isDark ? 15 : 20) : isDark ? palette.surface : palette.bg;
57590
58336
  const strokeColor = groupTagColor || palette.textMuted;
57591
58337
  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");
57592
58338
  groupG.append("title").text("Click to collapse");
@@ -57636,11 +58382,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57636
58382
  );
57637
58383
  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");
57638
58384
  participantG.append("title").text("Click to expand");
57639
- const pFill = effectiveTagColor ? mix(
57640
- effectiveTagColor,
57641
- themeBaseBg(palette, isDark),
57642
- isDark ? 30 : 40
57643
- ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
58385
+ const pFill = effectiveTagColor ? mix(effectiveTagColor, themeBaseBg(palette, isDark), isDark ? 30 : 40) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
57644
58386
  const pStroke = effectiveTagColor || palette.border;
57645
58387
  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);
57646
58388
  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);
@@ -58136,6 +58878,7 @@ var d3Selection27, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGH
58136
58878
  var init_renderer20 = __esm({
58137
58879
  "src/sequence/renderer.ts"() {
58138
58880
  "use strict";
58881
+ init_tag_groups();
58139
58882
  d3Selection27 = __toESM(require("d3-selection"), 1);
58140
58883
  init_color_utils();
58141
58884
  init_inline_markdown();
@@ -58889,6 +59632,7 @@ __export(advanced_exports, {
58889
59632
  resolveTaskName: () => resolveTaskName,
58890
59633
  rollUpContextRelationships: () => rollUpContextRelationships,
58891
59634
  sampleBetaPert: () => sampleBetaPert,
59635
+ searchMapLocations: () => searchMapLocations,
58892
59636
  seriesColors: () => seriesColors,
58893
59637
  shade: () => shade,
58894
59638
  shapeFill: () => shapeFill,
@@ -59562,6 +60306,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
59562
60306
  init_parser3();
59563
60307
 
59564
60308
  // src/timeline/renderer.ts
60309
+ init_tag_groups();
59565
60310
  var d3Scale3 = __toESM(require("d3-scale"), 1);
59566
60311
  var d3Selection3 = __toESM(require("d3-selection"), 1);
59567
60312
  init_fonts();
@@ -59826,7 +60571,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
59826
60571
  });
59827
60572
  let tagLanes = null;
59828
60573
  if (resolvedSwimlaneTG) {
59829
- const tagKey = resolvedSwimlaneTG.toLowerCase();
60574
+ const tagKey = tagAttrKey(resolvedSwimlaneTG);
59830
60575
  const tagGroup = parsed.timelineTagGroups.find(
59831
60576
  (g) => g.name.toLowerCase() === tagKey
59832
60577
  );
@@ -60132,7 +60877,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
60132
60877
  onTagStateChange?.(currentActiveGroup, currentSwimlaneGroup);
60133
60878
  },
60134
60879
  onEntryHover: (groupName, entryValue) => {
60135
- const tagKey = groupName.toLowerCase();
60880
+ const tagKey = tagAttrKey(groupName);
60136
60881
  if (entryValue) {
60137
60882
  const tagVal = entryValue.toLowerCase();
60138
60883
  fadeToTagValue(mainG, tagKey, tagVal);
@@ -60151,7 +60896,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
60151
60896
  }
60152
60897
  },
60153
60898
  onGroupRendered: (groupName, groupEl, isActive) => {
60154
- const groupKey2 = groupName.toLowerCase();
60899
+ const groupKey2 = tagAttrKey(groupName);
60155
60900
  groupEl.attr("data-tag-group", groupKey2);
60156
60901
  if (isActive && !viewMode) {
60157
60902
  const isSwimActive = currentSwimlaneGroup?.toLowerCase() === groupKey2;
@@ -64016,6 +64761,71 @@ function completeMapPlaces(query, gazetteer, opts) {
64016
64761
  });
64017
64762
  return [...cityItems, ...airportItems].slice(0, limit);
64018
64763
  }
64764
+ function searchMapLocations(query, gazetteer, opts) {
64765
+ const q = fold2(query);
64766
+ if (!q) return [];
64767
+ const limit = opts?.limit ?? 20;
64768
+ const rankOf = (hay) => hay === q ? 0 : hay.startsWith(q) ? 1 : hay.includes(q) ? 2 : -1;
64769
+ const cityRank = /* @__PURE__ */ new Map();
64770
+ const consider = (idx, rank) => {
64771
+ if (rank < 0) return;
64772
+ const prev = cityRank.get(idx);
64773
+ if (prev === void 0 || rank < prev) cityRank.set(idx, rank);
64774
+ };
64775
+ for (const [key, list] of Object.entries(gazetteer.byName)) {
64776
+ const r = rankOf(key);
64777
+ if (r >= 0) for (const i of list) consider(i, r);
64778
+ }
64779
+ for (const [key, idx] of Object.entries(gazetteer.alt)) {
64780
+ consider(idx, rankOf(key));
64781
+ }
64782
+ const cityItems = [...cityRank].filter(([i]) => gazetteer.cities[i] !== void 0).map(([i, rank]) => {
64783
+ const c = gazetteer.cities[i];
64784
+ const [, , iso, pop, name, sub] = c;
64785
+ const ambiguous = (gazetteer.byName[fold2(name)]?.length ?? 0) > 1;
64786
+ const qualifier = sub ?? iso;
64787
+ return {
64788
+ name,
64789
+ token: ambiguous ? `${name} ${qualifier}` : name,
64790
+ kind: "city",
64791
+ iso,
64792
+ ...sub !== void 0 && { sub },
64793
+ pop,
64794
+ detail: `${qualifier} \xB7 ${groupThousands(pop)}`,
64795
+ _rank: rank
64796
+ };
64797
+ });
64798
+ const airports = opts?.airports;
64799
+ const airportItems = [];
64800
+ if (airports?.airportIata) {
64801
+ for (const [code, idx] of Object.entries(airports.airportIata)) {
64802
+ const a = airports.airports[idx];
64803
+ if (!a) continue;
64804
+ const codeRank = rankOf(code);
64805
+ const nameRank = rankOf(fold2(a[4]));
64806
+ const rank = Math.min(
64807
+ codeRank < 0 ? 99 : codeRank,
64808
+ nameRank < 0 ? 99 : nameRank
64809
+ );
64810
+ if (rank > 2) continue;
64811
+ airportItems.push({
64812
+ name: a[4],
64813
+ token: code.toUpperCase(),
64814
+ kind: "airport",
64815
+ iso: a[2],
64816
+ pop: 0,
64817
+ detail: `Airport \xB7 ${a[4]}`,
64818
+ _rank: rank
64819
+ });
64820
+ }
64821
+ }
64822
+ return [...cityItems, ...airportItems].sort(
64823
+ (x, y) => x._rank - y._rank || // best match tier first
64824
+ (x.kind === y.kind ? 0 : x.kind === "city" ? -1 : 1) || // city before airport
64825
+ y.pop - x.pop || // populous city first
64826
+ (x.token < y.token ? -1 : x.token > y.token ? 1 : 0)
64827
+ ).slice(0, limit).map(({ _rank, ...rest }) => rest);
64828
+ }
64019
64829
  function completeMapRegions(query, regions, opts) {
64020
64830
  const q = fold2(query);
64021
64831
  if (!q) return [];
@@ -65234,6 +66044,7 @@ function formatLineDiff(path, original, migrated) {
65234
66044
  resolveTaskName,
65235
66045
  rollUpContextRelationships,
65236
66046
  sampleBetaPert,
66047
+ searchMapLocations,
65237
66048
  seriesColors,
65238
66049
  shade,
65239
66050
  shapeFill,