@diagrammo/dgmo 0.31.0 → 0.32.1

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 (72) hide show
  1. package/.cursorrules +4 -1
  2. package/.github/copilot-instructions.md +4 -1
  3. package/.windsurfrules +4 -1
  4. package/SKILL.md +4 -1
  5. package/dist/advanced.cjs +1297 -358
  6. package/dist/advanced.d.cts +117 -15
  7. package/dist/advanced.d.ts +117 -15
  8. package/dist/advanced.js +1291 -358
  9. package/dist/auto.cjs +1087 -316
  10. package/dist/auto.js +98 -98
  11. package/dist/auto.mjs +1087 -316
  12. package/dist/cli.cjs +140 -140
  13. package/dist/index.cjs +1090 -397
  14. package/dist/index.js +1090 -397
  15. package/docs/ai-integration.md +4 -1
  16. package/docs/language-reference.md +282 -27
  17. package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
  18. package/gallery/fixtures/c4-full.dgmo +4 -5
  19. package/gallery/fixtures/c4.dgmo +2 -3
  20. package/package.json +7 -1
  21. package/src/advanced.ts +7 -0
  22. package/src/boxes-and-lines/focus.ts +257 -0
  23. package/src/boxes-and-lines/layout-search.ts +131 -65
  24. package/src/boxes-and-lines/layout.ts +7 -1
  25. package/src/boxes-and-lines/parser.ts +19 -4
  26. package/src/boxes-and-lines/renderer.ts +54 -3
  27. package/src/c4/parser.ts +8 -7
  28. package/src/chart-type-registry.ts +129 -4
  29. package/src/chart-types.ts +4 -4
  30. package/src/chart.ts +18 -1
  31. package/src/colors.ts +225 -2
  32. package/src/cycle/parser.ts +2 -7
  33. package/src/d3.ts +67 -54
  34. package/src/diagnostics.ts +17 -0
  35. package/src/dimensions.ts +9 -13
  36. package/src/echarts.ts +42 -14
  37. package/src/er/parser.ts +6 -1
  38. package/src/gantt/parser.ts +44 -7
  39. package/src/graph/flowchart-parser.ts +77 -3
  40. package/src/graph/state-renderer.ts +2 -2
  41. package/src/infra/parser.ts +80 -0
  42. package/src/journey-map/parser.ts +8 -7
  43. package/src/kanban/parser.ts +8 -7
  44. package/src/map/context-labels.ts +134 -27
  45. package/src/map/geo.ts +10 -2
  46. package/src/map/layout.ts +259 -4
  47. package/src/map/parser.ts +2 -0
  48. package/src/map/renderer.ts +22 -11
  49. package/src/map/resolver.ts +68 -19
  50. package/src/mindmap/parser.ts +15 -7
  51. package/src/mindmap/renderer.ts +50 -12
  52. package/src/org/parser.ts +8 -7
  53. package/src/org/renderer.ts +22 -7
  54. package/src/palettes/color-utils.ts +12 -2
  55. package/src/palettes/index.ts +1 -0
  56. package/src/pert/renderer.ts +2 -2
  57. package/src/pyramid/parser.ts +2 -7
  58. package/src/quadrant/renderer.ts +2 -2
  59. package/src/raci/parser.ts +2 -7
  60. package/src/raci/renderer.ts +4 -4
  61. package/src/ring/parser.ts +2 -7
  62. package/src/sequence/parser.ts +18 -7
  63. package/src/sequence/renderer.ts +4 -4
  64. package/src/sitemap/parser.ts +8 -7
  65. package/src/sitemap/renderer.ts +2 -2
  66. package/src/tech-radar/parser.ts +2 -7
  67. package/src/timeline/renderer.ts +15 -5
  68. package/src/utils/parsing.ts +13 -1
  69. package/src/utils/scaling.ts +38 -81
  70. package/src/utils/tag-groups.ts +38 -0
  71. package/src/visualizations/parse.ts +6 -1
  72. package/src/wireframe/parser.ts +6 -1
package/dist/advanced.js CHANGED
@@ -38,6 +38,14 @@ function makeDgmoError(line11, message, severity = "error", code) {
38
38
  function formatDgmoError(err) {
39
39
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
40
40
  }
41
+ function makeFail(result) {
42
+ return (line11, message) => {
43
+ const diag = makeDgmoError(line11, message);
44
+ result.diagnostics.push(diag);
45
+ result.error = formatDgmoError(diag);
46
+ return result;
47
+ };
48
+ }
41
49
  function levenshtein(a, b) {
42
50
  const m = a.length;
43
51
  const n = b.length;
@@ -401,21 +409,99 @@ function resolveColor(color, palette) {
401
409
  }
402
410
  return colorNames[lower] ?? null;
403
411
  }
412
+ function nearestNamedColor(input) {
413
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
414
+ if (cssHex) input = cssHex;
415
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
416
+ if (!m) return null;
417
+ let h = m[1].toLowerCase();
418
+ if (h.length === 3)
419
+ h = h.split("").map((c) => c + c).join("");
420
+ const r = parseInt(h.slice(0, 2), 16) / 255;
421
+ const g = parseInt(h.slice(2, 4), 16) / 255;
422
+ const b = parseInt(h.slice(4, 6), 16) / 255;
423
+ const max = Math.max(r, g, b);
424
+ const min = Math.min(r, g, b);
425
+ const delta = max - min;
426
+ const l = (max + min) / 2;
427
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
428
+ if (s < 0.15) {
429
+ if (l < 0.2) return "black";
430
+ if (l > 0.85) return "white";
431
+ return "gray";
432
+ }
433
+ let hue;
434
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
435
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
436
+ else hue = 60 * ((r - g) / delta + 4);
437
+ if (hue < 0) hue += 360;
438
+ const anchors = [
439
+ ["red", 0],
440
+ ["orange", 30],
441
+ ["yellow", 55],
442
+ ["green", 120],
443
+ ["teal", 170],
444
+ ["cyan", 190],
445
+ ["blue", 225],
446
+ ["purple", 285],
447
+ ["red", 360]
448
+ ];
449
+ let best = "red";
450
+ let bestD = Infinity;
451
+ for (const [name, deg] of anchors) {
452
+ const d = Math.abs(hue - deg);
453
+ if (d < bestD) {
454
+ bestD = d;
455
+ best = name;
456
+ }
457
+ }
458
+ return best;
459
+ }
460
+ function isInvalidColorToken(token) {
461
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
462
+ const lower = token.toLowerCase();
463
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
464
+ }
465
+ function invalidColorDiagnostic(token, line11) {
466
+ if (!isInvalidColorToken(token)) return null;
467
+ const nearest = nearestNamedColor(token);
468
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
469
+ return makeDgmoError(
470
+ line11,
471
+ `Color "${token}" is not a valid DGMO color \u2014 DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${near}`,
472
+ "warning",
473
+ INVALID_COLOR_CODE
474
+ );
475
+ }
404
476
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
405
477
  const resolved = resolveColor(color, palette);
406
478
  if (resolved !== null) return resolved;
479
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
480
+ const nearest = nearestNamedColor(color);
481
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
482
+ diagnostics.push(
483
+ makeDgmoError(
484
+ line11,
485
+ `Color "${color}" is not supported \u2014 DGMO does not accept hex or CSS color values. Use a named palette color: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${near}`,
486
+ "error",
487
+ INVALID_COLOR_CODE
488
+ )
489
+ );
490
+ return void 0;
491
+ }
407
492
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
408
493
  const suggestion = hint ? ` ${hint}` : "";
409
494
  diagnostics.push(
410
495
  makeDgmoError(
411
496
  line11,
412
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
413
- "warning"
497
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
498
+ "warning",
499
+ INVALID_COLOR_CODE
414
500
  )
415
501
  );
416
502
  return void 0;
417
503
  }
418
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, seriesColors;
504
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
419
505
  var init_colors = __esm({
420
506
  "src/colors.ts"() {
421
507
  "use strict";
@@ -483,6 +569,92 @@ var init_colors = __esm({
483
569
  "orange",
484
570
  "cyan"
485
571
  ]);
572
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
573
+ INVALID_CSS_COLOR_HEX = Object.freeze({
574
+ pink: "#ffc0cb",
575
+ hotpink: "#ff69b4",
576
+ deeppink: "#ff1493",
577
+ lightpink: "#ffb6c1",
578
+ palevioletred: "#db7093",
579
+ crimson: "#dc143c",
580
+ scarlet: "#ff2400",
581
+ firebrick: "#b22222",
582
+ darkred: "#8b0000",
583
+ maroon: "#800000",
584
+ salmon: "#fa8072",
585
+ lightsalmon: "#ffa07a",
586
+ darksalmon: "#e9967a",
587
+ coral: "#ff7f50",
588
+ lightcoral: "#f08080",
589
+ tomato: "#ff6347",
590
+ orangered: "#ff4500",
591
+ darkorange: "#ff8c00",
592
+ gold: "#ffd700",
593
+ goldenrod: "#daa520",
594
+ darkgoldenrod: "#b8860b",
595
+ khaki: "#f0e68c",
596
+ darkkhaki: "#bdb76b",
597
+ amber: "#ffbf00",
598
+ lavender: "#e6e6fa",
599
+ violet: "#ee82ee",
600
+ magenta: "#ff00ff",
601
+ fuchsia: "#ff00ff",
602
+ orchid: "#da70d6",
603
+ plum: "#dda0dd",
604
+ indigo: "#4b0082",
605
+ navy: "#000080",
606
+ midnightblue: "#191970",
607
+ darkblue: "#00008b",
608
+ mediumblue: "#0000cd",
609
+ royalblue: "#4169e1",
610
+ cornflowerblue: "#6495ed",
611
+ dodgerblue: "#1e90ff",
612
+ deepskyblue: "#00bfff",
613
+ skyblue: "#87ceeb",
614
+ lightskyblue: "#87cefa",
615
+ lightblue: "#add8e6",
616
+ powderblue: "#b0e0e6",
617
+ steelblue: "#4682b4",
618
+ slateblue: "#6a5acd",
619
+ cadetblue: "#5f9ea0",
620
+ turquoise: "#40e0d0",
621
+ aqua: "#00ffff",
622
+ aquamarine: "#7fffd4",
623
+ lime: "#00ff00",
624
+ limegreen: "#32cd32",
625
+ lightgreen: "#90ee90",
626
+ palegreen: "#98fb98",
627
+ seagreen: "#2e8b57",
628
+ mediumseagreen: "#3cb371",
629
+ forestgreen: "#228b22",
630
+ darkgreen: "#006400",
631
+ olive: "#808000",
632
+ olivedrab: "#6b8e23",
633
+ darkolivegreen: "#556b2f",
634
+ chartreuse: "#7fff00",
635
+ lawngreen: "#7cfc00",
636
+ springgreen: "#00ff7f",
637
+ greenyellow: "#adff2f",
638
+ brown: "#a52a2a",
639
+ sienna: "#a0522d",
640
+ chocolate: "#d2691e",
641
+ peru: "#cd853f",
642
+ tan: "#d2b48c",
643
+ beige: "#f5f5dc",
644
+ wheat: "#f5deb3",
645
+ ivory: "#fffff0",
646
+ silver: "#c0c0c0",
647
+ lightgray: "#d3d3d3",
648
+ lightgrey: "#d3d3d3",
649
+ darkgray: "#a9a9a9",
650
+ darkgrey: "#a9a9a9",
651
+ dimgray: "#696969",
652
+ dimgrey: "#696969",
653
+ slategray: "#708090",
654
+ slategrey: "#708090",
655
+ gainsboro: "#dcdcdc",
656
+ grey: "#808080"
657
+ });
486
658
  seriesColors = [
487
659
  nord.nord10,
488
660
  // blue
@@ -657,7 +829,13 @@ function extractColor(label, palette, diagnostics, line11) {
657
829
  );
658
830
  if (lastSpaceIdx < 0) return { label };
659
831
  const trailing = label.substring(lastSpaceIdx + 1);
660
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
832
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
833
+ if (diagnostics && line11 !== void 0) {
834
+ const diag = invalidColorDiagnostic(trailing, line11);
835
+ if (diag) diagnostics.push(diag);
836
+ }
837
+ return { label };
838
+ }
661
839
  let color;
662
840
  if (diagnostics && line11 !== void 0) {
663
841
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1281,6 +1459,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1281
1459
  }
1282
1460
  }
1283
1461
  }
1462
+ function cascadeTagMetadata(roots, tagGroups) {
1463
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1464
+ if (keys.length === 0) return;
1465
+ const walk = (node, inherited) => {
1466
+ const childInherited = { ...inherited };
1467
+ for (const key of keys) {
1468
+ const own = node.metadata[key];
1469
+ if (own) {
1470
+ childInherited[key] = own;
1471
+ } else if (inherited[key]) {
1472
+ node.metadata[key] = inherited[key];
1473
+ }
1474
+ }
1475
+ for (const child of node.children) walk(child, childInherited);
1476
+ };
1477
+ for (const root of roots) walk(root, {});
1478
+ }
1284
1479
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1285
1480
  const defaults = [];
1286
1481
  for (const group of tagGroups) {
@@ -1819,7 +2014,12 @@ function parseVisualizationFull(content, palette) {
1819
2014
  }
1820
2015
  if (currentTimelineTagGroup && indent > 0) {
1821
2016
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1822
- const { label, color } = extractColor(entryText, palette);
2017
+ const { label, color } = extractColor(
2018
+ entryText,
2019
+ palette,
2020
+ result.diagnostics,
2021
+ lineNumber
2022
+ );
1823
2023
  if (color) {
1824
2024
  if (isDefault) {
1825
2025
  currentTimelineTagGroup.defaultValue = label;
@@ -3120,9 +3320,12 @@ function contrastText(bg, lightText, darkText) {
3120
3320
  }
3121
3321
  return lightText;
3122
3322
  }
3323
+ function themeBaseBg(palette, isDark) {
3324
+ return isDark ? palette.surface : palette.bg;
3325
+ }
3123
3326
  function shapeFill(palette, intent, isDark, opts) {
3124
3327
  if (opts?.solid) return intent;
3125
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3328
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3126
3329
  }
3127
3330
  function getSeriesColors(palette) {
3128
3331
  const c = palette.colors;
@@ -3153,7 +3356,7 @@ function getSegmentColors(palette, count) {
3153
3356
  }
3154
3357
  function politicalTints(palette, count, isDark) {
3155
3358
  if (count <= 0) return [];
3156
- const base = isDark ? palette.surface : palette.bg;
3359
+ const base = themeBaseBg(palette, isDark);
3157
3360
  const c = palette.colors;
3158
3361
  const swatches = [
3159
3362
  .../* @__PURE__ */ new Set([
@@ -3963,6 +4166,7 @@ __export(palettes_exports, {
3963
4166
  shade: () => shade,
3964
4167
  shapeFill: () => shapeFill,
3965
4168
  slatePalette: () => slatePalette,
4169
+ themeBaseBg: () => themeBaseBg,
3966
4170
  tidewaterPalette: () => tidewaterPalette,
3967
4171
  tint: () => tint,
3968
4172
  tokyoNightPalette: () => tokyoNightPalette
@@ -4196,6 +4400,30 @@ var init_scaling = __esm({
4196
4400
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4197
4401
  return new _ScaleContext(clamped, minScaleFactor);
4198
4402
  }
4403
+ /**
4404
+ * Fit content into a bounding box, scaling by whichever dimension is more
4405
+ * constraining (the smaller of the width- and height-fit ratios) so the
4406
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4407
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4408
+ * never shrunk past the readability floor).
4409
+ */
4410
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4411
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4412
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4413
+ const raw = Math.min(wRaw, hRaw);
4414
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4415
+ return new _ScaleContext(clamped, minScaleFactor);
4416
+ }
4417
+ /**
4418
+ * Build a context from an explicit raw factor (clamped to
4419
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4420
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4421
+ * can still overflow — re-measure the laid-out result and tighten.
4422
+ */
4423
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4424
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4425
+ return new _ScaleContext(clamped, minScaleFactor);
4426
+ }
4199
4427
  static identity() {
4200
4428
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4201
4429
  }
@@ -5835,12 +6063,7 @@ function parseSequenceDgmo(content, palette) {
5835
6063
  const trimmed = token.trim();
5836
6064
  return nameAliasMap.get(trimmed) ?? trimmed;
5837
6065
  };
5838
- const fail = (line11, message) => {
5839
- const diag = makeDgmoError(line11, message);
5840
- result.diagnostics.push(diag);
5841
- result.error = formatDgmoError(diag);
5842
- return result;
5843
- };
6066
+ const fail = makeFail(result);
5844
6067
  const pushError = (line11, message) => {
5845
6068
  const diag = makeDgmoError(line11, message);
5846
6069
  result.diagnostics.push(diag);
@@ -5855,6 +6078,7 @@ function parseSequenceDgmo(content, palette) {
5855
6078
  const lines = content.split("\n");
5856
6079
  let hasExplicitChart = false;
5857
6080
  let contentStarted = false;
6081
+ let bodyStarted = false;
5858
6082
  let firstLineIndex = -1;
5859
6083
  for (let fi = 0; fi < lines.length; fi++) {
5860
6084
  const fl = lines[fi].trim();
@@ -6166,6 +6390,7 @@ function parseSequenceDgmo(content, palette) {
6166
6390
  );
6167
6391
  }
6168
6392
  contentStarted = true;
6393
+ bodyStarted = true;
6169
6394
  const section = {
6170
6395
  kind: "section",
6171
6396
  // Capture group 1 guaranteed present after successful match.
@@ -6381,7 +6606,7 @@ function parseSequenceDgmo(content, palette) {
6381
6606
  alias: bareAlias
6382
6607
  } = splitPipe(trimmed, lineNumber);
6383
6608
  const inGroup = activeGroup && measureIndent(raw) > 0;
6384
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6609
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6385
6610
  contentStarted = true;
6386
6611
  const id = bareCore;
6387
6612
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6428,6 +6653,7 @@ function parseSequenceDgmo(content, palette) {
6428
6653
  }
6429
6654
  if (labeledArrow) {
6430
6655
  contentStarted = true;
6656
+ bodyStarted = true;
6431
6657
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6432
6658
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6433
6659
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6492,6 +6718,7 @@ function parseSequenceDgmo(content, palette) {
6492
6718
  const bareCall = bareCallSync || bareCallAsync;
6493
6719
  if (bareCall) {
6494
6720
  contentStarted = true;
6721
+ bodyStarted = true;
6495
6722
  const from = bareCall[1];
6496
6723
  const to = bareCall[2];
6497
6724
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6513,6 +6740,7 @@ function parseSequenceDgmo(content, palette) {
6513
6740
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6514
6741
  if (ifMatch) {
6515
6742
  contentStarted = true;
6743
+ bodyStarted = true;
6516
6744
  const block = {
6517
6745
  kind: "block",
6518
6746
  type: "if",
@@ -6529,6 +6757,7 @@ function parseSequenceDgmo(content, palette) {
6529
6757
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6530
6758
  if (loopMatch) {
6531
6759
  contentStarted = true;
6760
+ bodyStarted = true;
6532
6761
  const block = {
6533
6762
  kind: "block",
6534
6763
  type: "loop",
@@ -6545,6 +6774,7 @@ function parseSequenceDgmo(content, palette) {
6545
6774
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6546
6775
  if (parallelMatch) {
6547
6776
  contentStarted = true;
6777
+ bodyStarted = true;
6548
6778
  const block = {
6549
6779
  kind: "block",
6550
6780
  type: "parallel",
@@ -6641,6 +6871,7 @@ function parseSequenceDgmo(content, palette) {
6641
6871
  lineNumber,
6642
6872
  endLineNumber: lineNumber
6643
6873
  };
6874
+ bodyStarted = true;
6644
6875
  currentContainer().push(note);
6645
6876
  continue;
6646
6877
  }
@@ -6665,6 +6896,7 @@ function parseSequenceDgmo(content, palette) {
6665
6896
  endLineNumber: i + 1
6666
6897
  // i has advanced past the body lines (1-based)
6667
6898
  };
6899
+ bodyStarted = true;
6668
6900
  currentContainer().push(note);
6669
6901
  continue;
6670
6902
  }
@@ -7509,6 +7741,15 @@ function parseNodeRef(text) {
7509
7741
  }
7510
7742
  return null;
7511
7743
  }
7744
+ function parseNodeRefLoose(text) {
7745
+ const t = text.trim();
7746
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7747
+ const m = t.match(shapeRe);
7748
+ if (!m) return null;
7749
+ const ref = parseNodeRef(m[1]);
7750
+ if (!ref) return null;
7751
+ return { ref, trailing: m[2].trim() };
7752
+ }
7512
7753
  function splitArrows(line11) {
7513
7754
  const segments = [];
7514
7755
  const arrowPositions = [];
@@ -7604,6 +7845,7 @@ function parseFlowchart(content, palette) {
7604
7845
  const notes = [];
7605
7846
  let contentStarted = false;
7606
7847
  let firstLineParsed = false;
7848
+ let prevLineLastNodeId = null;
7607
7849
  const nameAliasMap = /* @__PURE__ */ new Map();
7608
7850
  function peelAlias2(seg) {
7609
7851
  const trimmed = seg.trim();
@@ -7611,6 +7853,19 @@ function parseFlowchart(content, palette) {
7611
7853
  if (!m) return { seg: trimmed };
7612
7854
  return { seg: m[1].trim(), alias: m[2] };
7613
7855
  }
7856
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7857
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7858
+ if (suffixWarnedLines.has(lineNumber)) return;
7859
+ suffixWarnedLines.add(lineNumber);
7860
+ result.diagnostics.push(
7861
+ makeDgmoError(
7862
+ lineNumber,
7863
+ `Ignored unsupported text after a node shape: "${trailing}". Flowcharts have no tag groups or node metadata; node colors are assigned automatically by shape (e.g. terminals green/red, decisions yellow). Remove the suffix.`,
7864
+ "warning",
7865
+ "W_FLOWCHART_NODE_SUFFIX"
7866
+ )
7867
+ );
7868
+ }
7614
7869
  function getOrCreateNode(ref, lineNumber) {
7615
7870
  const key = ref.id;
7616
7871
  const existing = nodeMap.get(key);
@@ -7669,6 +7924,8 @@ function parseFlowchart(content, palette) {
7669
7924
  indentStack[indentStack.length - 1].nodeId
7670
7925
  ) : null;
7671
7926
  const segments = splitArrows(trimmed);
7927
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7928
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7672
7929
  if (segments.length === 1) {
7673
7930
  const peeled = peelAlias2(segments[0]);
7674
7931
  const ref = parseNodeRef(peeled.seg);
@@ -7678,6 +7935,13 @@ function parseFlowchart(content, palette) {
7678
7935
  indentStack.push({ nodeId: node.id, indent });
7679
7936
  return node.id;
7680
7937
  }
7938
+ const loose = parseNodeRefLoose(peeled.seg);
7939
+ if (loose) {
7940
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7941
+ const node = getOrCreateNode(loose.ref, lineNumber);
7942
+ indentStack.push({ nodeId: node.id, indent });
7943
+ return node.id;
7944
+ }
7681
7945
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7682
7946
  if (aliasResolved !== void 0) {
7683
7947
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7713,16 +7977,23 @@ function parseFlowchart(content, palette) {
7713
7977
  }
7714
7978
  }
7715
7979
  }
7980
+ if (!ref) {
7981
+ const loose = parseNodeRefLoose(peeled.seg);
7982
+ if (loose) {
7983
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7984
+ ref = loose.ref;
7985
+ }
7986
+ }
7716
7987
  if (!ref) continue;
7717
7988
  const node = getOrCreateNode(ref, lineNumber);
7718
7989
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7719
7990
  if (pendingArrow !== null) {
7720
- const sourceId = lastNodeId ?? implicitSourceId;
7991
+ const sourceId = lastNodeId ?? effectiveSource;
7721
7992
  if (sourceId) {
7722
7993
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7723
7994
  }
7724
7995
  pendingArrow = null;
7725
- } else if (lastNodeId === null && implicitSourceId === null) {
7996
+ } else if (lastNodeId === null && effectiveSource === null) {
7726
7997
  }
7727
7998
  lastNodeId = node.id;
7728
7999
  }
@@ -7804,7 +8075,8 @@ function parseFlowchart(content, palette) {
7804
8075
  continue;
7805
8076
  }
7806
8077
  }
7807
- processContentLine(trimmed, lineNumber, indent);
8078
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8079
+ if (lastId) prevLineLastNodeId = lastId;
7808
8080
  }
7809
8081
  if (result.nodes.length === 0 && !result.error) {
7810
8082
  const diag = makeDgmoError(
@@ -8887,7 +9159,12 @@ function parseERDiagram(content, palette) {
8887
9159
  }
8888
9160
  if (currentTagGroup && !contentStarted && indent > 0) {
8889
9161
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8890
- const { label, color } = extractColor(cleanEntry, palette);
9162
+ const { label, color } = extractColor(
9163
+ cleanEntry,
9164
+ palette,
9165
+ result.diagnostics,
9166
+ lineNumber
9167
+ );
8891
9168
  if (isDefault) {
8892
9169
  currentTagGroup.defaultValue = label;
8893
9170
  } else if (currentTagGroup.entries.length === 0) {
@@ -9205,12 +9482,7 @@ function parseOrg(content, palette) {
9205
9482
  diagnostics: [],
9206
9483
  error: null
9207
9484
  };
9208
- const fail = (line11, message) => {
9209
- const diag = makeDgmoError(line11, message);
9210
- result.diagnostics.push(diag);
9211
- result.error = formatDgmoError(diag);
9212
- return result;
9213
- };
9485
+ const fail = makeFail(result);
9214
9486
  const pushError = (line11, message) => {
9215
9487
  const diag = makeDgmoError(line11, message);
9216
9488
  result.diagnostics.push(diag);
@@ -9319,7 +9591,12 @@ function parseOrg(content, palette) {
9319
9591
  const indent2 = measureIndent(line11);
9320
9592
  if (indent2 > 0) {
9321
9593
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9322
- const { label, color } = extractColor(cleanEntry, palette);
9594
+ const { label, color } = extractColor(
9595
+ cleanEntry,
9596
+ palette,
9597
+ result.diagnostics,
9598
+ lineNumber
9599
+ );
9323
9600
  if (isDefault) {
9324
9601
  currentTagGroup.defaultValue = label;
9325
9602
  } else if (currentTagGroup.entries.length === 0) {
@@ -9605,12 +9882,7 @@ function parseSitemap(content, palette) {
9605
9882
  diagnostics: [],
9606
9883
  error: null
9607
9884
  };
9608
- const fail = (line11, message) => {
9609
- const diag = makeDgmoError(line11, message);
9610
- result.diagnostics.push(diag);
9611
- result.error = formatDgmoError(diag);
9612
- return result;
9613
- };
9885
+ const fail = makeFail(result);
9614
9886
  const pushError = (line11, message) => {
9615
9887
  const diag = makeDgmoError(line11, message);
9616
9888
  result.diagnostics.push(diag);
@@ -9711,7 +9983,12 @@ function parseSitemap(content, palette) {
9711
9983
  const indent2 = measureIndent(line11);
9712
9984
  if (indent2 > 0) {
9713
9985
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9714
- const { label, color } = extractColor(cleanEntry, palette);
9986
+ const { label, color } = extractColor(
9987
+ cleanEntry,
9988
+ palette,
9989
+ result.diagnostics,
9990
+ lineNumber
9991
+ );
9715
9992
  currentTagGroup.entries.push({
9716
9993
  value: label,
9717
9994
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11426,7 +11703,7 @@ var init_chart_types = __esm({
11426
11703
  },
11427
11704
  {
11428
11705
  id: "sequence",
11429
- description: "Message / interaction flows",
11706
+ description: "Message request and response interaction flows",
11430
11707
  fallback: true
11431
11708
  },
11432
11709
  {
@@ -11500,7 +11777,7 @@ var init_chart_types = __esm({
11500
11777
  },
11501
11778
  {
11502
11779
  id: "map",
11503
- description: "Geographic map: a value or count per country, state, or region (choropleth); points of interest; routes. Use when categories are real-world places."
11780
+ description: "Geographic concept map: highlight/score regions, drop points of interest, connect with routes or edges"
11504
11781
  },
11505
11782
  // ── Tier 3 — Specialized analytical charts ────────────────
11506
11783
  {
@@ -11517,7 +11794,7 @@ var init_chart_types = __esm({
11517
11794
  },
11518
11795
  {
11519
11796
  id: "slope",
11520
- description: "Change for multiple things between exactly two periods"
11797
+ description: "Change between 2 time periods"
11521
11798
  },
11522
11799
  {
11523
11800
  id: "sankey",
@@ -11546,7 +11823,7 @@ var init_chart_types = __esm({
11546
11823
  // ── Tier 4 — General-purpose data charts ──────────────────
11547
11824
  {
11548
11825
  id: "bar",
11549
- description: "Categorical comparisons",
11826
+ description: "Categorical comparisons for 3 - 5 figures",
11550
11827
  fallback: true
11551
11828
  },
11552
11829
  {
@@ -11808,7 +12085,9 @@ function parseChart(content, palette) {
11808
12085
  if (dataValues) {
11809
12086
  const { label: rawLabel, color: pointColor } = extractColor(
11810
12087
  dataValues.label,
11811
- palette
12088
+ palette,
12089
+ result.diagnostics,
12090
+ lineNumber
11812
12091
  );
11813
12092
  const [first, ...rest] = dataValues.values;
11814
12093
  result.data.push({
@@ -11873,6 +12152,12 @@ function parseChart(content, palette) {
11873
12152
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11874
12153
  );
11875
12154
  }
12155
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12156
+ warn(
12157
+ result.seriesLineNumber ?? 1,
12158
+ `Plain "bar" shows only the first series ("${result.seriesNames[0]}"); the other ${result.seriesNames.length - 1} are dropped at render. Use "bar-stacked" for stacked bars or "multi-line" to plot every series.`
12159
+ );
12160
+ }
11876
12161
  if (!result.error && result.seriesNames) {
11877
12162
  const expectedCount = result.seriesNames.length;
11878
12163
  for (const dp of result.data) {
@@ -12156,7 +12441,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12156
12441
  if (!dataRow || dataRow.values.length < 2) return null;
12157
12442
  const { label: rawLabel, color: pointColor } = extractColor(
12158
12443
  dataRow.label,
12159
- palette
12444
+ palette,
12445
+ diagnostics,
12446
+ lineNumber
12160
12447
  );
12161
12448
  return {
12162
12449
  name: rawLabel,
@@ -12294,11 +12581,15 @@ function parseExtendedChartFull(content, palette) {
12294
12581
  const targetResolved = resolveSlot(rawTarget);
12295
12582
  const { label: source, color: sourceColor } = extractColor(
12296
12583
  sourceResolved,
12297
- palette
12584
+ palette,
12585
+ result.diagnostics,
12586
+ lineNumber
12298
12587
  );
12299
12588
  const { label: target, color: targetColor } = extractColor(
12300
12589
  targetResolved,
12301
- palette
12590
+ palette,
12591
+ result.diagnostics,
12592
+ lineNumber
12302
12593
  );
12303
12594
  if (sourceColor || targetColor) {
12304
12595
  if (!result.nodeColors) result.nodeColors = {};
@@ -12350,7 +12641,9 @@ function parseExtendedChartFull(content, palette) {
12350
12641
  const targetResolved = resolveSlot(dataRow2.label);
12351
12642
  const { label: target, color: targetColor } = extractColor(
12352
12643
  targetResolved,
12353
- palette
12644
+ palette,
12645
+ result.diagnostics,
12646
+ lineNumber
12354
12647
  );
12355
12648
  if (targetColor) {
12356
12649
  if (!result.nodeColors) result.nodeColors = {};
@@ -12383,7 +12676,9 @@ function parseExtendedChartFull(content, palette) {
12383
12676
  const trimmedResolved = resolveSlot(trimmed);
12384
12677
  const { label: nodeName, color: nodeColor2 } = extractColor(
12385
12678
  trimmedResolved,
12386
- palette
12679
+ palette,
12680
+ result.diagnostics,
12681
+ lineNumber
12387
12682
  );
12388
12683
  if (nodeColor2) {
12389
12684
  if (!result.nodeColors) result.nodeColors = {};
@@ -12473,8 +12768,11 @@ function parseExtendedChartFull(content, palette) {
12473
12768
  min: parseFloat(rangeMatch[1]),
12474
12769
  max: parseFloat(rangeMatch[2])
12475
12770
  };
12771
+ continue;
12772
+ }
12773
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12774
+ continue;
12476
12775
  }
12477
- continue;
12478
12776
  }
12479
12777
  }
12480
12778
  if (firstToken === "no-name") {
@@ -12534,7 +12832,9 @@ function parseExtendedChartFull(content, palette) {
12534
12832
  if (colonIndex >= 0) {
12535
12833
  const { label: fnName, color: fnColor } = extractColor(
12536
12834
  trimmed.substring(0, colonIndex).trim(),
12537
- palette
12835
+ palette,
12836
+ result.diagnostics,
12837
+ lineNumber
12538
12838
  );
12539
12839
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12540
12840
  if (!result.functions) result.functions = [];
@@ -12582,7 +12882,9 @@ function parseExtendedChartFull(content, palette) {
12582
12882
  if (dataRow?.values.length === 1) {
12583
12883
  const { label: rawLabel, color: pointColor } = extractColor(
12584
12884
  dataRow.label,
12585
- palette
12885
+ palette,
12886
+ result.diagnostics,
12887
+ lineNumber
12586
12888
  );
12587
12889
  result.data.push({
12588
12890
  label: rawLabel,
@@ -12681,11 +12983,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12681
12983
  const sc = ctx ?? ScaleContext.identity();
12682
12984
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12683
12985
  if (parsed.type === "sankey") {
12684
- const bg = isDark ? palette.surface : palette.bg;
12986
+ const bg = themeBaseBg(palette, isDark);
12685
12987
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12686
12988
  }
12687
12989
  if (parsed.type === "chord") {
12688
- const bg = isDark ? palette.surface : palette.bg;
12990
+ const bg = themeBaseBg(palette, isDark);
12689
12991
  return buildChordOption(
12690
12992
  parsed,
12691
12993
  palette,
@@ -12726,7 +13028,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12726
13028
  );
12727
13029
  }
12728
13030
  if (parsed.type === "funnel") {
12729
- const bg = isDark ? palette.surface : palette.bg;
13031
+ const bg = themeBaseBg(palette, isDark);
12730
13032
  return buildFunnelOption(
12731
13033
  parsed,
12732
13034
  palette,
@@ -13322,7 +13624,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13322
13624
  const gridLeft = parsed.ylabel ? 12 : 3;
13323
13625
  const gridRight = 4;
13324
13626
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13325
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13627
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13628
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13326
13629
  let graphic;
13327
13630
  if (showLabels && points.length > 0) {
13328
13631
  const labelPoints = [];
@@ -13462,7 +13765,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13462
13765
  }
13463
13766
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13464
13767
  const sc = ctx ?? ScaleContext.identity();
13465
- const bg = isDark ? palette.surface : palette.bg;
13768
+ const bg = themeBaseBg(palette, isDark);
13466
13769
  const heatmapRows = parsed.heatmapRows ?? [];
13467
13770
  const columns = parsed.columns ?? [];
13468
13771
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13781,7 +14084,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13781
14084
  colors,
13782
14085
  titleConfig
13783
14086
  } = buildChartCommons(parsed, palette, isDark, sc);
13784
- const bg = isDark ? palette.surface : palette.bg;
14087
+ const bg = themeBaseBg(palette, isDark);
13785
14088
  switch (parsed.type) {
13786
14089
  case "bar":
13787
14090
  return buildBarOption(
@@ -14729,12 +15032,7 @@ function parseKanban(content, palette) {
14729
15032
  diagnostics: [],
14730
15033
  error: null
14731
15034
  };
14732
- const fail = (line11, message) => {
14733
- const diag = makeDgmoError(line11, message);
14734
- result.diagnostics.push(diag);
14735
- result.error = formatDgmoError(diag);
14736
- return result;
14737
- };
15035
+ const fail = makeFail(result);
14738
15036
  const warn = (line11, message) => {
14739
15037
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14740
15038
  };
@@ -14837,7 +15135,12 @@ function parseKanban(content, palette) {
14837
15135
  const indent2 = measureIndent(line11);
14838
15136
  if (indent2 > 0) {
14839
15137
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14840
- const { label, color } = extractColor(cleanEntry, palette);
15138
+ const { label, color } = extractColor(
15139
+ cleanEntry,
15140
+ palette,
15141
+ result.diagnostics,
15142
+ lineNumber
15143
+ );
14841
15144
  if (isDefault) {
14842
15145
  currentTagGroup.defaultValue = label;
14843
15146
  } else if (currentTagGroup.entries.length === 0) {
@@ -15005,11 +15308,11 @@ function parseKanban(content, palette) {
15005
15308
  for (const col of result.columns) {
15006
15309
  for (const card of col.cards) {
15007
15310
  for (const [tagKey, tagValue] of Object.entries(card.tags)) {
15008
- const groupKey = metaAliasMap.get(tagKey.toLowerCase()) ?? tagKey.toLowerCase();
15009
- const validValues = tagValueSets.get(groupKey);
15311
+ const groupKey2 = metaAliasMap.get(tagKey.toLowerCase()) ?? tagKey.toLowerCase();
15312
+ const validValues = tagValueSets.get(groupKey2);
15010
15313
  if (validValues && !validValues.has(tagValue.toLowerCase())) {
15011
- const entries = result.tagGroups.find((g) => g.name.toLowerCase() === groupKey)?.entries.map((e) => e.value);
15012
- let msg = `Unknown tag value "${tagValue}" for group "${groupKey}"`;
15314
+ const entries = result.tagGroups.find((g) => g.name.toLowerCase() === groupKey2)?.entries.map((e) => e.value);
15315
+ let msg = `Unknown tag value "${tagValue}" for group "${groupKey2}"`;
15013
15316
  if (entries) {
15014
15317
  const hint = suggest(tagValue, entries);
15015
15318
  if (hint) msg += `. ${hint}`;
@@ -15181,12 +15484,7 @@ function parseC4(content, palette) {
15181
15484
  if (!result.error && severity === "error")
15182
15485
  result.error = formatDgmoError(diag);
15183
15486
  };
15184
- const fail = (line11, message) => {
15185
- const diag = makeDgmoError(line11, message);
15186
- result.diagnostics.push(diag);
15187
- result.error = formatDgmoError(diag);
15188
- return result;
15189
- };
15487
+ const fail = makeFail(result);
15190
15488
  if (!content?.trim()) {
15191
15489
  return fail(0, "No content provided");
15192
15490
  }
@@ -15287,7 +15585,12 @@ function parseC4(content, palette) {
15287
15585
  const indent2 = measureIndent(line11);
15288
15586
  if (indent2 > 0) {
15289
15587
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15290
- const { label, color } = extractColor(cleanEntry, palette);
15588
+ const { label, color } = extractColor(
15589
+ cleanEntry,
15590
+ palette,
15591
+ result.diagnostics,
15592
+ lineNumber
15593
+ );
15291
15594
  if (isDefault) {
15292
15595
  currentTagGroup.defaultValue = label;
15293
15596
  } else if (currentTagGroup.entries.length === 0) {
@@ -16952,8 +17255,66 @@ function parseInfra(content) {
16952
17255
  }
16953
17256
  }
16954
17257
  validateTagGroupNames(result.tagGroups, warn, setError);
17258
+ checkReachability(result);
16955
17259
  return result;
16956
17260
  }
17261
+ function checkReachability(result) {
17262
+ if (result.nodes.length === 0) return;
17263
+ const entries = result.nodes.filter((n) => n.isEdge);
17264
+ if (entries.length === 0) {
17265
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17266
+ result.diagnostics.push(
17267
+ makeDgmoError(
17268
+ line11,
17269
+ `Infra diagram has no 'internet' or 'edge' entry point \u2014 an infra diagram traces request traffic from an entry inward, so without one nothing carries traffic. Add an 'internet' or 'edge' node and route from it.`,
17270
+ "warning",
17271
+ "W_INFRA_NO_ENTRY"
17272
+ )
17273
+ );
17274
+ return;
17275
+ }
17276
+ const groupChildMap = /* @__PURE__ */ new Map();
17277
+ for (const node of result.nodes) {
17278
+ if (node.groupId) {
17279
+ const list = groupChildMap.get(node.groupId) ?? [];
17280
+ list.push(node.id);
17281
+ groupChildMap.set(node.groupId, list);
17282
+ }
17283
+ }
17284
+ const outbound = /* @__PURE__ */ new Map();
17285
+ for (const edge of result.edges) {
17286
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17287
+ const list = outbound.get(edge.sourceId) ?? [];
17288
+ list.push(...targets);
17289
+ outbound.set(edge.sourceId, list);
17290
+ }
17291
+ const reachable = /* @__PURE__ */ new Set();
17292
+ const queue = [];
17293
+ for (const entry of entries) {
17294
+ reachable.add(entry.id);
17295
+ queue.push(entry.id);
17296
+ }
17297
+ while (queue.length > 0) {
17298
+ const current = queue.shift();
17299
+ for (const next of outbound.get(current) ?? []) {
17300
+ if (!reachable.has(next)) {
17301
+ reachable.add(next);
17302
+ queue.push(next);
17303
+ }
17304
+ }
17305
+ }
17306
+ for (const node of result.nodes) {
17307
+ if (node.isEdge || reachable.has(node.id)) continue;
17308
+ result.diagnostics.push(
17309
+ makeDgmoError(
17310
+ node.lineNumber,
17311
+ `'${node.label}' is unreachable from an 'internet'/'edge' entry \u2014 no request traffic flows to it, so it's dead on an infra diagram. Connect it downstream of an entry, or remove it.`,
17312
+ "warning",
17313
+ "W_INFRA_UNREACHABLE"
17314
+ )
17315
+ );
17316
+ }
17317
+ }
16957
17318
  function stripNodeDecorations(name) {
16958
17319
  let s = name.trim();
16959
17320
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17243,7 +17604,12 @@ function parseGantt(content, palette) {
17243
17604
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17244
17605
  if (eraEntryMatch) {
17245
17606
  const eraLabelRaw = eraEntryMatch[3].trim();
17246
- const eraExtracted = extractColor(eraLabelRaw, palette);
17607
+ const eraExtracted = extractColor(
17608
+ eraLabelRaw,
17609
+ palette,
17610
+ diagnostics,
17611
+ lineNumber
17612
+ );
17247
17613
  result.eras.push({
17248
17614
  startDate: eraEntryMatch[1],
17249
17615
  endDate: eraEntryMatch[2],
@@ -17265,7 +17631,12 @@ function parseGantt(content, palette) {
17265
17631
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17266
17632
  if (markerEntryMatch) {
17267
17633
  const markerLabelRaw = markerEntryMatch[2].trim();
17268
- const markerExtracted = extractColor(markerLabelRaw, palette);
17634
+ const markerExtracted = extractColor(
17635
+ markerLabelRaw,
17636
+ palette,
17637
+ diagnostics,
17638
+ lineNumber
17639
+ );
17269
17640
  result.markers.push({
17270
17641
  date: markerEntryMatch[1],
17271
17642
  label: markerExtracted.label,
@@ -17286,7 +17657,12 @@ function parseGantt(content, palette) {
17286
17657
  } else {
17287
17658
  if (COMMENT_RE.test(line11)) continue;
17288
17659
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17289
- const extracted = extractColor(cleanEntry, palette);
17660
+ const extracted = extractColor(
17661
+ cleanEntry,
17662
+ palette,
17663
+ diagnostics,
17664
+ lineNumber
17665
+ );
17290
17666
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17291
17667
  const isFirstEntry = currentTagGroup.entries.length === 0;
17292
17668
  currentTagGroup.entries.push({
@@ -17481,7 +17857,12 @@ function parseGantt(content, palette) {
17481
17857
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17482
17858
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17483
17859
  const eraLabelRaw = eraOffsetMatch[3].trim();
17484
- const eraExtracted = extractColor(eraLabelRaw, palette);
17860
+ const eraExtracted = extractColor(
17861
+ eraLabelRaw,
17862
+ palette,
17863
+ diagnostics,
17864
+ lineNumber
17865
+ );
17485
17866
  result.eras.push({
17486
17867
  startDate: "",
17487
17868
  endDate: "",
@@ -17497,7 +17878,12 @@ function parseGantt(content, palette) {
17497
17878
  if (markerOffsetMatch) {
17498
17879
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17499
17880
  const markerLabelRaw = markerOffsetMatch[2].trim();
17500
- const markerExtracted = extractColor(markerLabelRaw, palette);
17881
+ const markerExtracted = extractColor(
17882
+ markerLabelRaw,
17883
+ palette,
17884
+ diagnostics,
17885
+ lineNumber
17886
+ );
17501
17887
  result.markers.push({
17502
17888
  date: "",
17503
17889
  label: markerExtracted.label,
@@ -17571,7 +17957,12 @@ function parseGantt(content, palette) {
17571
17957
  const eraMatch = line11.match(ERA_RE);
17572
17958
  if (eraMatch) {
17573
17959
  const eraLabelRaw = eraMatch[3].trim();
17574
- const eraExtracted = extractColor(eraLabelRaw, palette);
17960
+ const eraExtracted = extractColor(
17961
+ eraLabelRaw,
17962
+ palette,
17963
+ diagnostics,
17964
+ lineNumber
17965
+ );
17575
17966
  result.eras.push({
17576
17967
  startDate: eraMatch[1],
17577
17968
  endDate: eraMatch[2],
@@ -17589,7 +17980,12 @@ function parseGantt(content, palette) {
17589
17980
  const markerMatch = line11.match(MARKER_RE);
17590
17981
  if (markerMatch) {
17591
17982
  const markerLabelRaw = markerMatch[2].trim();
17592
- const markerExtracted = extractColor(markerLabelRaw, palette);
17983
+ const markerExtracted = extractColor(
17984
+ markerLabelRaw,
17985
+ palette,
17986
+ diagnostics,
17987
+ lineNumber
17988
+ );
17593
17989
  result.markers.push({
17594
17990
  date: markerMatch[1],
17595
17991
  label: markerExtracted.label,
@@ -19061,7 +19457,7 @@ function parseBoxesAndLines(content, palette) {
19061
19457
  const trimmed = raw.trim();
19062
19458
  const indent = measureIndent2(raw);
19063
19459
  if (!trimmed || trimmed.startsWith("//")) continue;
19064
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19460
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
19065
19461
  result.diagnostics.push(
19066
19462
  makeDgmoError(
19067
19463
  lineNum,
@@ -19100,13 +19496,13 @@ function parseBoxesAndLines(content, palette) {
19100
19496
  for (const pair of pairs) {
19101
19497
  const colonIdx = pair.indexOf(":");
19102
19498
  if (colonIdx > 0) {
19103
- const groupKey = pair.substring(0, colonIdx).trim().toLowerCase();
19499
+ const groupKey2 = pair.substring(0, colonIdx).trim().toLowerCase();
19104
19500
  const value = pair.substring(colonIdx + 1).trim().toLowerCase();
19105
- if (groupKey && value) {
19106
- if (!initialHiddenTagValues.has(groupKey)) {
19107
- initialHiddenTagValues.set(groupKey, /* @__PURE__ */ new Set());
19501
+ if (groupKey2 && value) {
19502
+ if (!initialHiddenTagValues.has(groupKey2)) {
19503
+ initialHiddenTagValues.set(groupKey2, /* @__PURE__ */ new Set());
19108
19504
  }
19109
- initialHiddenTagValues.get(groupKey).add(value);
19505
+ initialHiddenTagValues.get(groupKey2).add(value);
19110
19506
  }
19111
19507
  }
19112
19508
  }
@@ -19190,7 +19586,12 @@ function parseBoxesAndLines(content, palette) {
19190
19586
  if (tagBlockMatch.inlineValues) {
19191
19587
  for (const rawVal of tagBlockMatch.inlineValues) {
19192
19588
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19193
- const { label, color } = extractColor(cleanVal);
19589
+ const { label, color } = extractColor(
19590
+ cleanVal,
19591
+ palette,
19592
+ result.diagnostics,
19593
+ lineNum
19594
+ );
19194
19595
  newTagGroup.entries.push({
19195
19596
  value: label,
19196
19597
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19207,7 +19608,12 @@ function parseBoxesAndLines(content, palette) {
19207
19608
  }
19208
19609
  if (currentTagGroup && !contentStarted && indent > 0) {
19209
19610
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19210
- const { label, color } = extractColor(cleanEntry);
19611
+ const { label, color } = extractColor(
19612
+ cleanEntry,
19613
+ palette,
19614
+ result.diagnostics,
19615
+ lineNum
19616
+ );
19211
19617
  currentTagGroup.entries.push({
19212
19618
  value: label,
19213
19619
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19805,7 +20211,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19805
20211
  metadata
19806
20212
  };
19807
20213
  }
19808
- var MAX_GROUP_DEPTH;
20214
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19809
20215
  var init_parser18 = __esm({
19810
20216
  "src/boxes-and-lines/parser.ts"() {
19811
20217
  "use strict";
@@ -19817,6 +20223,8 @@ var init_parser18 = __esm({
19817
20223
  init_reserved_key_registry();
19818
20224
  init_notes();
19819
20225
  MAX_GROUP_DEPTH = 2;
20226
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20227
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19820
20228
  }
19821
20229
  });
19822
20230
 
@@ -19836,12 +20244,7 @@ function parseMindmap(content, palette) {
19836
20244
  diagnostics: [],
19837
20245
  error: null
19838
20246
  };
19839
- const fail = (line11, message) => {
19840
- const diag = makeDgmoError(line11, message);
19841
- result.diagnostics.push(diag);
19842
- result.error = formatDgmoError(diag);
19843
- return result;
19844
- };
20247
+ const fail = makeFail(result);
19845
20248
  const pushError = (line11, message) => {
19846
20249
  const diag = makeDgmoError(line11, message);
19847
20250
  result.diagnostics.push(diag);
@@ -19954,7 +20357,12 @@ function parseMindmap(content, palette) {
19954
20357
  const indent2 = measureIndent(line11);
19955
20358
  if (indent2 > 0) {
19956
20359
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19957
- const { label, color } = extractColor(cleanEntry, palette);
20360
+ const { label, color } = extractColor(
20361
+ cleanEntry,
20362
+ palette,
20363
+ result.diagnostics,
20364
+ lineNumber
20365
+ );
19958
20366
  if (isDefault) {
19959
20367
  currentTagGroup.defaultValue = label;
19960
20368
  } else if (currentTagGroup.entries.length === 0) {
@@ -20031,6 +20439,7 @@ function parseMindmap(content, palette) {
20031
20439
  collectAll(result.roots);
20032
20440
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
20033
20441
  validateTagGroupNames(result.tagGroups, pushWarning);
20442
+ cascadeTagMetadata(result.roots, result.tagGroups);
20034
20443
  }
20035
20444
  if (result.roots.length === 0 && !result.error) {
20036
20445
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20637,7 +21046,12 @@ function parseWireframe(content) {
20637
21046
  }
20638
21047
  if (indent > 0 && currentTagGroup) {
20639
21048
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20640
- const { label, color } = extractColor(cleanEntry);
21049
+ const { label, color } = extractColor(
21050
+ cleanEntry,
21051
+ void 0,
21052
+ diagnostics,
21053
+ lineNumber
21054
+ );
20641
21055
  currentTagGroup.entries.push({
20642
21056
  value: label,
20643
21057
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20885,12 +21299,7 @@ function parseTechRadar(content) {
20885
21299
  diagnostics: [],
20886
21300
  error: null
20887
21301
  };
20888
- const fail = (line11, message) => {
20889
- const diag = makeDgmoError(line11, message);
20890
- result.diagnostics.push(diag);
20891
- result.error = formatDgmoError(diag);
20892
- return result;
20893
- };
21302
+ const fail = makeFail(result);
20894
21303
  const warn = (line11, message) => {
20895
21304
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20896
21305
  };
@@ -21258,12 +21667,7 @@ function parseCycle(content) {
21258
21667
  let state = "top";
21259
21668
  let currentNode = null;
21260
21669
  let currentEdge = null;
21261
- const fail = (line11, message) => {
21262
- const diag = makeDgmoError(line11, message);
21263
- result.diagnostics.push(diag);
21264
- result.error = formatDgmoError(diag);
21265
- return result;
21266
- };
21670
+ const fail = makeFail(result);
21267
21671
  const warn = (line11, message, severity = "warning") => {
21268
21672
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21269
21673
  };
@@ -21540,12 +21944,7 @@ function parseJourneyMap(content, palette) {
21540
21944
  diagnostics: [],
21541
21945
  error: null
21542
21946
  };
21543
- const fail = (line11, message) => {
21544
- const diag = makeDgmoError(line11, message);
21545
- result.diagnostics.push(diag);
21546
- result.error = formatDgmoError(diag);
21547
- return result;
21548
- };
21947
+ const fail = makeFail(result);
21549
21948
  const warn = (line11, message) => {
21550
21949
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21551
21950
  };
@@ -21694,7 +22093,12 @@ function parseJourneyMap(content, palette) {
21694
22093
  if (currentTagGroup && !contentStarted) {
21695
22094
  if (indent > 0) {
21696
22095
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21697
- const { label, color } = extractColor(cleanEntry, palette);
22096
+ const { label, color } = extractColor(
22097
+ cleanEntry,
22098
+ palette,
22099
+ result.diagnostics,
22100
+ lineNumber
22101
+ );
21698
22102
  if (isDefault) {
21699
22103
  currentTagGroup.defaultValue = label;
21700
22104
  } else if (currentTagGroup.entries.length === 0) {
@@ -21829,11 +22233,11 @@ function parseJourneyMap(content, palette) {
21829
22233
  const allSteps = hasPhases ? result.phases.flatMap((p) => p.steps) : result.steps;
21830
22234
  for (const step of allSteps) {
21831
22235
  for (const [tagKey, tagValue] of Object.entries(step.tags)) {
21832
- const groupKey = aliasMap.get(tagKey.toLowerCase()) ?? tagKey.toLowerCase();
21833
- const validValues = tagValueSets.get(groupKey);
22236
+ const groupKey2 = aliasMap.get(tagKey.toLowerCase()) ?? tagKey.toLowerCase();
22237
+ const validValues = tagValueSets.get(groupKey2);
21834
22238
  if (validValues && !validValues.has(tagValue.toLowerCase())) {
21835
- const entries = result.tagGroups.find((g) => g.name.toLowerCase() === groupKey)?.entries.map((e) => e.value);
21836
- let msg = `Unknown tag value "${tagValue}" for group "${groupKey}"`;
22239
+ const entries = result.tagGroups.find((g) => g.name.toLowerCase() === groupKey2)?.entries.map((e) => e.value);
22240
+ let msg = `Unknown tag value "${tagValue}" for group "${groupKey2}"`;
21837
22241
  if (entries) {
21838
22242
  const hint = suggest(tagValue, entries);
21839
22243
  if (hint) msg += `. ${hint}`;
@@ -22066,12 +22470,7 @@ function parsePyramid(content) {
22066
22470
  const lines = content.split("\n");
22067
22471
  let headerParsed = false;
22068
22472
  let currentLayer = null;
22069
- const fail = (line11, message) => {
22070
- const diag = makeDgmoError(line11, message);
22071
- result.diagnostics.push(diag);
22072
- result.error = formatDgmoError(diag);
22073
- return result;
22074
- };
22473
+ const fail = makeFail(result);
22075
22474
  const warn = (line11, message, severity = "warning") => {
22076
22475
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22077
22476
  };
@@ -22218,12 +22617,7 @@ function parseRing(content) {
22218
22617
  const lines = content.split("\n");
22219
22618
  let headerParsed = false;
22220
22619
  let currentLayer = null;
22221
- const fail = (line11, message) => {
22222
- const diag = makeDgmoError(line11, message);
22223
- result.diagnostics.push(diag);
22224
- result.error = formatDgmoError(diag);
22225
- return result;
22226
- };
22620
+ const fail = makeFail(result);
22227
22621
  const warn = (line11, message, severity = "warning") => {
22228
22622
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22229
22623
  };
@@ -22687,12 +23081,7 @@ function parseRaci(content, palette) {
22687
23081
  diagnostics: [],
22688
23082
  error: null
22689
23083
  };
22690
- const fail = (line11, message) => {
22691
- const diag = makeDgmoError(line11, message);
22692
- result.diagnostics.push(diag);
22693
- result.error = formatDgmoError(diag);
22694
- return result;
22695
- };
23084
+ const fail = makeFail(result);
22696
23085
  const warn = (line11, message, code) => {
22697
23086
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22698
23087
  };
@@ -23295,6 +23684,81 @@ function measureInfra(content) {
23295
23684
  const parsed = parseInfra(content);
23296
23685
  return { nodes: parsed.nodes.length };
23297
23686
  }
23687
+ function minDimsSequence(c) {
23688
+ return {
23689
+ width: Math.max((c.participants ?? 2) * 80, 320),
23690
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23691
+ };
23692
+ }
23693
+ function minDimsRaci(c) {
23694
+ return {
23695
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23696
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23697
+ };
23698
+ }
23699
+ function minDimsMindmap(c) {
23700
+ return {
23701
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23702
+ height: Math.max((c.depth ?? 2) * 60, 200)
23703
+ };
23704
+ }
23705
+ function minDimsTechRadar() {
23706
+ return { width: 360, height: 400 };
23707
+ }
23708
+ function minDimsHeatmap(c) {
23709
+ return {
23710
+ width: Math.max((c.columns ?? 3) * 40, 300),
23711
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23712
+ };
23713
+ }
23714
+ function minDimsArc(c) {
23715
+ return {
23716
+ width: 300,
23717
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23718
+ };
23719
+ }
23720
+ function minDimsOrg(c) {
23721
+ return {
23722
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23723
+ height: Math.max((c.depth ?? 2) * 80, 200)
23724
+ };
23725
+ }
23726
+ function minDimsGantt(c) {
23727
+ return {
23728
+ width: 400,
23729
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23730
+ };
23731
+ }
23732
+ function minDimsKanban(c) {
23733
+ return {
23734
+ width: Math.max((c.columns ?? 3) * 120, 360),
23735
+ height: 300
23736
+ };
23737
+ }
23738
+ function minDimsEntities(c) {
23739
+ return {
23740
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23741
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23742
+ };
23743
+ }
23744
+ function minDimsGraph(c) {
23745
+ return {
23746
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23747
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23748
+ };
23749
+ }
23750
+ function minDimsPert(c) {
23751
+ return {
23752
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23753
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23754
+ };
23755
+ }
23756
+ function minDimsInfra(c) {
23757
+ return {
23758
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23759
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23760
+ };
23761
+ }
23298
23762
  function isExtendedChartParser(parse) {
23299
23763
  return EXTENDED_CHART_DOORS.has(parse);
23300
23764
  }
@@ -23338,33 +23802,50 @@ var init_chart_type_registry = __esm({
23338
23802
  id: "sequence",
23339
23803
  category: "diagram",
23340
23804
  parse: parseSequenceDgmo,
23341
- measure: measureSequence
23805
+ measure: measureSequence,
23806
+ minDims: minDimsSequence
23342
23807
  },
23343
23808
  {
23344
23809
  id: "flowchart",
23345
23810
  category: "diagram",
23346
23811
  parse: parseFlowchart,
23347
- measure: measureFlowchart
23812
+ measure: measureFlowchart,
23813
+ minDims: minDimsGraph
23348
23814
  },
23349
23815
  {
23350
23816
  id: "class",
23351
23817
  category: "diagram",
23352
23818
  parse: parseClassDiagram,
23353
- measure: measureClass
23819
+ measure: measureClass,
23820
+ minDims: minDimsEntities
23821
+ },
23822
+ {
23823
+ id: "er",
23824
+ category: "diagram",
23825
+ parse: parseERDiagram,
23826
+ measure: measureER,
23827
+ minDims: minDimsEntities
23354
23828
  },
23355
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23356
23829
  {
23357
23830
  id: "state",
23358
23831
  category: "diagram",
23359
23832
  parse: parseState,
23360
- measure: measureStateGraph
23833
+ measure: measureStateGraph,
23834
+ minDims: minDimsGraph
23835
+ },
23836
+ {
23837
+ id: "org",
23838
+ category: "diagram",
23839
+ parse: parseOrg,
23840
+ measure: measureOrg,
23841
+ minDims: minDimsOrg
23361
23842
  },
23362
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23363
23843
  {
23364
23844
  id: "kanban",
23365
23845
  category: "diagram",
23366
23846
  parse: parseKanban,
23367
- measure: measureKanban
23847
+ measure: measureKanban,
23848
+ minDims: minDimsKanban
23368
23849
  },
23369
23850
  { id: "c4", category: "diagram", parse: parseC4 },
23370
23851
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23372,25 +23853,40 @@ var init_chart_type_registry = __esm({
23372
23853
  id: "infra",
23373
23854
  category: "diagram",
23374
23855
  parse: parseInfra,
23375
- measure: measureInfra
23856
+ measure: measureInfra,
23857
+ minDims: minDimsInfra
23376
23858
  },
23377
23859
  {
23378
23860
  id: "gantt",
23379
23861
  category: "diagram",
23380
23862
  parse: parseGantt,
23381
- measure: measureGantt
23863
+ measure: measureGantt,
23864
+ minDims: minDimsGantt
23865
+ },
23866
+ {
23867
+ id: "pert",
23868
+ category: "diagram",
23869
+ parse: parsePert,
23870
+ measure: measurePert,
23871
+ minDims: minDimsPert
23382
23872
  },
23383
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23384
23873
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23385
23874
  {
23386
23875
  id: "mindmap",
23387
23876
  category: "diagram",
23388
23877
  parse: parseMindmap,
23389
- measure: measureMindmap
23878
+ measure: measureMindmap,
23879
+ minDims: minDimsMindmap
23390
23880
  },
23391
23881
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23392
23882
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23393
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23883
+ {
23884
+ id: "raci",
23885
+ category: "diagram",
23886
+ parse: parseRaci,
23887
+ measure: measureRaci,
23888
+ minDims: minDimsRaci
23889
+ },
23394
23890
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23395
23891
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23396
23892
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23412,7 +23908,8 @@ var init_chart_type_registry = __esm({
23412
23908
  id: "heatmap",
23413
23909
  category: "data-chart",
23414
23910
  parse: parseHeatmap,
23415
- measure: measureHeatmap
23911
+ measure: measureHeatmap,
23912
+ minDims: minDimsHeatmap
23416
23913
  },
23417
23914
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23418
23915
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23422,7 +23919,8 @@ var init_chart_type_registry = __esm({
23422
23919
  id: "arc",
23423
23920
  category: "visualization",
23424
23921
  parse: parseArc,
23425
- measure: measureArc
23922
+ measure: measureArc,
23923
+ minDims: minDimsArc
23426
23924
  },
23427
23925
  { id: "timeline", category: "visualization", parse: parseTimeline },
23428
23926
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23432,7 +23930,8 @@ var init_chart_type_registry = __esm({
23432
23930
  id: "tech-radar",
23433
23931
  category: "visualization",
23434
23932
  parse: parseTechRadar,
23435
- measure: measureTechRadar
23933
+ measure: measureTechRadar,
23934
+ minDims: minDimsTechRadar
23436
23935
  },
23437
23936
  { id: "cycle", category: "visualization", parse: parseCycle },
23438
23937
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24654,7 +25153,7 @@ function nodeStroke(palette, nodeColor2) {
24654
25153
  }
24655
25154
  function containerFill(palette, isDark, nodeColor2) {
24656
25155
  if (nodeColor2) {
24657
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25156
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24658
25157
  }
24659
25158
  return mix(palette.surface, palette.bg, 40);
24660
25159
  }
@@ -24812,10 +25311,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24812
25311
  const iconY = iconPad;
24813
25312
  const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24814
25313
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25314
+ const iconColor = contrastText(
25315
+ fill2,
25316
+ palette.textOnFillLight,
25317
+ palette.textOnFillDark
25318
+ );
24815
25319
  const cx = iconSize / 2;
24816
25320
  const cy = iconSize / 2;
24817
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
24818
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25321
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25322
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24819
25323
  }
24820
25324
  }
24821
25325
  for (const edge of layout.edges) {
@@ -24904,10 +25408,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24904
25408
  const iconY = iconPad;
24905
25409
  const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24906
25410
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25411
+ const iconColor = labelColor;
24907
25412
  const cx = iconSize / 2;
24908
25413
  const cy = iconSize / 2;
24909
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
24910
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25414
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25415
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24911
25416
  }
24912
25417
  }
24913
25418
  if (hasAncestorTrail) {
@@ -25004,16 +25509,16 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
25004
25509
  const computedLayout = legendHandle.getLayout();
25005
25510
  if (computedLayout.activeCapsule?.addonX != null) {
25006
25511
  const capsule = computedLayout.activeCapsule;
25007
- const groupKey = capsule.groupName.toLowerCase();
25008
- const isHidden = hiddenAttributes?.has(groupKey) ?? false;
25512
+ const groupKey2 = capsule.groupName.toLowerCase();
25513
+ const isHidden = hiddenAttributes?.has(groupKey2) ?? false;
25009
25514
  const activeGroupEl = legendParentBase.select(
25010
- `[data-legend-group="${groupKey}"]`
25515
+ `[data-legend-group="${groupKey2}"]`
25011
25516
  );
25012
25517
  if (!activeGroupEl.empty()) {
25013
25518
  const eyeX = capsule.addonX;
25014
25519
  const eyeY = (LEGEND_HEIGHT - LEGEND_EYE_SIZE) / 2;
25015
25520
  const hitPad = 6;
25016
- const eyeG = activeGroupEl.append("g").attr("class", "org-legend-eye").attr("data-legend-visibility", groupKey).style("cursor", "pointer").attr("opacity", isHidden ? 0.4 : 0.7);
25521
+ const eyeG = activeGroupEl.append("g").attr("class", "org-legend-eye").attr("data-legend-visibility", groupKey2).style("cursor", "pointer").attr("opacity", isHidden ? 0.4 : 0.7);
25017
25522
  eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width", LEGEND_EYE_SIZE + hitPad * 2).attr("height", LEGEND_EYE_SIZE + hitPad * 2).attr("fill", "transparent").attr("pointer-events", "all");
25018
25523
  eyeG.append("path").attr("d", isHidden ? EYE_CLOSED_PATH : EYE_OPEN_PATH).attr("transform", `translate(${eyeX}, ${eyeY})`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.2).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
25019
25524
  }
@@ -25829,7 +26334,7 @@ function nodeStroke2(_palette, nodeColor2) {
25829
26334
  }
25830
26335
  function containerFill2(palette, isDark, nodeColor2) {
25831
26336
  if (nodeColor2) {
25832
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26337
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25833
26338
  }
25834
26339
  return mix(palette.surface, palette.bg, 40);
25835
26340
  }
@@ -26132,14 +26637,14 @@ function renderLegend(parent, legendGroups, palette, isDark, activeTagGroup, fix
26132
26637
  const computedLayout = legendHandle.getLayout();
26133
26638
  if (computedLayout.activeCapsule?.addonX != null) {
26134
26639
  const capsule = computedLayout.activeCapsule;
26135
- const groupKey = capsule.groupName.toLowerCase();
26136
- const isHidden = hiddenAttributes?.has(groupKey) ?? false;
26137
- const activeGroupEl = parent.select(`[data-legend-group="${groupKey}"]`);
26640
+ const groupKey2 = capsule.groupName.toLowerCase();
26641
+ const isHidden = hiddenAttributes?.has(groupKey2) ?? false;
26642
+ const activeGroupEl = parent.select(`[data-legend-group="${groupKey2}"]`);
26138
26643
  if (!activeGroupEl.empty()) {
26139
26644
  const eyeX = capsule.addonX;
26140
26645
  const eyeY = (LEGEND_HEIGHT - LEGEND_EYE_SIZE) / 2;
26141
26646
  const hitPad = 6;
26142
- const eyeG = activeGroupEl.append("g").attr("class", "sitemap-legend-eye").attr("data-legend-visibility", groupKey).style("cursor", "pointer").attr("opacity", isHidden ? 0.4 : 0.7);
26647
+ const eyeG = activeGroupEl.append("g").attr("class", "sitemap-legend-eye").attr("data-legend-visibility", groupKey2).style("cursor", "pointer").attr("opacity", isHidden ? 0.4 : 0.7);
26143
26648
  eyeG.append("rect").attr("x", eyeX - hitPad).attr("y", eyeY - hitPad).attr("width", LEGEND_EYE_SIZE + hitPad * 2).attr("height", LEGEND_EYE_SIZE + hitPad * 2).attr("fill", "transparent").attr("pointer-events", "all");
26144
26649
  eyeG.append("path").attr("d", isHidden ? EYE_CLOSED_PATH : EYE_OPEN_PATH).attr("transform", `translate(${eyeX}, ${eyeY})`).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.2).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
26145
26650
  }
@@ -28726,7 +29231,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28726
29231
  onToggleDescriptions,
28727
29232
  onToggleControlsExpand,
28728
29233
  exportMode = false,
28729
- controlsHost
29234
+ controlsHost,
29235
+ rampDomain
28730
29236
  } = options ?? {};
28731
29237
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28732
29238
  const width = exportDims?.width ?? container.clientWidth;
@@ -28746,8 +29252,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28746
29252
  const sTitleY = sctx.structural(TITLE_Y);
28747
29253
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28748
29254
  const hasRamp = nodeValues.length > 0;
28749
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28750
- const rampMax = Math.max(...nodeValues);
29255
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29256
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28751
29257
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28752
29258
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28753
29259
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28899,7 +29405,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28899
29405
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28900
29406
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28901
29407
  if (group.collapsed) {
28902
- const fillColor = isDark ? palette.surface : palette.bg;
29408
+ const fillColor = themeBaseBg(palette, isDark);
28903
29409
  const strokeColor = palette.border;
28904
29410
  groupG.append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", group.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", sNodeStrokeWidth);
28905
29411
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -28927,8 +29433,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28927
29433
  const color = edgeColorMap.get(i) ?? palette.textMuted;
28928
29434
  if (hidden.size > 0) {
28929
29435
  let isHidden = false;
28930
- for (const [groupKey, hiddenVals] of hidden) {
28931
- const val = le.metadata[groupKey];
29436
+ for (const [groupKey2, hiddenVals] of hidden) {
29437
+ const val = le.metadata[groupKey2];
28932
29438
  if (val && hiddenVals.has(val.toLowerCase())) {
28933
29439
  isHidden = true;
28934
29440
  break;
@@ -29028,8 +29534,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29028
29534
  if (!node) continue;
29029
29535
  if (hidden.size > 0) {
29030
29536
  let isHidden = false;
29031
- for (const [groupKey, hiddenVals] of hidden) {
29032
- const val = node.metadata[groupKey];
29537
+ for (const [groupKey2, hiddenVals] of hidden) {
29538
+ const val = node.metadata[groupKey2];
29033
29539
  if (val && hiddenVals.has(val.toLowerCase())) {
29034
29540
  isHidden = true;
29035
29541
  break;
@@ -29255,6 +29761,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29255
29761
  });
29256
29762
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29257
29763
  }
29764
+ if (!exportDims && !exportMode) {
29765
+ const iconSize = 14;
29766
+ const focusG = svg.append("g").attr("class", "bl-focus-icon").attr("data-export-ignore", "true").style("display", "none").style("pointer-events", "auto").style("cursor", "pointer");
29767
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29768
+ const cx = iconSize / 2;
29769
+ const cy = iconSize / 2;
29770
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", palette.bg).attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
29771
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29772
+ }
29258
29773
  }
29259
29774
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29260
29775
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29851,7 +30366,7 @@ function shuffle(a, r) {
29851
30366
  return x;
29852
30367
  }
29853
30368
  function flatten(d) {
29854
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30369
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29855
30370
  const pts = [];
29856
30371
  let i = 0, cx = 0, cy = 0, cmd = "";
29857
30372
  const num = () => parseFloat(toks[i++]);
@@ -29895,17 +30410,10 @@ function flatten(d) {
29895
30410
  }
29896
30411
  return pts;
29897
30412
  }
29898
- function segPoint(p1, p2, p3, p4) {
29899
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29900
- if (Math.abs(den) < 1e-9) return null;
29901
- const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
29902
- return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
29903
- }
29904
- function countSplineCrossings(layout) {
29905
- const center = /* @__PURE__ */ new Map();
29906
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29907
- for (const g of layout.groups)
29908
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30413
+ function flatPolys(layout) {
30414
+ const key = layout.edges;
30415
+ const hit = FLAT_CACHE.get(key);
30416
+ if (hit) return hit;
29909
30417
  const polys = layout.edges.map((e) => {
29910
30418
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29911
30419
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29915,8 +30423,24 @@ function countSplineCrossings(layout) {
29915
30423
  if (p.y < y0) y0 = p.y;
29916
30424
  if (p.y > y1) y1 = p.y;
29917
30425
  }
29918
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30426
+ return { pts, x0, y0, x1, y1 };
29919
30427
  });
30428
+ FLAT_CACHE.set(key, polys);
30429
+ return polys;
30430
+ }
30431
+ function segPoint(p1, p2, p3, p4) {
30432
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30433
+ if (Math.abs(den) < 1e-9) return null;
30434
+ const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
30435
+ return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
30436
+ }
30437
+ function countSplineCrossings(layout, floor = Infinity) {
30438
+ const center = /* @__PURE__ */ new Map();
30439
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30440
+ for (const g of layout.groups)
30441
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30442
+ const polys = flatPolys(layout);
30443
+ const edges = layout.edges;
29920
30444
  const R = 34;
29921
30445
  let total = 0;
29922
30446
  for (let a = 0; a < polys.length; a++)
@@ -29924,23 +30448,33 @@ function countSplineCrossings(layout) {
29924
30448
  const A = polys[a], B = polys[b];
29925
30449
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29926
30450
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29927
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30451
+ const ea = edges[a], eb = edges[b];
30452
+ let sh0, sh1;
30453
+ if (ea.source === eb.source || ea.source === eb.target)
30454
+ sh0 = center.get(ea.source);
30455
+ if (ea.target === eb.source || ea.target === eb.target)
30456
+ sh1 = center.get(ea.target);
29928
30457
  const hits = [];
29929
- for (let i = 1; i < A.pts.length; i++)
29930
- for (let j = 1; j < B.pts.length; j++) {
29931
- const p = segPoint(
29932
- A.pts[i - 1],
29933
- A.pts[i],
29934
- B.pts[j - 1],
29935
- B.pts[j]
29936
- );
30458
+ const ap = A.pts, bp = B.pts;
30459
+ for (let i = 1; i < ap.length; i++) {
30460
+ const a0 = ap[i - 1], a1 = ap[i];
30461
+ const axMin = a0.x < a1.x ? a0.x : a1.x, axMax = a0.x > a1.x ? a0.x : a1.x, ayMin = a0.y < a1.y ? a0.y : a1.y, ayMax = a0.y > a1.y ? a0.y : a1.y;
30462
+ for (let j = 1; j < bp.length; j++) {
30463
+ const b0 = bp[j - 1], b1 = bp[j];
30464
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30465
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30466
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30467
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30468
+ const p = segPoint(a0, a1, b0, b1);
29937
30469
  if (!p) continue;
29938
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29939
- continue;
30470
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30471
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29940
30472
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29941
30473
  hits.push(p);
29942
30474
  }
30475
+ }
29943
30476
  total += hits.length;
30477
+ if (total > floor) return total;
29944
30478
  }
29945
30479
  return total;
29946
30480
  }
@@ -29978,17 +30512,8 @@ function detectEdgeOverlaps(layout, opts) {
29978
30512
  w: g.width,
29979
30513
  h: g.height
29980
30514
  });
29981
- const polys = layout.edges.map((e) => {
29982
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29983
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29984
- for (const p of pts) {
29985
- if (p.x < x0) x0 = p.x;
29986
- if (p.x > x1) x1 = p.x;
29987
- if (p.y < y0) y0 = p.y;
29988
- if (p.y > y1) y1 = p.y;
29989
- }
29990
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29991
- });
30515
+ const polys = flatPolys(layout);
30516
+ const edges = layout.edges;
29992
30517
  const runs = [];
29993
30518
  for (let a = 0; a < polys.length; a++)
29994
30519
  for (let b = a + 1; b < polys.length; b++) {
@@ -29996,7 +30521,12 @@ function detectEdgeOverlaps(layout, opts) {
29996
30521
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29997
30522
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29998
30523
  continue;
29999
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30524
+ const ea = edges[a], eb = edges[b];
30525
+ let shr0, shr1;
30526
+ if (ea.source === eb.source || ea.source === eb.target)
30527
+ shr0 = rect.get(ea.source);
30528
+ if (ea.target === eb.source || ea.target === eb.target)
30529
+ shr1 = rect.get(ea.target);
30000
30530
  let run = [];
30001
30531
  let runLen = 0;
30002
30532
  const flush = () => {
@@ -30010,7 +30540,7 @@ function detectEdgeOverlaps(layout, opts) {
30010
30540
  runLen = 0;
30011
30541
  };
30012
30542
  for (const p of A.pts) {
30013
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30543
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
30014
30544
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
30015
30545
  if (covered) {
30016
30546
  if (run.length)
@@ -30045,9 +30575,10 @@ function detectEdgeNodePierces(layout, opts) {
30045
30575
  });
30046
30576
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
30047
30577
  const out = [];
30578
+ const polys = flatPolys(layout);
30048
30579
  layout.edges.forEach((e, idx) => {
30049
30580
  if (e.points.length < 2) return;
30050
- const poly = flatten(splineGen(e.points) ?? "");
30581
+ const poly = polys[idx].pts;
30051
30582
  for (const r of rects) {
30052
30583
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
30053
30584
  continue;
@@ -30394,8 +30925,10 @@ function edgeLength(layout) {
30394
30925
  );
30395
30926
  return total;
30396
30927
  }
30397
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30928
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30398
30929
  const hideDescriptions = opts?.hideDescriptions ?? false;
30930
+ const onProgress = opts?.onProgress;
30931
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30399
30932
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30400
30933
  if (collapseInfo) {
30401
30934
  const missing = /* @__PURE__ */ new Set();
@@ -30809,17 +31342,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30809
31342
  seed: s
30810
31343
  });
30811
31344
  const badness = (lay, floor) => {
30812
- const x = countSplineCrossings(lay);
31345
+ const x = countSplineCrossings(lay, floor);
30813
31346
  if (x > floor) return Infinity;
30814
31347
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30815
31348
  };
30816
31349
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31350
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31351
+ let progressDone = 0;
31352
+ const step = async (phase) => {
31353
+ if (!onProgress) return;
31354
+ onProgress(++progressDone, progressTotal, phase);
31355
+ await tick();
31356
+ };
30817
31357
  const pool = [];
30818
31358
  for (const cfg of configs) {
30819
31359
  try {
30820
31360
  pool.push(place(cfg));
30821
31361
  } catch {
30822
31362
  }
31363
+ await step("Optimizing layout");
30823
31364
  }
30824
31365
  if (!pool.length)
30825
31366
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30828,9 +31369,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30828
31369
  layered = layeredCandidates(parsed, sizes);
30829
31370
  } catch {
30830
31371
  }
30831
- pool.sort(
30832
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30833
- );
31372
+ const fastKey = /* @__PURE__ */ new Map();
31373
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31374
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30834
31375
  const refineK = Math.min(REFINE_K, pool.length);
30835
31376
  let best = pool[0];
30836
31377
  let bestObj = Infinity;
@@ -30845,7 +31386,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30845
31386
  best = lay;
30846
31387
  }
30847
31388
  };
30848
- for (const lay of pool.slice(0, refineK)) consider(lay);
31389
+ for (const lay of pool.slice(0, refineK)) {
31390
+ consider(lay);
31391
+ await step("Refining layout");
31392
+ }
30849
31393
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30850
31394
  const extra = [];
30851
31395
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30861,9 +31405,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30861
31405
  } catch {
30862
31406
  }
30863
31407
  }
30864
- extra.sort(
30865
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30866
- );
31408
+ const extraKey = /* @__PURE__ */ new Map();
31409
+ for (const lay of extra)
31410
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31411
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30867
31412
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30868
31413
  }
30869
31414
  for (const lay of layered) {
@@ -30890,7 +31435,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30890
31435
  }
30891
31436
  return best;
30892
31437
  }
30893
- var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31438
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30894
31439
  var init_layout_search = __esm({
30895
31440
  "src/boxes-and-lines/layout-search.ts"() {
30896
31441
  "use strict";
@@ -30902,6 +31447,8 @@ var init_layout_search = __esm({
30902
31447
  ESCALATE_SEEDS = 18;
30903
31448
  ESCALATE_REFINE = 10;
30904
31449
  splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
31450
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31451
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30905
31452
  GROUP_LABEL_ZONE2 = 32;
30906
31453
  }
30907
31454
  });
@@ -30986,12 +31533,15 @@ function computeNodeSize(node, reserveValueRow) {
30986
31533
  }
30987
31534
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30988
31535
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30989
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31536
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30990
31537
  ...layoutOptions?.hideDescriptions !== void 0 && {
30991
31538
  hideDescriptions: layoutOptions.hideDescriptions
30992
31539
  },
30993
31540
  ...layoutOptions?.previousPositions !== void 0 && {
30994
31541
  previousPositions: layoutOptions.previousPositions
31542
+ },
31543
+ ...layoutOptions?.onProgress !== void 0 && {
31544
+ onProgress: layoutOptions.onProgress
30995
31545
  }
30996
31546
  });
30997
31547
  return attachNotes(
@@ -31776,7 +32326,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31776
32326
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31777
32327
  const availWidth = containerWidth;
31778
32328
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31779
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32329
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32330
+ availWidth,
32331
+ layout.width,
32332
+ availHeight,
32333
+ layout.height
32334
+ );
31780
32335
  let renderLayout = layout;
31781
32336
  if (ctx.factor < 1) {
31782
32337
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31785,17 +32340,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31785
32340
  hiddenCounts.set(n.id, n.hiddenCount);
31786
32341
  }
31787
32342
  }
31788
- renderLayout = layoutMindmap(parsed, palette, {
32343
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31789
32344
  interactive: !isExport,
31790
32345
  ...hiddenCounts.size > 0 && { hiddenCounts },
31791
32346
  activeTagGroup: activeTagGroup ?? null,
31792
32347
  ...hideDescriptions !== void 0 && { hideDescriptions },
31793
- ctx
32348
+ ctx: c
31794
32349
  });
32350
+ renderLayout = relayout(ctx);
32351
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32352
+ const refit = Math.min(
32353
+ availWidth / renderLayout.width,
32354
+ availHeight / renderLayout.height
32355
+ );
32356
+ if (refit >= 0.999) break;
32357
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32358
+ renderLayout = relayout(ctx);
32359
+ }
31795
32360
  }
31796
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31797
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31798
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32361
+ const fitScale = isExport ? 1 : Math.min(
32362
+ 1,
32363
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32364
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32365
+ );
32366
+ const scaledWidth = renderLayout.width * fitScale;
32367
+ const scaledHeight = renderLayout.height * fitScale;
32368
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32369
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32370
+ const mainG = svg.append("g").attr(
32371
+ "transform",
32372
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32373
+ );
31799
32374
  if (ctx.isBelowFloor) {
31800
32375
  svg.attr("width", "100%");
31801
32376
  }
@@ -39160,10 +39735,10 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
39160
39735
  });
39161
39736
  legendG.selectAll("[data-legend-group]").classed("infra-legend-group", true);
39162
39737
  for (const group of legendGroups) {
39163
- const groupKey = group.name.toLowerCase();
39738
+ const groupKey2 = group.name.toLowerCase();
39164
39739
  for (const entry of group.entries) {
39165
39740
  const entryEl = legendG.select(
39166
- `[data-legend-group="${groupKey}"] [data-legend-entry="${entry.value.toLowerCase()}"]`
39741
+ `[data-legend-group="${groupKey2}"] [data-legend-entry="${entry.value.toLowerCase()}"]`
39167
39742
  );
39168
39743
  if (!entryEl.empty()) {
39169
39744
  entryEl.attr("data-legend-entry", entry.key.toLowerCase()).attr("data-legend-color", entry.color).attr("data-legend-type", group.type).attr(
@@ -41142,7 +41717,7 @@ __export(renderer_exports11, {
41142
41717
  import * as d3Selection17 from "d3-selection";
41143
41718
  import * as d3Shape9 from "d3-shape";
41144
41719
  function analysisBlockChrome(palette, isDark) {
41145
- const surfaceBg = isDark ? palette.surface : palette.bg;
41720
+ const surfaceBg = themeBaseBg(palette, isDark);
41146
41721
  return {
41147
41722
  fill: mix(palette.surface, palette.bg, 40),
41148
41723
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -45853,7 +46428,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45853
46428
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45854
46429
  const gw = group.width + sGroupExtraPadding * 2;
45855
46430
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45856
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46431
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45857
46432
  const strokeColor = group.color ?? palette.textMuted;
45858
46433
  const groupWrapper = contentG.append("g").attr("class", "st-group-wrapper").attr("data-line-number", String(group.lineNumber)).attr("data-group-id", group.id).attr("data-group-toggle", group.id).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", `Collapse group ${group.label}`).style("cursor", "pointer");
45859
46434
  groupWrapper.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "st-group");
@@ -50045,6 +50620,16 @@ __export(resolver_exports, {
50045
50620
  isSubNationalUsExtent: () => isSubNationalUsExtent,
50046
50621
  resolveMap: () => resolveMap
50047
50622
  });
50623
+ function containerOvershoot(span, usOriented) {
50624
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50625
+ return Math.max(
50626
+ CONTAINER_OVERSHOOT_MIN,
50627
+ Math.min(
50628
+ CONTAINER_OVERSHOOT_MAX,
50629
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50630
+ )
50631
+ );
50632
+ }
50048
50633
  function bboxArea(b) {
50049
50634
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
50050
50635
  }
@@ -50541,7 +51126,7 @@ function resolveMap(parsed, data) {
50541
51126
  const containerUnion = unionExtent(containerBoxes, points);
50542
51127
  if (containerUnion)
50543
51128
  extent3 = pad(
50544
- clampContainerToCluster(containerUnion, points),
51129
+ clampContainerToCluster(containerUnion, points, usOriented),
50545
51130
  PAD_FRACTION
50546
51131
  );
50547
51132
  }
@@ -50625,17 +51210,21 @@ function mostCommonCountry(regions, poiCountries) {
50625
51210
  }
50626
51211
  return best;
50627
51212
  }
50628
- function clampContainerToCluster(container, points) {
51213
+ function clampContainerToCluster(container, points, usOriented) {
50629
51214
  const poi = unionExtent([], points);
50630
51215
  if (!poi) return container;
50631
51216
  let [[west, south], [east, north]] = container;
50632
51217
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50633
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50634
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50635
- if (east <= 180 && pEast <= 180) {
50636
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50637
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50638
- }
51218
+ const over = containerOvershoot(
51219
+ Math.max(pEast - pWest, pNorth - pSouth),
51220
+ usOriented
51221
+ );
51222
+ south = Math.max(south, pSouth - over);
51223
+ north = Math.min(north, pNorth + over);
51224
+ const wOver = pWest - over;
51225
+ const eOver = pEast + over;
51226
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
51227
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
50639
51228
  return [
50640
51229
  [west, south],
50641
51230
  [east, north]
@@ -50653,7 +51242,7 @@ function firstError(diags) {
50653
51242
  const e = diags.find((d) => d.severity === "error");
50654
51243
  return e ? formatDgmoError(e) : null;
50655
51244
  }
50656
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_DEG, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
51245
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_MAX, CONTAINER_OVERSHOOT_MIN, CONTAINER_OVERSHOOT_DECAY, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50657
51246
  var init_resolver2 = __esm({
50658
51247
  "src/map/resolver.ts"() {
50659
51248
  "use strict";
@@ -50666,7 +51255,9 @@ var init_resolver2 = __esm({
50666
51255
  WORLD_LAT_SOUTH = -58;
50667
51256
  WORLD_LAT_NORTH = 78;
50668
51257
  POI_ZOOM_FLOOR_DEG = 7;
50669
- CONTAINER_OVERSHOOT_DEG = 8;
51258
+ CONTAINER_OVERSHOOT_MAX = 8;
51259
+ CONTAINER_OVERSHOOT_MIN = 3;
51260
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50670
51261
  US_NATIONAL_LON_SPAN = 48;
50671
51262
  CONUS_BBOX = [
50672
51263
  [-125, 25],
@@ -50832,12 +51423,12 @@ function tierBand(maxSpanDeg) {
50832
51423
  }
50833
51424
  function labelBudget(width, height, band) {
50834
51425
  const bandCap = {
50835
- world: 7,
50836
- continental: 6,
50837
- regional: 5,
50838
- local: 4
51426
+ world: 10,
51427
+ continental: 9,
51428
+ regional: 7,
51429
+ local: 6
50839
51430
  };
50840
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51431
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50841
51432
  return Math.max(0, Math.min(area2, bandCap[band]));
50842
51433
  }
50843
51434
  function waterEligible(tier, kind, band) {
@@ -50891,6 +51482,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50891
51482
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50892
51483
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50893
51484
  }
51485
+ function rectDist2(px, py, x0, y0, x1, y1) {
51486
+ const dx = Math.max(x0 - px, 0, px - x1);
51487
+ const dy = Math.max(y0 - py, 0, py - y1);
51488
+ return dx * dx + dy * dy;
51489
+ }
50894
51490
  function rectFits(r, width, height) {
50895
51491
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50896
51492
  }
@@ -50909,6 +51505,7 @@ function placeContextLabels(args) {
50909
51505
  palette,
50910
51506
  project,
50911
51507
  collides,
51508
+ contentPoints,
50912
51509
  overLand
50913
51510
  } = args;
50914
51511
  void projection;
@@ -50979,8 +51576,17 @@ function placeContextLabels(args) {
50979
51576
  const [x0, y0, x1, y1] = c.bbox;
50980
51577
  const w = x1 - x0;
50981
51578
  const h = y1 - y0;
50982
- return { c, w, h, area: w * h };
50983
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51579
+ let dist = Infinity;
51580
+ if (contentPoints?.length) {
51581
+ for (const p of contentPoints) {
51582
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51583
+ if (d < dist) dist = d;
51584
+ }
51585
+ }
51586
+ return { c, w, h, area: w * h, dist };
51587
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51588
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51589
+ );
50984
51590
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50985
51591
  let ci = 0;
50986
51592
  for (const r of ranked) {
@@ -50997,7 +51603,7 @@ function placeContextLabels(args) {
50997
51603
  );
50998
51604
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50999
51605
  const fade = Math.round(t * COUNTRY_FADE_MAX);
51000
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51606
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
51001
51607
  const text = c.name;
51002
51608
  const tw = labelWidth(text, 0, fontSize);
51003
51609
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -51010,6 +51616,9 @@ function placeContextLabels(args) {
51010
51616
  letterSpacing: 0,
51011
51617
  color,
51012
51618
  fontSize,
51619
+ // Multi-position dodging: carry the ordered interior positions through to the
51620
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51621
+ ...c.positions ? { positions: c.positions } : {},
51013
51622
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
51014
51623
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
51015
51624
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -51024,39 +51633,53 @@ function placeContextLabels(args) {
51024
51633
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
51025
51634
  const waterCap = budget - Math.min(2, countryCount);
51026
51635
  let waterPlaced = 0;
51027
- for (const cand of candidates) {
51028
- if (placed.length >= budget) break;
51029
- if (cand.italic && waterPlaced >= waterCap) continue;
51636
+ const gateAt = (cx, cy, cand) => {
51030
51637
  const rect = rectAround(
51031
- cand.cx,
51032
- cand.cy,
51638
+ cx,
51639
+ cy,
51033
51640
  cand.lines,
51034
51641
  cand.letterSpacing,
51035
51642
  cand.fontSize
51036
51643
  );
51037
- if (!rectFits(rect, width, height)) continue;
51644
+ if (!rectFits(rect, width, height)) return null;
51038
51645
  if (cand.italic && overLand) {
51039
51646
  const inset = 2;
51040
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51647
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51041
51648
  const touchesLand = cand.lines.some((line11, li) => {
51042
51649
  const lw = labelWidth(line11, cand.letterSpacing);
51043
- const x0 = cand.cx - lw / 2 + inset;
51044
- const x1 = cand.cx + lw / 2 - inset;
51045
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51650
+ const x0 = cx - lw / 2 + inset;
51651
+ const x1 = cx + lw / 2 - inset;
51652
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
51046
51653
  const base = top + li * LINE_HEIGHT;
51047
51654
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
51048
51655
  (y) => xs.some((x) => overLand(x, y))
51049
51656
  );
51050
51657
  });
51051
- if (touchesLand) continue;
51658
+ if (touchesLand) return null;
51052
51659
  }
51053
- if (collides(rect)) continue;
51054
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
51055
- placedRects.push(rect);
51660
+ if (collides(rect)) return null;
51661
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51662
+ return null;
51663
+ return rect;
51664
+ };
51665
+ for (const cand of candidates) {
51666
+ if (placed.length >= budget) break;
51667
+ if (cand.italic && waterPlaced >= waterCap) continue;
51668
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51669
+ let chosen = null;
51670
+ for (const [px, py] of positions) {
51671
+ const rect = gateAt(px, py, cand);
51672
+ if (rect) {
51673
+ chosen = { x: px, y: py, rect };
51674
+ break;
51675
+ }
51676
+ }
51677
+ if (!chosen) continue;
51678
+ placedRects.push(chosen.rect);
51056
51679
  if (cand.italic) waterPlaced++;
51057
51680
  placed.push({
51058
- x: cand.cx,
51059
- y: cand.cy,
51681
+ x: chosen.x,
51682
+ y: chosen.y,
51060
51683
  text: cand.text,
51061
51684
  anchor: "middle",
51062
51685
  color: cand.color,
@@ -51074,7 +51697,7 @@ function placeContextLabels(args) {
51074
51697
  }
51075
51698
  return placed;
51076
51699
  }
51077
- var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, KIND_ORDER;
51700
+ var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, MAX_COUNTRY_POSITIONS, COUNTRY_POS_GRID, COUNTRY_POS_TOPN_MARGIN, KIND_ORDER;
51078
51701
  var init_context_labels = __esm({
51079
51702
  "src/map/context-labels.ts"() {
51080
51703
  "use strict";
@@ -51092,6 +51715,9 @@ var init_context_labels = __esm({
51092
51715
  COUNTRY_SIZE_FRAC_MIN = 0.06;
51093
51716
  COUNTRY_SIZE_FRAC_MAX = 0.32;
51094
51717
  COUNTRY_FADE_MAX = 45;
51718
+ MAX_COUNTRY_POSITIONS = 4;
51719
+ COUNTRY_POS_GRID = 5;
51720
+ COUNTRY_POS_TOPN_MARGIN = 3;
51095
51721
  KIND_ORDER = {
51096
51722
  ocean: 0,
51097
51723
  sea: 1,
@@ -51185,6 +51811,84 @@ function decodeLayer(topo) {
51185
51811
  decodeCache.set(topo, out);
51186
51812
  return out;
51187
51813
  }
51814
+ function countryLabelPositions(args) {
51815
+ const { geometry, bounds, project, width, height, curated } = args;
51816
+ const w0 = bounds[0][0];
51817
+ const s0 = bounds[0][1];
51818
+ const e0 = bounds[1][0];
51819
+ const n0 = bounds[1][1];
51820
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51821
+ return mkCurated(curated, project);
51822
+ }
51823
+ const N = COUNTRY_POS_GRID;
51824
+ const onLand = [];
51825
+ const kept = [];
51826
+ for (let i = 0; i < N; i++) {
51827
+ onLand[i] = [];
51828
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51829
+ for (let j = 0; j < N; j++) {
51830
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51831
+ const land = pointInGeometry(geometry, lon, lat);
51832
+ onLand[i][j] = land;
51833
+ if (!land) continue;
51834
+ const p = project(lon, lat);
51835
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51836
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51837
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51838
+ }
51839
+ }
51840
+ if (!kept.length) return mkCurated(curated, project);
51841
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51842
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51843
+ const interiorness = (c) => {
51844
+ let n = 0;
51845
+ for (let di = -1; di <= 1; di++)
51846
+ for (let dj = -1; dj <= 1; dj++) {
51847
+ if (di === 0 && dj === 0) continue;
51848
+ const ni = c.i + di;
51849
+ const nj = c.j + dj;
51850
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51851
+ }
51852
+ return n;
51853
+ };
51854
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51855
+ const pool = [...kept];
51856
+ pool.sort((a, b) => {
51857
+ const d = interiorness(b) - interiorness(a);
51858
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51859
+ });
51860
+ const ordered = [pool.shift()];
51861
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51862
+ let bestIdx = 0;
51863
+ let bestMin = -1;
51864
+ for (let k = 0; k < pool.length; k++) {
51865
+ const c = pool[k];
51866
+ let minD = Infinity;
51867
+ for (const o of ordered) {
51868
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51869
+ if (d < minD) minD = d;
51870
+ }
51871
+ if (minD > bestMin) {
51872
+ bestMin = minD;
51873
+ bestIdx = k;
51874
+ }
51875
+ }
51876
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51877
+ }
51878
+ const grid = ordered.map((c) => ({
51879
+ lonLat: [c.lon, c.lat],
51880
+ screen: [c.sx, c.sy]
51881
+ }));
51882
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51883
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51884
+ return out;
51885
+ }
51886
+ function mkCurated(curated, project) {
51887
+ if (!curated) return [];
51888
+ const p = project(curated[0], curated[1]);
51889
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51890
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51891
+ }
51188
51892
  function projectionFor(family, extent3) {
51189
51893
  switch (family) {
51190
51894
  case "albers-usa":
@@ -52012,6 +52716,8 @@ function layoutMap(resolvedIn, data, size, opts) {
52012
52716
  if (p[1] < minY) minY = p[1];
52013
52717
  if (p[1] > maxY) maxY = p[1];
52014
52718
  }
52719
+ r.bbox = [minX, minY, maxX, maxY];
52720
+ r.rings = rings;
52015
52721
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
52016
52722
  });
52017
52723
  const fillAt = (x, y) => {
@@ -53202,6 +53908,7 @@ function layoutMap(resolvedIn, data, size, opts) {
53202
53908
  for (const box of insets)
53203
53909
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
53204
53910
  const countryCandidates = [];
53911
+ const rawCountries = [];
53205
53912
  for (const f of worldLayer.values()) {
53206
53913
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
53207
53914
  if (!iso || regionById.has(iso)) continue;
@@ -53218,11 +53925,59 @@ function layoutMap(resolvedIn, data, size, opts) {
53218
53925
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
53219
53926
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
53220
53927
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
53221
- countryCandidates.push({
53928
+ rawCountries.push({
53929
+ f,
53930
+ iso,
53222
53931
  name: f.properties?.name ?? iso,
53223
53932
  bbox: [x0, y0, x1, y1],
53224
53933
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
53225
- curatedAnchor: !!anchorLngLat
53934
+ curatedLngLat: anchorLngLat ?? null,
53935
+ area: (x1 - x0) * (y1 - y0)
53936
+ });
53937
+ }
53938
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53939
+ const cBudget = labelBudget(width, height, cBand);
53940
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53941
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53942
+ const rankOrder = rawCountries.map((r, idx) => {
53943
+ let dist = Infinity;
53944
+ const [x0, y0, x1, y1] = r.bbox;
53945
+ for (const [px, py] of contentPoints) {
53946
+ const dx = Math.max(x0 - px, 0, px - x1);
53947
+ const dy = Math.max(y0 - py, 0, py - y1);
53948
+ const d = dx * dx + dy * dy;
53949
+ if (d < dist) dist = d;
53950
+ }
53951
+ return { idx, area: r.area, dist };
53952
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53953
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53954
+ ).slice(0, topN);
53955
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53956
+ for (let i = 0; i < rawCountries.length; i++) {
53957
+ const r = rawCountries[i];
53958
+ let anchor = r.anchor;
53959
+ let positions;
53960
+ if (genIdx.has(i) && anchor) {
53961
+ const gb = geoBounds2(r.f);
53962
+ const gen = countryLabelPositions({
53963
+ geometry: r.f.geometry,
53964
+ bounds: gb,
53965
+ project,
53966
+ width,
53967
+ height,
53968
+ curated: r.curatedLngLat
53969
+ });
53970
+ if (gen.length) {
53971
+ positions = gen.map((p) => p.screen);
53972
+ anchor = positions[0];
53973
+ }
53974
+ }
53975
+ countryCandidates.push({
53976
+ name: r.name,
53977
+ bbox: r.bbox,
53978
+ anchor,
53979
+ curatedAnchor: !!r.curatedLngLat,
53980
+ ...positions ? { positions } : {}
53226
53981
  });
53227
53982
  }
53228
53983
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -53256,6 +54011,7 @@ function layoutMap(resolvedIn, data, size, opts) {
53256
54011
  palette,
53257
54012
  project,
53258
54013
  collides,
54014
+ contentPoints,
53259
54015
  // Water labels must stay over open water — `fillAt` returns the ocean
53260
54016
  // backdrop colour off-land and a region fill on-land (lakes/states count
53261
54017
  // as land here, which is the safe side for an ocean name).
@@ -53508,7 +54264,7 @@ function ringToCoastPaths(ring, frame) {
53508
54264
  function coastlineOuterRings(regions, minExtent, frame) {
53509
54265
  const paths = [];
53510
54266
  for (const r of regions) {
53511
- const rings = parsePathRings(r.d);
54267
+ const rings = r.rings ?? parsePathRings(r.d);
53512
54268
  for (let i = 0; i < rings.length; i++) {
53513
54269
  const ring = rings[i];
53514
54270
  if (ring.length < 3) continue;
@@ -53703,14 +54459,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53703
54459
  if (decoCluster !== void 0)
53704
54460
  gPatch.attr("data-cluster-deco", decoCluster);
53705
54461
  for (const r of layout.regions) {
53706
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53707
- for (const ring of parsePathRings(r.d))
53708
- for (const [px, py] of ring) {
53709
- if (px < minX) minX = px;
53710
- if (px > maxX) maxX = px;
53711
- if (py < minY) minY = py;
53712
- if (py > maxY) maxY = py;
53713
- }
54462
+ let minX, minY, maxX, maxY;
54463
+ if (r.bbox) {
54464
+ [minX, minY, maxX, maxY] = r.bbox;
54465
+ } else {
54466
+ minX = Infinity;
54467
+ minY = Infinity;
54468
+ maxX = -Infinity;
54469
+ maxY = -Infinity;
54470
+ for (const ring of parsePathRings(r.d))
54471
+ for (const [px, py] of ring) {
54472
+ if (px < minX) minX = px;
54473
+ if (px > maxX) maxX = px;
54474
+ if (py < minY) minY = py;
54475
+ if (py > maxY) maxY = py;
54476
+ }
54477
+ }
53714
54478
  const hit = blobRects.some(
53715
54479
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53716
54480
  );
@@ -54937,7 +55701,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54937
55701
  const tasksAll = [...allTasks(parsed)];
54938
55702
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54939
55703
  const solid = parsed.options["solid-fill"] === "on";
54940
- const surfaceBg = isDark ? palette.surface : palette.bg;
55704
+ const surfaceBg = themeBaseBg(palette, isDark);
54941
55705
  const roleCount = Math.max(1, parsed.roles.length);
54942
55706
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54943
55707
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -55115,10 +55879,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
55115
55879
  const cx = roleX(i) + sColumnInset;
55116
55880
  const cw = roleColW - 2 * sColumnInset;
55117
55881
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
55118
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55882
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
55119
55883
  const headerFill = mix(
55120
55884
  roleColor,
55121
- isDark ? palette.surface : palette.bg,
55885
+ themeBaseBg(palette, isDark),
55122
55886
  30
55123
55887
  );
55124
55888
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -55706,15 +56470,15 @@ function propagateGroupTags(participantMeta, groups) {
55706
56470
  }
55707
56471
  }
55708
56472
  }
55709
- function computeReceiverInheritance(participants, messages, groupKey, participantMeta) {
56473
+ function computeReceiverInheritance(participants, messages, groupKey2, participantMeta) {
55710
56474
  const inheritance = /* @__PURE__ */ new Map();
55711
56475
  for (const p of participants) {
55712
56476
  const meta = participantMeta.get(p.id);
55713
- if (meta?.[groupKey]) continue;
56477
+ if (meta?.[groupKey2]) continue;
55714
56478
  const incomingValues = /* @__PURE__ */ new Set();
55715
56479
  for (const msg of messages) {
55716
- if (msg.to === p.id && msg.metadata?.[groupKey]) {
55717
- incomingValues.add(msg.metadata[groupKey]);
56480
+ if (msg.to === p.id && msg.metadata?.[groupKey2]) {
56481
+ incomingValues.add(msg.metadata[groupKey2]);
55718
56482
  }
55719
56483
  }
55720
56484
  if (incomingValues.size === 1) {
@@ -55740,7 +56504,7 @@ function resolveSequenceTags(parsed, activeTagGroup) {
55740
56504
  }
55741
56505
  return result;
55742
56506
  }
55743
- const groupKey = group.name.toLowerCase();
56507
+ const groupKey2 = group.name.toLowerCase();
55744
56508
  const participantMeta = /* @__PURE__ */ new Map();
55745
56509
  for (const p of parsed.participants) {
55746
56510
  participantMeta.set(p.id, { ...p.metadata || {} });
@@ -55749,27 +56513,27 @@ function resolveSequenceTags(parsed, activeTagGroup) {
55749
56513
  const inherited = computeReceiverInheritance(
55750
56514
  parsed.participants,
55751
56515
  parsed.messages,
55752
- groupKey,
56516
+ groupKey2,
55753
56517
  participantMeta
55754
56518
  );
55755
56519
  for (const [id, value] of inherited) {
55756
56520
  const meta = participantMeta.get(id);
55757
- meta[groupKey] = value;
56521
+ meta[groupKey2] = value;
55758
56522
  }
55759
56523
  if (group.defaultValue) {
55760
56524
  for (const p of parsed.participants) {
55761
56525
  const meta = participantMeta.get(p.id);
55762
- if (!(groupKey in meta)) {
55763
- meta[groupKey] = group.defaultValue;
56526
+ if (!(groupKey2 in meta)) {
56527
+ meta[groupKey2] = group.defaultValue;
55764
56528
  }
55765
56529
  }
55766
56530
  }
55767
56531
  for (const p of parsed.participants) {
55768
56532
  const meta = participantMeta.get(p.id);
55769
- result.participants.set(p.id, meta[groupKey] || void 0);
56533
+ result.participants.set(p.id, meta[groupKey2] || void 0);
55770
56534
  }
55771
56535
  for (const msg of parsed.messages) {
55772
- result.messages.set(msg.lineNumber, msg.metadata?.[groupKey] || void 0);
56536
+ result.messages.set(msg.lineNumber, msg.metadata?.[groupKey2] || void 0);
55773
56537
  }
55774
56538
  return result;
55775
56539
  }
@@ -56845,7 +57609,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56845
57609
  const groupTagColor = getTagColor(groupTagValue || void 0);
56846
57610
  const fillColor = groupTagColor ? mix(
56847
57611
  groupTagColor,
56848
- isDark ? palette.surface : palette.bg,
57612
+ themeBaseBg(palette, isDark),
56849
57613
  isDark ? 15 : 20
56850
57614
  ) : isDark ? palette.surface : palette.bg;
56851
57615
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56899,7 +57663,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56899
57663
  participantG.append("title").text("Click to expand");
56900
57664
  const pFill = effectiveTagColor ? mix(
56901
57665
  effectiveTagColor,
56902
- isDark ? palette.surface : palette.bg,
57666
+ themeBaseBg(palette, isDark),
56903
57667
  isDark ? 30 : 40
56904
57668
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56905
57669
  const pStroke = effectiveTagColor || palette.border;
@@ -57106,7 +57870,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
57106
57870
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
57107
57871
  const actTagColor = getTagColor(actTagValue);
57108
57872
  const actBaseColor = actTagColor || palette.primary;
57109
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57873
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
57110
57874
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
57111
57875
  const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
57112
57876
  if (tagKey && actTagValue) {
@@ -58811,7 +59575,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58811
59575
  const textColor = palette.text;
58812
59576
  const mutedColor = palette.border;
58813
59577
  const bgColor = palette.bg;
58814
- const bg = isDark ? palette.surface : palette.bg;
59578
+ const bg = themeBaseBg(palette, isDark);
58815
59579
  const colors = getSeriesColors(palette);
58816
59580
  const groupColorMap = /* @__PURE__ */ new Map();
58817
59581
  timelineGroups.forEach((grp, i) => {
@@ -59144,10 +59908,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59144
59908
  }
59145
59909
  },
59146
59910
  onGroupRendered: (groupName, groupEl, isActive) => {
59147
- const groupKey = groupName.toLowerCase();
59148
- groupEl.attr("data-tag-group", groupKey);
59911
+ const groupKey2 = groupName.toLowerCase();
59912
+ groupEl.attr("data-tag-group", groupKey2);
59149
59913
  if (isActive && !viewMode) {
59150
- const isSwimActive = currentSwimlaneGroup?.toLowerCase() === groupKey;
59914
+ const isSwimActive = currentSwimlaneGroup?.toLowerCase() === groupKey2;
59151
59915
  const pillWidth2 = measureLegendText(groupName, LG_PILL_FONT_SIZE) + LG_PILL_PAD;
59152
59916
  const pillXOff = LG_CAPSULE_PAD;
59153
59917
  const iconX = pillXOff + pillWidth2 + 5;
@@ -59158,9 +59922,9 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59158
59922
  iconY,
59159
59923
  isSwimActive
59160
59924
  );
59161
- iconEl.attr("data-swimlane-toggle", groupKey).on("click", (event) => {
59925
+ iconEl.attr("data-swimlane-toggle", groupKey2).on("click", (event) => {
59162
59926
  event.stopPropagation();
59163
- currentSwimlaneGroup = currentSwimlaneGroup === groupKey ? null : groupKey;
59927
+ currentSwimlaneGroup = currentSwimlaneGroup === groupKey2 ? null : groupKey2;
59164
59928
  onTagStateChange?.(
59165
59929
  currentActiveGroup,
59166
59930
  currentSwimlaneGroup
@@ -59226,9 +59990,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
59226
59990
  drawLegend2();
59227
59991
  }
59228
59992
  }
59229
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59993
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59230
59994
  const {
59231
59995
  width,
59996
+ height,
59232
59997
  tooltip,
59233
59998
  solid,
59234
59999
  textColor,
@@ -59280,9 +60045,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
59280
60045
  const rowH = ctx.structural(28);
59281
60046
  const innerHeight = rowH * sorted.length;
59282
60047
  const usedHeight = margin.top + innerHeight + margin.bottom;
60048
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
60049
+ const svgHeight = fitToContainer ? height : usedHeight;
59283
60050
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
59284
- const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", usedHeight).attr("viewBox", `0 0 ${width} ${usedHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
59285
- if (ctx.isBelowFloor) {
60051
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", svgHeight).attr("viewBox", `0 0 ${width} ${usedHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
60052
+ if (ctx.isBelowFloor && !fitToContainer) {
59286
60053
  svg.attr("width", "100%");
59287
60054
  }
59288
60055
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -61016,7 +61783,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
61016
61783
  onClickItem
61017
61784
  );
61018
61785
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
61019
- const bg = isDark ? palette.surface : palette.bg;
61786
+ const bg = themeBaseBg(palette, isDark);
61020
61787
  const getQuadrantColor = (label, defaultIdx) => {
61021
61788
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
61022
61789
  };
@@ -61355,18 +62122,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
61355
62122
  palette,
61356
62123
  viewState,
61357
62124
  options,
61358
- exportMode
62125
+ exportMode,
62126
+ isDark: theme === "dark"
61359
62127
  };
61360
62128
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
61361
62129
  return (handler ?? exportVisualization)(ctx);
61362
62130
  }
62131
+ function ctxTagOverride(ctx) {
62132
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
62133
+ }
61363
62134
  async function exportOrg(ctx) {
61364
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62135
+ const { content, theme, palette, viewState, exportMode } = ctx;
61365
62136
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
61366
62137
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
61367
62138
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
61368
62139
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
61369
- const isDark = theme === "dark";
62140
+ const isDark = ctx.isDark;
61370
62141
  const effectivePalette = await resolveExportPalette(theme, palette);
61371
62142
  const orgParsed = parseOrg2(content, effectivePalette);
61372
62143
  if (orgParsed.error) return "";
@@ -61374,7 +62145,7 @@ async function exportOrg(ctx) {
61374
62145
  const activeTagGroup = resolveActiveTagGroup(
61375
62146
  orgParsed.tagGroups,
61376
62147
  orgParsed.options["active-tag"],
61377
- viewState?.tag ?? options?.tagGroup
62148
+ ctxTagOverride(ctx)
61378
62149
  );
61379
62150
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
61380
62151
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61407,12 +62178,12 @@ async function exportOrg(ctx) {
61407
62178
  return finalizeSvgExport(container, theme, effectivePalette);
61408
62179
  }
61409
62180
  async function exportSitemap(ctx) {
61410
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62181
+ const { content, theme, palette, viewState, exportMode } = ctx;
61411
62182
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
61412
62183
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
61413
62184
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
61414
62185
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
61415
- const isDark = theme === "dark";
62186
+ const isDark = ctx.isDark;
61416
62187
  const effectivePalette = await resolveExportPalette(theme, palette);
61417
62188
  const sitemapParsed = parseSitemap2(content, effectivePalette);
61418
62189
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -61420,7 +62191,7 @@ async function exportSitemap(ctx) {
61420
62191
  const activeTagGroup = resolveActiveTagGroup(
61421
62192
  sitemapParsed.tagGroups,
61422
62193
  sitemapParsed.options["active-tag"],
61423
- viewState?.tag ?? options?.tagGroup
62194
+ ctxTagOverride(ctx)
61424
62195
  );
61425
62196
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
61426
62197
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61451,7 +62222,7 @@ async function exportSitemap(ctx) {
61451
62222
  return finalizeSvgExport(container, theme, effectivePalette);
61452
62223
  }
61453
62224
  async function exportKanban(ctx) {
61454
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62225
+ const { content, theme, palette, viewState, exportMode } = ctx;
61455
62226
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
61456
62227
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
61457
62228
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61463,11 +62234,11 @@ async function exportKanban(ctx) {
61463
62234
  document.body.appendChild(container);
61464
62235
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
61465
62236
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
61466
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
62237
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
61467
62238
  activeTagGroup: resolveActiveTagGroup(
61468
62239
  kanbanParsed.tagGroups,
61469
62240
  kanbanParsed.options["active-tag"],
61470
- viewState?.tag ?? options?.tagGroup
62241
+ ctxTagOverride(ctx)
61471
62242
  ),
61472
62243
  currentSwimlaneGroup: viewState?.swim ?? null,
61473
62244
  ...kanbanCollapsedLanes !== void 0 && {
@@ -61500,7 +62271,7 @@ async function exportClass(ctx) {
61500
62271
  classParsed,
61501
62272
  classLayout,
61502
62273
  effectivePalette,
61503
- theme === "dark",
62274
+ ctx.isDark,
61504
62275
  void 0,
61505
62276
  { width: exportWidth, height: exportHeight },
61506
62277
  void 0,
@@ -61509,7 +62280,7 @@ async function exportClass(ctx) {
61509
62280
  return finalizeSvgExport(container, theme, effectivePalette);
61510
62281
  }
61511
62282
  async function exportEr(ctx) {
61512
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62283
+ const { content, theme, palette, viewState, exportMode } = ctx;
61513
62284
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
61514
62285
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
61515
62286
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -61527,13 +62298,13 @@ async function exportEr(ctx) {
61527
62298
  erParsed,
61528
62299
  erLayout,
61529
62300
  effectivePalette,
61530
- theme === "dark",
62301
+ ctx.isDark,
61531
62302
  void 0,
61532
62303
  { width: exportWidth, height: exportHeight },
61533
62304
  resolveActiveTagGroup(
61534
62305
  erParsed.tagGroups,
61535
62306
  erParsed.options["active-tag"],
61536
- viewState?.tag ?? options?.tagGroup
62307
+ ctxTagOverride(ctx)
61537
62308
  ),
61538
62309
  viewState?.sem,
61539
62310
  exportMode
@@ -61541,7 +62312,7 @@ async function exportEr(ctx) {
61541
62312
  return finalizeSvgExport(container, theme, effectivePalette);
61542
62313
  }
61543
62314
  async function exportBoxesAndLines(ctx) {
61544
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62315
+ const { content, theme, palette, viewState, exportMode } = ctx;
61545
62316
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
61546
62317
  const effectivePalette = await resolveExportPalette(theme, palette);
61547
62318
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -61561,13 +62332,13 @@ async function exportBoxesAndLines(ctx) {
61561
62332
  const exportWidth = blLayout.width + PADDING3 * 2;
61562
62333
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
61563
62334
  const container = createExportContainer(exportWidth, exportHeight);
61564
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62335
+ const blActiveTagGroup = ctxTagOverride(ctx);
61565
62336
  renderBoxesAndLinesForExport2(
61566
62337
  container,
61567
62338
  blParsed,
61568
62339
  blLayout,
61569
62340
  effectivePalette,
61570
- theme === "dark",
62341
+ ctx.isDark,
61571
62342
  {
61572
62343
  exportDims: { width: exportWidth, height: exportHeight },
61573
62344
  ...blActiveTagGroup !== void 0 && {
@@ -61582,12 +62353,12 @@ async function exportBoxesAndLines(ctx) {
61582
62353
  return finalizeSvgExport(container, theme, effectivePalette);
61583
62354
  }
61584
62355
  async function exportMindmap(ctx) {
61585
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62356
+ const { content, theme, palette, viewState, exportMode } = ctx;
61586
62357
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
61587
62358
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
61588
62359
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
61589
62360
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
61590
- const isDark = theme === "dark";
62361
+ const isDark = ctx.isDark;
61591
62362
  const effectivePalette = await resolveExportPalette(theme, palette);
61592
62363
  const mmParsed = parseMindmap2(content, effectivePalette);
61593
62364
  if (mmParsed.error) return "";
@@ -61595,7 +62366,7 @@ async function exportMindmap(ctx) {
61595
62366
  const activeTagGroup = resolveActiveTagGroup(
61596
62367
  mmParsed.tagGroups,
61597
62368
  mmParsed.options["active-tag"],
61598
- viewState?.tag ?? options?.tagGroup
62369
+ ctxTagOverride(ctx)
61599
62370
  );
61600
62371
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
61601
62372
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61645,7 +62416,7 @@ async function exportWireframe(ctx) {
61645
62416
  wireframeParsed,
61646
62417
  wireframeLayout,
61647
62418
  effectivePalette,
61648
- theme === "dark",
62419
+ ctx.isDark,
61649
62420
  void 0,
61650
62421
  { width: exportWidth, height: exportHeight },
61651
62422
  theme
@@ -61653,7 +62424,7 @@ async function exportWireframe(ctx) {
61653
62424
  return finalizeSvgExport(container, theme, effectivePalette);
61654
62425
  }
61655
62426
  async function exportC4(ctx) {
61656
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62427
+ const { content, theme, palette, viewState, exportMode } = ctx;
61657
62428
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61658
62429
  const {
61659
62430
  layoutC4Context: layoutC4Context2,
@@ -61665,9 +62436,9 @@ async function exportC4(ctx) {
61665
62436
  const effectivePalette = await resolveExportPalette(theme, palette);
61666
62437
  const c4Parsed = parseC42(content, effectivePalette);
61667
62438
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61668
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61669
- const c4System = options?.c4System ?? viewState?.c4s;
61670
- const c4Container = options?.c4Container ?? viewState?.c4c;
62439
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62440
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62441
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61671
62442
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61672
62443
  if (c4Layout.nodes.length === 0) return "";
61673
62444
  const PADDING3 = 20;
@@ -61681,13 +62452,13 @@ async function exportC4(ctx) {
61681
62452
  c4Parsed,
61682
62453
  c4Layout,
61683
62454
  effectivePalette,
61684
- theme === "dark",
62455
+ ctx.isDark,
61685
62456
  void 0,
61686
62457
  { width: exportWidth, height: exportHeight },
61687
62458
  resolveActiveTagGroup(
61688
62459
  c4Parsed.tagGroups,
61689
62460
  c4Parsed.options["active-tag"],
61690
- viewState?.tag ?? options?.tagGroup
62461
+ ctxTagOverride(ctx)
61691
62462
  ),
61692
62463
  exportMode
61693
62464
  );
@@ -61708,14 +62479,14 @@ async function exportFlowchart(ctx) {
61708
62479
  fcParsed,
61709
62480
  layout,
61710
62481
  effectivePalette,
61711
- theme === "dark",
62482
+ ctx.isDark,
61712
62483
  void 0,
61713
62484
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61714
62485
  );
61715
62486
  return finalizeSvgExport(container, theme, effectivePalette);
61716
62487
  }
61717
62488
  async function exportInfra(ctx) {
61718
- const { content, theme, palette, viewState, options } = ctx;
62489
+ const { content, theme, palette, viewState } = ctx;
61719
62490
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61720
62491
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61721
62492
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61728,7 +62499,7 @@ async function exportInfra(ctx) {
61728
62499
  const activeTagGroup = resolveActiveTagGroup(
61729
62500
  infraParsed.tagGroups,
61730
62501
  infraParsed.options["active-tag"],
61731
- viewState?.tag ?? options?.tagGroup
62502
+ ctxTagOverride(ctx)
61732
62503
  );
61733
62504
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61734
62505
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61746,7 +62517,7 @@ async function exportInfra(ctx) {
61746
62517
  container,
61747
62518
  infraLayout,
61748
62519
  effectivePalette,
61749
- theme === "dark",
62520
+ ctx.isDark,
61750
62521
  showInfraTitle ? infraParsed.title : null,
61751
62522
  showInfraTitle ? infraParsed.titleLineNumber : null,
61752
62523
  infraTagGroups,
@@ -61793,7 +62564,7 @@ async function exportPert(ctx) {
61793
62564
  pertResolved,
61794
62565
  pertLayout,
61795
62566
  effectivePalette,
61796
- theme === "dark",
62567
+ ctx.isDark,
61797
62568
  {
61798
62569
  title: pertParsed.title,
61799
62570
  exportDims: { width: exportW, height: exportH },
@@ -61806,7 +62577,7 @@ async function exportPert(ctx) {
61806
62577
  return finalizeSvgExport(container, theme, effectivePalette);
61807
62578
  }
61808
62579
  async function exportGantt(ctx) {
61809
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62580
+ const { content, theme, palette, viewState, exportMode } = ctx;
61810
62581
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61811
62582
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61812
62583
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61824,7 +62595,7 @@ async function exportGantt(ctx) {
61824
62595
  container,
61825
62596
  resolved,
61826
62597
  effectivePalette,
61827
- theme === "dark",
62598
+ ctx.isDark,
61828
62599
  {
61829
62600
  ...ganttCollapsedGroups !== void 0 && {
61830
62601
  collapsedGroups: ganttCollapsedGroups
@@ -61838,7 +62609,7 @@ async function exportGantt(ctx) {
61838
62609
  currentActiveGroup: resolveActiveTagGroup(
61839
62610
  resolved.tagGroups,
61840
62611
  resolved.options.activeTag ?? void 0,
61841
- viewState?.tag ?? options?.tagGroup
62612
+ ctxTagOverride(ctx)
61842
62613
  ),
61843
62614
  exportMode
61844
62615
  },
@@ -61861,7 +62632,7 @@ async function exportState(ctx) {
61861
62632
  stateParsed,
61862
62633
  layout,
61863
62634
  effectivePalette,
61864
- theme === "dark",
62635
+ ctx.isDark,
61865
62636
  void 0,
61866
62637
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61867
62638
  );
@@ -61881,7 +62652,7 @@ async function exportTechRadar(ctx) {
61881
62652
  container,
61882
62653
  radarParsed,
61883
62654
  effectivePalette,
61884
- theme === "dark",
62655
+ ctx.isDark,
61885
62656
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61886
62657
  viewState,
61887
62658
  exportMode
@@ -61898,13 +62669,13 @@ async function exportJourneyMap(ctx) {
61898
62669
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61899
62670
  return "";
61900
62671
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61901
- isDark: theme === "dark"
62672
+ isDark: ctx.isDark
61902
62673
  });
61903
62674
  const container = createExportContainer(
61904
62675
  jmLayout.totalWidth,
61905
62676
  jmLayout.totalHeight
61906
62677
  );
61907
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62678
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61908
62679
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61909
62680
  exportMode
61910
62681
  });
@@ -61922,7 +62693,7 @@ async function exportCycle(ctx) {
61922
62693
  container,
61923
62694
  cycleParsed,
61924
62695
  effectivePalette,
61925
- theme === "dark",
62696
+ ctx.isDark,
61926
62697
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61927
62698
  viewState,
61928
62699
  exportMode
@@ -61959,7 +62730,7 @@ async function exportMap(ctx) {
61959
62730
  mapResolved,
61960
62731
  mapData,
61961
62732
  effectivePalette,
61962
- theme === "dark",
62733
+ ctx.isDark,
61963
62734
  dims
61964
62735
  );
61965
62736
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61976,7 +62747,7 @@ async function exportPyramid(ctx) {
61976
62747
  container,
61977
62748
  pyramidParsed,
61978
62749
  effectivePalette,
61979
- theme === "dark",
62750
+ ctx.isDark,
61980
62751
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61981
62752
  );
61982
62753
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61993,7 +62764,7 @@ async function exportRing(ctx) {
61993
62764
  container,
61994
62765
  ringParsed,
61995
62766
  effectivePalette,
61996
- theme === "dark",
62767
+ ctx.isDark,
61997
62768
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61998
62769
  );
61999
62770
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -62010,7 +62781,7 @@ async function exportRaci(ctx) {
62010
62781
  container,
62011
62782
  raciParsed,
62012
62783
  effectivePalette,
62013
- theme === "dark",
62784
+ ctx.isDark,
62014
62785
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
62015
62786
  );
62016
62787
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -62033,7 +62804,7 @@ async function exportSlope(ctx) {
62033
62804
  container,
62034
62805
  parsed,
62035
62806
  effectivePalette,
62036
- theme === "dark",
62807
+ ctx.isDark,
62037
62808
  void 0,
62038
62809
  dims
62039
62810
  );
@@ -62049,14 +62820,14 @@ async function exportArc(ctx) {
62049
62820
  container,
62050
62821
  parsed,
62051
62822
  effectivePalette,
62052
- theme === "dark",
62823
+ ctx.isDark,
62053
62824
  void 0,
62054
62825
  dims
62055
62826
  );
62056
62827
  return finalizeSvgExport(container, theme, effectivePalette);
62057
62828
  }
62058
62829
  async function exportTimeline(ctx) {
62059
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62830
+ const { content, theme, palette, viewState, exportMode } = ctx;
62060
62831
  const parsed = parseTimeline(content, palette);
62061
62832
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
62062
62833
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -62065,13 +62836,13 @@ async function exportTimeline(ctx) {
62065
62836
  container,
62066
62837
  parsed,
62067
62838
  effectivePalette,
62068
- theme === "dark",
62839
+ ctx.isDark,
62069
62840
  void 0,
62070
62841
  dims,
62071
62842
  resolveActiveTagGroup(
62072
62843
  parsed.timelineTagGroups,
62073
62844
  parsed.timelineActiveTag,
62074
- viewState?.tag ?? options?.tagGroup
62845
+ ctxTagOverride(ctx)
62075
62846
  ),
62076
62847
  viewState?.swim,
62077
62848
  void 0,
@@ -62090,7 +62861,7 @@ async function exportWordcloud(ctx) {
62090
62861
  container,
62091
62862
  parsed,
62092
62863
  effectivePalette,
62093
- theme === "dark",
62864
+ ctx.isDark,
62094
62865
  dims
62095
62866
  );
62096
62867
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -62105,7 +62876,7 @@ async function exportVenn(ctx) {
62105
62876
  container,
62106
62877
  parsed,
62107
62878
  effectivePalette,
62108
- theme === "dark",
62879
+ ctx.isDark,
62109
62880
  void 0,
62110
62881
  dims
62111
62882
  );
@@ -62121,14 +62892,14 @@ async function exportQuadrant(ctx) {
62121
62892
  container,
62122
62893
  parsed,
62123
62894
  effectivePalette,
62124
- theme === "dark",
62895
+ ctx.isDark,
62125
62896
  void 0,
62126
62897
  dims
62127
62898
  );
62128
62899
  return finalizeSvgExport(container, theme, effectivePalette);
62129
62900
  }
62130
62901
  async function exportVisualization(ctx) {
62131
- const { content, theme, palette, viewState, options } = ctx;
62902
+ const { content, theme, palette, viewState } = ctx;
62132
62903
  const parsed = parseVisualization(content, palette);
62133
62904
  if (parsed.type !== "sequence") {
62134
62905
  if (parsed.error) {
@@ -62139,7 +62910,7 @@ async function exportVisualization(ctx) {
62139
62910
  }
62140
62911
  }
62141
62912
  const effectivePalette = await resolveExportPalette(theme, palette);
62142
- const isDark = theme === "dark";
62913
+ const isDark = ctx.isDark;
62143
62914
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
62144
62915
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
62145
62916
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -62147,7 +62918,7 @@ async function exportVisualization(ctx) {
62147
62918
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
62148
62919
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
62149
62920
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
62150
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62921
+ const seqActiveTagGroup = ctxTagOverride(ctx);
62151
62922
  renderSequenceDiagram2(
62152
62923
  container,
62153
62924
  seqParsed,
@@ -62412,6 +63183,162 @@ function collapseBoxesAndLines(parsed, collapsedGroups) {
62412
63183
  };
62413
63184
  }
62414
63185
 
63186
+ // src/boxes-and-lines/focus.ts
63187
+ var GROUP_PREFIX = "__group_";
63188
+ var groupKey = (label) => `${GROUP_PREFIX}${label}`;
63189
+ var isGroupKey = (k) => k.startsWith(GROUP_PREFIX);
63190
+ var groupLabelOf = (k) => k.slice(GROUP_PREFIX.length);
63191
+ function focusBoxesAndLines(parsed, target) {
63192
+ const allValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
63193
+ const rampDomain = allValues.length > 0 ? { min: Math.min(...allValues), max: Math.max(...allValues) } : null;
63194
+ const nodeLabelSet = new Set(parsed.nodes.map((n) => n.label));
63195
+ const groupByLabel = /* @__PURE__ */ new Map();
63196
+ for (const g of parsed.groups) groupByLabel.set(g.label, g);
63197
+ const parentOf = /* @__PURE__ */ new Map();
63198
+ for (const g of parsed.groups)
63199
+ for (const child of g.children) parentOf.set(child, g.label);
63200
+ const topAncestor = (label) => {
63201
+ let p = parentOf.get(label);
63202
+ if (p === void 0) return void 0;
63203
+ for (; ; ) {
63204
+ const up = parentOf.get(p);
63205
+ if (up === void 0) return p;
63206
+ p = up;
63207
+ }
63208
+ };
63209
+ const descendantsOf = (groupLabel) => {
63210
+ const nodes2 = /* @__PURE__ */ new Set();
63211
+ const groups2 = /* @__PURE__ */ new Set();
63212
+ const seen = /* @__PURE__ */ new Set([groupLabel]);
63213
+ const stack = [groupLabel];
63214
+ while (stack.length) {
63215
+ const cur = stack.pop();
63216
+ const g = groupByLabel.get(cur);
63217
+ if (!g) continue;
63218
+ for (const child of g.children) {
63219
+ if (groupByLabel.has(child)) {
63220
+ if (!seen.has(child)) {
63221
+ seen.add(child);
63222
+ groups2.add(child);
63223
+ stack.push(child);
63224
+ }
63225
+ } else {
63226
+ nodes2.add(child);
63227
+ }
63228
+ }
63229
+ }
63230
+ return { nodes: nodes2, groups: groups2 };
63231
+ };
63232
+ const focusNodeLabels = /* @__PURE__ */ new Set();
63233
+ const focusGroupLabels = /* @__PURE__ */ new Set();
63234
+ const focusEndpointSet = /* @__PURE__ */ new Set();
63235
+ if (target.kind === "group") {
63236
+ const gl = isGroupKey(target.id) ? groupLabelOf(target.id) : target.id;
63237
+ if (groupByLabel.has(gl)) {
63238
+ focusGroupLabels.add(gl);
63239
+ focusEndpointSet.add(groupKey(gl));
63240
+ const desc = descendantsOf(gl);
63241
+ for (const n of desc.nodes) {
63242
+ focusNodeLabels.add(n);
63243
+ focusEndpointSet.add(n);
63244
+ }
63245
+ for (const sg of desc.groups) {
63246
+ focusGroupLabels.add(sg);
63247
+ focusEndpointSet.add(groupKey(sg));
63248
+ }
63249
+ }
63250
+ } else {
63251
+ if (nodeLabelSet.has(target.id)) {
63252
+ focusNodeLabels.add(target.id);
63253
+ focusEndpointSet.add(target.id);
63254
+ }
63255
+ }
63256
+ const neighborIds = /* @__PURE__ */ new Set();
63257
+ const collapsedNeighborGroupIds = /* @__PURE__ */ new Set();
63258
+ const neighborBoxes = /* @__PURE__ */ new Set();
63259
+ const keepGroupLabels = new Set(focusGroupLabels);
63260
+ const keptEdges = [];
63261
+ const selfLoops = [];
63262
+ const keepGroupAndSubgroups = (gl) => {
63263
+ keepGroupLabels.add(gl);
63264
+ for (const sg of descendantsOf(gl).groups) keepGroupLabels.add(sg);
63265
+ };
63266
+ const classifyNeighbor = (key) => {
63267
+ if (isGroupKey(key)) {
63268
+ const gl = groupLabelOf(key);
63269
+ if (!groupByLabel.has(gl)) return false;
63270
+ if (focusGroupLabels.has(gl)) return true;
63271
+ neighborIds.add(key);
63272
+ collapsedNeighborGroupIds.add(gl);
63273
+ keepGroupAndSubgroups(gl);
63274
+ return true;
63275
+ }
63276
+ if (focusNodeLabels.has(key)) return true;
63277
+ if (!nodeLabelSet.has(key)) return false;
63278
+ const top = topAncestor(key);
63279
+ if (top !== void 0 && !focusGroupLabels.has(top)) {
63280
+ neighborIds.add(groupKey(top));
63281
+ collapsedNeighborGroupIds.add(top);
63282
+ keepGroupAndSubgroups(top);
63283
+ return true;
63284
+ }
63285
+ neighborIds.add(key);
63286
+ neighborBoxes.add(key);
63287
+ return true;
63288
+ };
63289
+ for (const edge of parsed.edges) {
63290
+ const inS = focusEndpointSet.has(edge.source);
63291
+ const inT = focusEndpointSet.has(edge.target);
63292
+ if (edge.source === edge.target) {
63293
+ if (inS) selfLoops.push(edge);
63294
+ continue;
63295
+ }
63296
+ if (inS && inT) {
63297
+ keptEdges.push(edge);
63298
+ continue;
63299
+ }
63300
+ if (!inS && !inT) continue;
63301
+ const other = inS ? edge.target : edge.source;
63302
+ if (classifyNeighbor(other)) keptEdges.push(edge);
63303
+ }
63304
+ const keepNodeLabels = /* @__PURE__ */ new Set([
63305
+ ...focusNodeLabels,
63306
+ ...neighborBoxes
63307
+ ]);
63308
+ const nodes = parsed.nodes.filter((n) => keepNodeLabels.has(n.label));
63309
+ const groups = parsed.groups.filter((g) => keepGroupLabels.has(g.label));
63310
+ const notes = parsed.notes?.filter((note) => keepNodeLabels.has(note.ref));
63311
+ const filtered = {
63312
+ ...parsed,
63313
+ nodes,
63314
+ edges: keptEdges,
63315
+ groups,
63316
+ ...notes !== void 0 && { notes }
63317
+ };
63318
+ delete filtered.nodePositions;
63319
+ const collapsed = collapseBoxesAndLines(filtered, collapsedNeighborGroupIds);
63320
+ let resultParsed = collapsed.parsed;
63321
+ if (selfLoops.length > 0) {
63322
+ const visible = new Set(resultParsed.nodes.map((n) => n.label));
63323
+ const survivors = selfLoops.filter((e) => visible.has(e.source));
63324
+ if (survivors.length > 0)
63325
+ resultParsed = {
63326
+ ...resultParsed,
63327
+ edges: [...resultParsed.edges, ...survivors]
63328
+ };
63329
+ }
63330
+ return {
63331
+ parsed: resultParsed,
63332
+ neighborIds,
63333
+ collapsedNeighborGroupIds,
63334
+ rampDomain,
63335
+ collapseInfo: {
63336
+ collapsedChildCounts: collapsed.collapsedChildCounts,
63337
+ originalGroups: collapsed.originalGroups
63338
+ }
63339
+ };
63340
+ }
63341
+
62415
63342
  // src/advanced.ts
62416
63343
  init_parser11();
62417
63344
  init_layout2();
@@ -63827,6 +64754,8 @@ export {
63827
64754
  CHART_TYPE_DESCRIPTIONS,
63828
64755
  ECHART_EXPORT_WIDTH,
63829
64756
  INFRA_BEHAVIOR_KEYS,
64757
+ INVALID_COLOR_CODE,
64758
+ INVALID_CSS_COLOR_HEX,
63830
64759
  LEGEND_GEAR_PILL_W,
63831
64760
  LEGEND_HEIGHT,
63832
64761
  RACI_ERROR_CODES,
@@ -63888,6 +64817,7 @@ export {
63888
64817
  extractSymbols4 as extractInfraSymbols,
63889
64818
  extractPertSymbols,
63890
64819
  findUnsafePipePositions,
64820
+ focusBoxesAndLines,
63891
64821
  focusOrgTree,
63892
64822
  formatDateLabel,
63893
64823
  formatDgmoError,
@@ -63908,8 +64838,10 @@ export {
63908
64838
  hslToHex,
63909
64839
  inferParticipantType,
63910
64840
  inferRoles,
64841
+ invalidColorDiagnostic,
63911
64842
  isArchiveColumn,
63912
64843
  isExtendedChartType,
64844
+ isInvalidColorToken,
63913
64845
  isLegacyMetadataLine,
63914
64846
  isRecognizedColorName,
63915
64847
  isSequenceBlock,
@@ -63950,6 +64882,7 @@ export {
63950
64882
  migrateContent,
63951
64883
  mix,
63952
64884
  mulberry32,
64885
+ nearestNamedColor,
63953
64886
  nord,
63954
64887
  nordPalette,
63955
64888
  normalizeName,