@diagrammo/dgmo 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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/auto.mjs 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;
@@ -304,21 +312,99 @@ function resolveColor(color, palette) {
304
312
  }
305
313
  return colorNames[lower] ?? null;
306
314
  }
315
+ function nearestNamedColor(input) {
316
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
317
+ if (cssHex) input = cssHex;
318
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
319
+ if (!m) return null;
320
+ let h = m[1].toLowerCase();
321
+ if (h.length === 3)
322
+ h = h.split("").map((c) => c + c).join("");
323
+ const r = parseInt(h.slice(0, 2), 16) / 255;
324
+ const g = parseInt(h.slice(2, 4), 16) / 255;
325
+ const b = parseInt(h.slice(4, 6), 16) / 255;
326
+ const max = Math.max(r, g, b);
327
+ const min = Math.min(r, g, b);
328
+ const delta = max - min;
329
+ const l = (max + min) / 2;
330
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
331
+ if (s < 0.15) {
332
+ if (l < 0.2) return "black";
333
+ if (l > 0.85) return "white";
334
+ return "gray";
335
+ }
336
+ let hue;
337
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
338
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
339
+ else hue = 60 * ((r - g) / delta + 4);
340
+ if (hue < 0) hue += 360;
341
+ const anchors = [
342
+ ["red", 0],
343
+ ["orange", 30],
344
+ ["yellow", 55],
345
+ ["green", 120],
346
+ ["teal", 170],
347
+ ["cyan", 190],
348
+ ["blue", 225],
349
+ ["purple", 285],
350
+ ["red", 360]
351
+ ];
352
+ let best = "red";
353
+ let bestD = Infinity;
354
+ for (const [name, deg] of anchors) {
355
+ const d = Math.abs(hue - deg);
356
+ if (d < bestD) {
357
+ bestD = d;
358
+ best = name;
359
+ }
360
+ }
361
+ return best;
362
+ }
363
+ function isInvalidColorToken(token) {
364
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
365
+ const lower = token.toLowerCase();
366
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
367
+ }
368
+ function invalidColorDiagnostic(token, line11) {
369
+ if (!isInvalidColorToken(token)) return null;
370
+ const nearest = nearestNamedColor(token);
371
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
372
+ return makeDgmoError(
373
+ line11,
374
+ `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}`,
375
+ "warning",
376
+ INVALID_COLOR_CODE
377
+ );
378
+ }
307
379
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
308
380
  const resolved = resolveColor(color, palette);
309
381
  if (resolved !== null) return resolved;
382
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
383
+ const nearest = nearestNamedColor(color);
384
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
385
+ diagnostics.push(
386
+ makeDgmoError(
387
+ line11,
388
+ `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}`,
389
+ "error",
390
+ INVALID_COLOR_CODE
391
+ )
392
+ );
393
+ return void 0;
394
+ }
310
395
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
311
396
  const suggestion = hint ? ` ${hint}` : "";
312
397
  diagnostics.push(
313
398
  makeDgmoError(
314
399
  line11,
315
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
316
- "warning"
400
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
401
+ "warning",
402
+ INVALID_COLOR_CODE
317
403
  )
318
404
  );
319
405
  return void 0;
320
406
  }
321
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, seriesColors;
407
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
322
408
  var init_colors = __esm({
323
409
  "src/colors.ts"() {
324
410
  "use strict";
@@ -386,6 +472,92 @@ var init_colors = __esm({
386
472
  "orange",
387
473
  "cyan"
388
474
  ]);
475
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
476
+ INVALID_CSS_COLOR_HEX = Object.freeze({
477
+ pink: "#ffc0cb",
478
+ hotpink: "#ff69b4",
479
+ deeppink: "#ff1493",
480
+ lightpink: "#ffb6c1",
481
+ palevioletred: "#db7093",
482
+ crimson: "#dc143c",
483
+ scarlet: "#ff2400",
484
+ firebrick: "#b22222",
485
+ darkred: "#8b0000",
486
+ maroon: "#800000",
487
+ salmon: "#fa8072",
488
+ lightsalmon: "#ffa07a",
489
+ darksalmon: "#e9967a",
490
+ coral: "#ff7f50",
491
+ lightcoral: "#f08080",
492
+ tomato: "#ff6347",
493
+ orangered: "#ff4500",
494
+ darkorange: "#ff8c00",
495
+ gold: "#ffd700",
496
+ goldenrod: "#daa520",
497
+ darkgoldenrod: "#b8860b",
498
+ khaki: "#f0e68c",
499
+ darkkhaki: "#bdb76b",
500
+ amber: "#ffbf00",
501
+ lavender: "#e6e6fa",
502
+ violet: "#ee82ee",
503
+ magenta: "#ff00ff",
504
+ fuchsia: "#ff00ff",
505
+ orchid: "#da70d6",
506
+ plum: "#dda0dd",
507
+ indigo: "#4b0082",
508
+ navy: "#000080",
509
+ midnightblue: "#191970",
510
+ darkblue: "#00008b",
511
+ mediumblue: "#0000cd",
512
+ royalblue: "#4169e1",
513
+ cornflowerblue: "#6495ed",
514
+ dodgerblue: "#1e90ff",
515
+ deepskyblue: "#00bfff",
516
+ skyblue: "#87ceeb",
517
+ lightskyblue: "#87cefa",
518
+ lightblue: "#add8e6",
519
+ powderblue: "#b0e0e6",
520
+ steelblue: "#4682b4",
521
+ slateblue: "#6a5acd",
522
+ cadetblue: "#5f9ea0",
523
+ turquoise: "#40e0d0",
524
+ aqua: "#00ffff",
525
+ aquamarine: "#7fffd4",
526
+ lime: "#00ff00",
527
+ limegreen: "#32cd32",
528
+ lightgreen: "#90ee90",
529
+ palegreen: "#98fb98",
530
+ seagreen: "#2e8b57",
531
+ mediumseagreen: "#3cb371",
532
+ forestgreen: "#228b22",
533
+ darkgreen: "#006400",
534
+ olive: "#808000",
535
+ olivedrab: "#6b8e23",
536
+ darkolivegreen: "#556b2f",
537
+ chartreuse: "#7fff00",
538
+ lawngreen: "#7cfc00",
539
+ springgreen: "#00ff7f",
540
+ greenyellow: "#adff2f",
541
+ brown: "#a52a2a",
542
+ sienna: "#a0522d",
543
+ chocolate: "#d2691e",
544
+ peru: "#cd853f",
545
+ tan: "#d2b48c",
546
+ beige: "#f5f5dc",
547
+ wheat: "#f5deb3",
548
+ ivory: "#fffff0",
549
+ silver: "#c0c0c0",
550
+ lightgray: "#d3d3d3",
551
+ lightgrey: "#d3d3d3",
552
+ darkgray: "#a9a9a9",
553
+ darkgrey: "#a9a9a9",
554
+ dimgray: "#696969",
555
+ dimgrey: "#696969",
556
+ slategray: "#708090",
557
+ slategrey: "#708090",
558
+ gainsboro: "#dcdcdc",
559
+ grey: "#808080"
560
+ });
389
561
  seriesColors = [
390
562
  nord.nord10,
391
563
  // blue
@@ -560,7 +732,13 @@ function extractColor(label, palette, diagnostics, line11) {
560
732
  );
561
733
  if (lastSpaceIdx < 0) return { label };
562
734
  const trailing = label.substring(lastSpaceIdx + 1);
563
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
735
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
736
+ if (diagnostics && line11 !== void 0) {
737
+ const diag = invalidColorDiagnostic(trailing, line11);
738
+ if (diag) diagnostics.push(diag);
739
+ }
740
+ return { label };
741
+ }
564
742
  let color;
565
743
  if (diagnostics && line11 !== void 0) {
566
744
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1184,6 +1362,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1184
1362
  }
1185
1363
  }
1186
1364
  }
1365
+ function cascadeTagMetadata(roots, tagGroups) {
1366
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1367
+ if (keys.length === 0) return;
1368
+ const walk = (node, inherited) => {
1369
+ const childInherited = { ...inherited };
1370
+ for (const key of keys) {
1371
+ const own = node.metadata[key];
1372
+ if (own) {
1373
+ childInherited[key] = own;
1374
+ } else if (inherited[key]) {
1375
+ node.metadata[key] = inherited[key];
1376
+ }
1377
+ }
1378
+ for (const child of node.children) walk(child, childInherited);
1379
+ };
1380
+ for (const root of roots) walk(root, {});
1381
+ }
1187
1382
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1188
1383
  const defaults = [];
1189
1384
  for (const group of tagGroups) {
@@ -1722,7 +1917,12 @@ function parseVisualizationFull(content, palette) {
1722
1917
  }
1723
1918
  if (currentTimelineTagGroup && indent > 0) {
1724
1919
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1725
- const { label, color } = extractColor(entryText, palette);
1920
+ const { label, color } = extractColor(
1921
+ entryText,
1922
+ palette,
1923
+ result.diagnostics,
1924
+ lineNumber
1925
+ );
1726
1926
  if (color) {
1727
1927
  if (isDefault) {
1728
1928
  currentTimelineTagGroup.defaultValue = label;
@@ -3023,9 +3223,12 @@ function contrastText(bg, lightText, darkText) {
3023
3223
  }
3024
3224
  return lightText;
3025
3225
  }
3226
+ function themeBaseBg(palette, isDark) {
3227
+ return isDark ? palette.surface : palette.bg;
3228
+ }
3026
3229
  function shapeFill(palette, intent, isDark, opts) {
3027
3230
  if (opts?.solid) return intent;
3028
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3231
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3029
3232
  }
3030
3233
  function getSeriesColors(palette) {
3031
3234
  const c = palette.colors;
@@ -3056,7 +3259,7 @@ function getSegmentColors(palette, count) {
3056
3259
  }
3057
3260
  function politicalTints(palette, count, isDark) {
3058
3261
  if (count <= 0) return [];
3059
- const base = isDark ? palette.surface : palette.bg;
3262
+ const base = themeBaseBg(palette, isDark);
3060
3263
  const c = palette.colors;
3061
3264
  const swatches = [
3062
3265
  .../* @__PURE__ */ new Set([
@@ -3866,6 +4069,7 @@ __export(palettes_exports, {
3866
4069
  shade: () => shade,
3867
4070
  shapeFill: () => shapeFill,
3868
4071
  slatePalette: () => slatePalette,
4072
+ themeBaseBg: () => themeBaseBg,
3869
4073
  tidewaterPalette: () => tidewaterPalette,
3870
4074
  tint: () => tint,
3871
4075
  tokyoNightPalette: () => tokyoNightPalette
@@ -4099,6 +4303,30 @@ var init_scaling = __esm({
4099
4303
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4100
4304
  return new _ScaleContext(clamped, minScaleFactor);
4101
4305
  }
4306
+ /**
4307
+ * Fit content into a bounding box, scaling by whichever dimension is more
4308
+ * constraining (the smaller of the width- and height-fit ratios) so the
4309
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4310
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4311
+ * never shrunk past the readability floor).
4312
+ */
4313
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4314
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4315
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4316
+ const raw = Math.min(wRaw, hRaw);
4317
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4318
+ return new _ScaleContext(clamped, minScaleFactor);
4319
+ }
4320
+ /**
4321
+ * Build a context from an explicit raw factor (clamped to
4322
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4323
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4324
+ * can still overflow — re-measure the laid-out result and tighten.
4325
+ */
4326
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4327
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4328
+ return new _ScaleContext(clamped, minScaleFactor);
4329
+ }
4102
4330
  static identity() {
4103
4331
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4104
4332
  }
@@ -5799,12 +6027,7 @@ function parseSequenceDgmo(content, palette) {
5799
6027
  const trimmed = token.trim();
5800
6028
  return nameAliasMap.get(trimmed) ?? trimmed;
5801
6029
  };
5802
- const fail = (line11, message) => {
5803
- const diag = makeDgmoError(line11, message);
5804
- result.diagnostics.push(diag);
5805
- result.error = formatDgmoError(diag);
5806
- return result;
5807
- };
6030
+ const fail = makeFail(result);
5808
6031
  const pushError = (line11, message) => {
5809
6032
  const diag = makeDgmoError(line11, message);
5810
6033
  result.diagnostics.push(diag);
@@ -5819,6 +6042,7 @@ function parseSequenceDgmo(content, palette) {
5819
6042
  const lines = content.split("\n");
5820
6043
  let hasExplicitChart = false;
5821
6044
  let contentStarted = false;
6045
+ let bodyStarted = false;
5822
6046
  let firstLineIndex = -1;
5823
6047
  for (let fi = 0; fi < lines.length; fi++) {
5824
6048
  const fl = lines[fi].trim();
@@ -6130,6 +6354,7 @@ function parseSequenceDgmo(content, palette) {
6130
6354
  );
6131
6355
  }
6132
6356
  contentStarted = true;
6357
+ bodyStarted = true;
6133
6358
  const section = {
6134
6359
  kind: "section",
6135
6360
  // Capture group 1 guaranteed present after successful match.
@@ -6345,7 +6570,7 @@ function parseSequenceDgmo(content, palette) {
6345
6570
  alias: bareAlias
6346
6571
  } = splitPipe(trimmed, lineNumber);
6347
6572
  const inGroup = activeGroup && measureIndent(raw) > 0;
6348
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6573
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6349
6574
  contentStarted = true;
6350
6575
  const id = bareCore;
6351
6576
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6392,6 +6617,7 @@ function parseSequenceDgmo(content, palette) {
6392
6617
  }
6393
6618
  if (labeledArrow) {
6394
6619
  contentStarted = true;
6620
+ bodyStarted = true;
6395
6621
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6396
6622
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6397
6623
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6456,6 +6682,7 @@ function parseSequenceDgmo(content, palette) {
6456
6682
  const bareCall = bareCallSync || bareCallAsync;
6457
6683
  if (bareCall) {
6458
6684
  contentStarted = true;
6685
+ bodyStarted = true;
6459
6686
  const from = bareCall[1];
6460
6687
  const to = bareCall[2];
6461
6688
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6477,6 +6704,7 @@ function parseSequenceDgmo(content, palette) {
6477
6704
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6478
6705
  if (ifMatch) {
6479
6706
  contentStarted = true;
6707
+ bodyStarted = true;
6480
6708
  const block = {
6481
6709
  kind: "block",
6482
6710
  type: "if",
@@ -6493,6 +6721,7 @@ function parseSequenceDgmo(content, palette) {
6493
6721
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6494
6722
  if (loopMatch) {
6495
6723
  contentStarted = true;
6724
+ bodyStarted = true;
6496
6725
  const block = {
6497
6726
  kind: "block",
6498
6727
  type: "loop",
@@ -6509,6 +6738,7 @@ function parseSequenceDgmo(content, palette) {
6509
6738
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6510
6739
  if (parallelMatch) {
6511
6740
  contentStarted = true;
6741
+ bodyStarted = true;
6512
6742
  const block = {
6513
6743
  kind: "block",
6514
6744
  type: "parallel",
@@ -6605,6 +6835,7 @@ function parseSequenceDgmo(content, palette) {
6605
6835
  lineNumber,
6606
6836
  endLineNumber: lineNumber
6607
6837
  };
6838
+ bodyStarted = true;
6608
6839
  currentContainer().push(note);
6609
6840
  continue;
6610
6841
  }
@@ -6629,6 +6860,7 @@ function parseSequenceDgmo(content, palette) {
6629
6860
  endLineNumber: i + 1
6630
6861
  // i has advanced past the body lines (1-based)
6631
6862
  };
6863
+ bodyStarted = true;
6632
6864
  currentContainer().push(note);
6633
6865
  continue;
6634
6866
  }
@@ -7473,6 +7705,15 @@ function parseNodeRef(text) {
7473
7705
  }
7474
7706
  return null;
7475
7707
  }
7708
+ function parseNodeRefLoose(text) {
7709
+ const t = text.trim();
7710
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7711
+ const m = t.match(shapeRe);
7712
+ if (!m) return null;
7713
+ const ref = parseNodeRef(m[1]);
7714
+ if (!ref) return null;
7715
+ return { ref, trailing: m[2].trim() };
7716
+ }
7476
7717
  function splitArrows(line11) {
7477
7718
  const segments = [];
7478
7719
  const arrowPositions = [];
@@ -7568,6 +7809,7 @@ function parseFlowchart(content, palette) {
7568
7809
  const notes = [];
7569
7810
  let contentStarted = false;
7570
7811
  let firstLineParsed = false;
7812
+ let prevLineLastNodeId = null;
7571
7813
  const nameAliasMap = /* @__PURE__ */ new Map();
7572
7814
  function peelAlias2(seg) {
7573
7815
  const trimmed = seg.trim();
@@ -7575,6 +7817,19 @@ function parseFlowchart(content, palette) {
7575
7817
  if (!m) return { seg: trimmed };
7576
7818
  return { seg: m[1].trim(), alias: m[2] };
7577
7819
  }
7820
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7821
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7822
+ if (suffixWarnedLines.has(lineNumber)) return;
7823
+ suffixWarnedLines.add(lineNumber);
7824
+ result.diagnostics.push(
7825
+ makeDgmoError(
7826
+ lineNumber,
7827
+ `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.`,
7828
+ "warning",
7829
+ "W_FLOWCHART_NODE_SUFFIX"
7830
+ )
7831
+ );
7832
+ }
7578
7833
  function getOrCreateNode(ref, lineNumber) {
7579
7834
  const key = ref.id;
7580
7835
  const existing = nodeMap.get(key);
@@ -7633,6 +7888,8 @@ function parseFlowchart(content, palette) {
7633
7888
  indentStack[indentStack.length - 1].nodeId
7634
7889
  ) : null;
7635
7890
  const segments = splitArrows(trimmed);
7891
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7892
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7636
7893
  if (segments.length === 1) {
7637
7894
  const peeled = peelAlias2(segments[0]);
7638
7895
  const ref = parseNodeRef(peeled.seg);
@@ -7642,6 +7899,13 @@ function parseFlowchart(content, palette) {
7642
7899
  indentStack.push({ nodeId: node.id, indent });
7643
7900
  return node.id;
7644
7901
  }
7902
+ const loose = parseNodeRefLoose(peeled.seg);
7903
+ if (loose) {
7904
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7905
+ const node = getOrCreateNode(loose.ref, lineNumber);
7906
+ indentStack.push({ nodeId: node.id, indent });
7907
+ return node.id;
7908
+ }
7645
7909
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7646
7910
  if (aliasResolved !== void 0) {
7647
7911
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7677,16 +7941,23 @@ function parseFlowchart(content, palette) {
7677
7941
  }
7678
7942
  }
7679
7943
  }
7944
+ if (!ref) {
7945
+ const loose = parseNodeRefLoose(peeled.seg);
7946
+ if (loose) {
7947
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7948
+ ref = loose.ref;
7949
+ }
7950
+ }
7680
7951
  if (!ref) continue;
7681
7952
  const node = getOrCreateNode(ref, lineNumber);
7682
7953
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7683
7954
  if (pendingArrow !== null) {
7684
- const sourceId = lastNodeId ?? implicitSourceId;
7955
+ const sourceId = lastNodeId ?? effectiveSource;
7685
7956
  if (sourceId) {
7686
7957
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7687
7958
  }
7688
7959
  pendingArrow = null;
7689
- } else if (lastNodeId === null && implicitSourceId === null) {
7960
+ } else if (lastNodeId === null && effectiveSource === null) {
7690
7961
  }
7691
7962
  lastNodeId = node.id;
7692
7963
  }
@@ -7768,7 +8039,8 @@ function parseFlowchart(content, palette) {
7768
8039
  continue;
7769
8040
  }
7770
8041
  }
7771
- processContentLine(trimmed, lineNumber, indent);
8042
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8043
+ if (lastId) prevLineLastNodeId = lastId;
7772
8044
  }
7773
8045
  if (result.nodes.length === 0 && !result.error) {
7774
8046
  const diag = makeDgmoError(
@@ -8851,7 +9123,12 @@ function parseERDiagram(content, palette) {
8851
9123
  }
8852
9124
  if (currentTagGroup && !contentStarted && indent > 0) {
8853
9125
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8854
- const { label, color } = extractColor(cleanEntry, palette);
9126
+ const { label, color } = extractColor(
9127
+ cleanEntry,
9128
+ palette,
9129
+ result.diagnostics,
9130
+ lineNumber
9131
+ );
8855
9132
  if (isDefault) {
8856
9133
  currentTagGroup.defaultValue = label;
8857
9134
  } else if (currentTagGroup.entries.length === 0) {
@@ -9169,12 +9446,7 @@ function parseOrg(content, palette) {
9169
9446
  diagnostics: [],
9170
9447
  error: null
9171
9448
  };
9172
- const fail = (line11, message) => {
9173
- const diag = makeDgmoError(line11, message);
9174
- result.diagnostics.push(diag);
9175
- result.error = formatDgmoError(diag);
9176
- return result;
9177
- };
9449
+ const fail = makeFail(result);
9178
9450
  const pushError = (line11, message) => {
9179
9451
  const diag = makeDgmoError(line11, message);
9180
9452
  result.diagnostics.push(diag);
@@ -9283,7 +9555,12 @@ function parseOrg(content, palette) {
9283
9555
  const indent2 = measureIndent(line11);
9284
9556
  if (indent2 > 0) {
9285
9557
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9286
- const { label, color } = extractColor(cleanEntry, palette);
9558
+ const { label, color } = extractColor(
9559
+ cleanEntry,
9560
+ palette,
9561
+ result.diagnostics,
9562
+ lineNumber
9563
+ );
9287
9564
  if (isDefault) {
9288
9565
  currentTagGroup.defaultValue = label;
9289
9566
  } else if (currentTagGroup.entries.length === 0) {
@@ -9569,12 +9846,7 @@ function parseSitemap(content, palette) {
9569
9846
  diagnostics: [],
9570
9847
  error: null
9571
9848
  };
9572
- const fail = (line11, message) => {
9573
- const diag = makeDgmoError(line11, message);
9574
- result.diagnostics.push(diag);
9575
- result.error = formatDgmoError(diag);
9576
- return result;
9577
- };
9849
+ const fail = makeFail(result);
9578
9850
  const pushError = (line11, message) => {
9579
9851
  const diag = makeDgmoError(line11, message);
9580
9852
  result.diagnostics.push(diag);
@@ -9675,7 +9947,12 @@ function parseSitemap(content, palette) {
9675
9947
  const indent2 = measureIndent(line11);
9676
9948
  if (indent2 > 0) {
9677
9949
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9678
- const { label, color } = extractColor(cleanEntry, palette);
9950
+ const { label, color } = extractColor(
9951
+ cleanEntry,
9952
+ palette,
9953
+ result.diagnostics,
9954
+ lineNumber
9955
+ );
9679
9956
  currentTagGroup.entries.push({
9680
9957
  value: label,
9681
9958
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11390,7 +11667,7 @@ var init_chart_types = __esm({
11390
11667
  },
11391
11668
  {
11392
11669
  id: "sequence",
11393
- description: "Message / interaction flows",
11670
+ description: "Message request and response interaction flows",
11394
11671
  fallback: true
11395
11672
  },
11396
11673
  {
@@ -11464,7 +11741,7 @@ var init_chart_types = __esm({
11464
11741
  },
11465
11742
  {
11466
11743
  id: "map",
11467
- description: "Geographic map: a value or count per country, state, or region (choropleth); points of interest; routes. Use when categories are real-world places."
11744
+ description: "Geographic concept map: highlight/score regions, drop points of interest, connect with routes or edges"
11468
11745
  },
11469
11746
  // ── Tier 3 — Specialized analytical charts ────────────────
11470
11747
  {
@@ -11481,7 +11758,7 @@ var init_chart_types = __esm({
11481
11758
  },
11482
11759
  {
11483
11760
  id: "slope",
11484
- description: "Change for multiple things between exactly two periods"
11761
+ description: "Change between 2 time periods"
11485
11762
  },
11486
11763
  {
11487
11764
  id: "sankey",
@@ -11510,7 +11787,7 @@ var init_chart_types = __esm({
11510
11787
  // ── Tier 4 — General-purpose data charts ──────────────────
11511
11788
  {
11512
11789
  id: "bar",
11513
- description: "Categorical comparisons",
11790
+ description: "Categorical comparisons for 3 - 5 figures",
11514
11791
  fallback: true
11515
11792
  },
11516
11793
  {
@@ -11772,7 +12049,9 @@ function parseChart(content, palette) {
11772
12049
  if (dataValues) {
11773
12050
  const { label: rawLabel, color: pointColor } = extractColor(
11774
12051
  dataValues.label,
11775
- palette
12052
+ palette,
12053
+ result.diagnostics,
12054
+ lineNumber
11776
12055
  );
11777
12056
  const [first, ...rest] = dataValues.values;
11778
12057
  result.data.push({
@@ -11837,6 +12116,12 @@ function parseChart(content, palette) {
11837
12116
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11838
12117
  );
11839
12118
  }
12119
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12120
+ warn2(
12121
+ result.seriesLineNumber ?? 1,
12122
+ `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.`
12123
+ );
12124
+ }
11840
12125
  if (!result.error && result.seriesNames) {
11841
12126
  const expectedCount = result.seriesNames.length;
11842
12127
  for (const dp of result.data) {
@@ -12107,7 +12392,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12107
12392
  if (!dataRow || dataRow.values.length < 2) return null;
12108
12393
  const { label: rawLabel, color: pointColor } = extractColor(
12109
12394
  dataRow.label,
12110
- palette
12395
+ palette,
12396
+ diagnostics,
12397
+ lineNumber
12111
12398
  );
12112
12399
  return {
12113
12400
  name: rawLabel,
@@ -12245,11 +12532,15 @@ function parseExtendedChartFull(content, palette) {
12245
12532
  const targetResolved = resolveSlot(rawTarget);
12246
12533
  const { label: source, color: sourceColor } = extractColor(
12247
12534
  sourceResolved,
12248
- palette
12535
+ palette,
12536
+ result.diagnostics,
12537
+ lineNumber
12249
12538
  );
12250
12539
  const { label: target, color: targetColor } = extractColor(
12251
12540
  targetResolved,
12252
- palette
12541
+ palette,
12542
+ result.diagnostics,
12543
+ lineNumber
12253
12544
  );
12254
12545
  if (sourceColor || targetColor) {
12255
12546
  if (!result.nodeColors) result.nodeColors = {};
@@ -12301,7 +12592,9 @@ function parseExtendedChartFull(content, palette) {
12301
12592
  const targetResolved = resolveSlot(dataRow2.label);
12302
12593
  const { label: target, color: targetColor } = extractColor(
12303
12594
  targetResolved,
12304
- palette
12595
+ palette,
12596
+ result.diagnostics,
12597
+ lineNumber
12305
12598
  );
12306
12599
  if (targetColor) {
12307
12600
  if (!result.nodeColors) result.nodeColors = {};
@@ -12334,7 +12627,9 @@ function parseExtendedChartFull(content, palette) {
12334
12627
  const trimmedResolved = resolveSlot(trimmed);
12335
12628
  const { label: nodeName, color: nodeColor2 } = extractColor(
12336
12629
  trimmedResolved,
12337
- palette
12630
+ palette,
12631
+ result.diagnostics,
12632
+ lineNumber
12338
12633
  );
12339
12634
  if (nodeColor2) {
12340
12635
  if (!result.nodeColors) result.nodeColors = {};
@@ -12424,8 +12719,11 @@ function parseExtendedChartFull(content, palette) {
12424
12719
  min: parseFloat(rangeMatch[1]),
12425
12720
  max: parseFloat(rangeMatch[2])
12426
12721
  };
12722
+ continue;
12723
+ }
12724
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12725
+ continue;
12427
12726
  }
12428
- continue;
12429
12727
  }
12430
12728
  }
12431
12729
  if (firstToken === "no-name") {
@@ -12485,7 +12783,9 @@ function parseExtendedChartFull(content, palette) {
12485
12783
  if (colonIndex >= 0) {
12486
12784
  const { label: fnName, color: fnColor } = extractColor(
12487
12785
  trimmed.substring(0, colonIndex).trim(),
12488
- palette
12786
+ palette,
12787
+ result.diagnostics,
12788
+ lineNumber
12489
12789
  );
12490
12790
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12491
12791
  if (!result.functions) result.functions = [];
@@ -12533,7 +12833,9 @@ function parseExtendedChartFull(content, palette) {
12533
12833
  if (dataRow?.values.length === 1) {
12534
12834
  const { label: rawLabel, color: pointColor } = extractColor(
12535
12835
  dataRow.label,
12536
- palette
12836
+ palette,
12837
+ result.diagnostics,
12838
+ lineNumber
12537
12839
  );
12538
12840
  result.data.push({
12539
12841
  label: rawLabel,
@@ -12632,11 +12934,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12632
12934
  const sc = ctx ?? ScaleContext.identity();
12633
12935
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12634
12936
  if (parsed.type === "sankey") {
12635
- const bg = isDark ? palette.surface : palette.bg;
12937
+ const bg = themeBaseBg(palette, isDark);
12636
12938
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12637
12939
  }
12638
12940
  if (parsed.type === "chord") {
12639
- const bg = isDark ? palette.surface : palette.bg;
12941
+ const bg = themeBaseBg(palette, isDark);
12640
12942
  return buildChordOption(
12641
12943
  parsed,
12642
12944
  palette,
@@ -12677,7 +12979,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12677
12979
  );
12678
12980
  }
12679
12981
  if (parsed.type === "funnel") {
12680
- const bg = isDark ? palette.surface : palette.bg;
12982
+ const bg = themeBaseBg(palette, isDark);
12681
12983
  return buildFunnelOption(
12682
12984
  parsed,
12683
12985
  palette,
@@ -13273,7 +13575,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13273
13575
  const gridLeft = parsed.ylabel ? 12 : 3;
13274
13576
  const gridRight = 4;
13275
13577
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13276
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13578
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13579
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13277
13580
  let graphic;
13278
13581
  if (showLabels && points.length > 0) {
13279
13582
  const labelPoints = [];
@@ -13413,7 +13716,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13413
13716
  }
13414
13717
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13415
13718
  const sc = ctx ?? ScaleContext.identity();
13416
- const bg = isDark ? palette.surface : palette.bg;
13719
+ const bg = themeBaseBg(palette, isDark);
13417
13720
  const heatmapRows = parsed.heatmapRows ?? [];
13418
13721
  const columns = parsed.columns ?? [];
13419
13722
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13732,7 +14035,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13732
14035
  colors,
13733
14036
  titleConfig
13734
14037
  } = buildChartCommons(parsed, palette, isDark, sc);
13735
- const bg = isDark ? palette.surface : palette.bg;
14038
+ const bg = themeBaseBg(palette, isDark);
13736
14039
  switch (parsed.type) {
13737
14040
  case "bar":
13738
14041
  return buildBarOption(
@@ -14680,12 +14983,7 @@ function parseKanban(content, palette) {
14680
14983
  diagnostics: [],
14681
14984
  error: null
14682
14985
  };
14683
- const fail = (line11, message) => {
14684
- const diag = makeDgmoError(line11, message);
14685
- result.diagnostics.push(diag);
14686
- result.error = formatDgmoError(diag);
14687
- return result;
14688
- };
14986
+ const fail = makeFail(result);
14689
14987
  const warn2 = (line11, message) => {
14690
14988
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14691
14989
  };
@@ -14788,7 +15086,12 @@ function parseKanban(content, palette) {
14788
15086
  const indent2 = measureIndent(line11);
14789
15087
  if (indent2 > 0) {
14790
15088
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14791
- const { label, color } = extractColor(cleanEntry, palette);
15089
+ const { label, color } = extractColor(
15090
+ cleanEntry,
15091
+ palette,
15092
+ result.diagnostics,
15093
+ lineNumber
15094
+ );
14792
15095
  if (isDefault) {
14793
15096
  currentTagGroup.defaultValue = label;
14794
15097
  } else if (currentTagGroup.entries.length === 0) {
@@ -15132,12 +15435,7 @@ function parseC4(content, palette) {
15132
15435
  if (!result.error && severity === "error")
15133
15436
  result.error = formatDgmoError(diag);
15134
15437
  };
15135
- const fail = (line11, message) => {
15136
- const diag = makeDgmoError(line11, message);
15137
- result.diagnostics.push(diag);
15138
- result.error = formatDgmoError(diag);
15139
- return result;
15140
- };
15438
+ const fail = makeFail(result);
15141
15439
  if (!content?.trim()) {
15142
15440
  return fail(0, "No content provided");
15143
15441
  }
@@ -15238,7 +15536,12 @@ function parseC4(content, palette) {
15238
15536
  const indent2 = measureIndent(line11);
15239
15537
  if (indent2 > 0) {
15240
15538
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15241
- const { label, color } = extractColor(cleanEntry, palette);
15539
+ const { label, color } = extractColor(
15540
+ cleanEntry,
15541
+ palette,
15542
+ result.diagnostics,
15543
+ lineNumber
15544
+ );
15242
15545
  if (isDefault) {
15243
15546
  currentTagGroup.defaultValue = label;
15244
15547
  } else if (currentTagGroup.entries.length === 0) {
@@ -16903,8 +17206,66 @@ function parseInfra(content) {
16903
17206
  }
16904
17207
  }
16905
17208
  validateTagGroupNames(result.tagGroups, warn2, setError);
17209
+ checkReachability(result);
16906
17210
  return result;
16907
17211
  }
17212
+ function checkReachability(result) {
17213
+ if (result.nodes.length === 0) return;
17214
+ const entries = result.nodes.filter((n) => n.isEdge);
17215
+ if (entries.length === 0) {
17216
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17217
+ result.diagnostics.push(
17218
+ makeDgmoError(
17219
+ line11,
17220
+ `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.`,
17221
+ "warning",
17222
+ "W_INFRA_NO_ENTRY"
17223
+ )
17224
+ );
17225
+ return;
17226
+ }
17227
+ const groupChildMap = /* @__PURE__ */ new Map();
17228
+ for (const node of result.nodes) {
17229
+ if (node.groupId) {
17230
+ const list = groupChildMap.get(node.groupId) ?? [];
17231
+ list.push(node.id);
17232
+ groupChildMap.set(node.groupId, list);
17233
+ }
17234
+ }
17235
+ const outbound = /* @__PURE__ */ new Map();
17236
+ for (const edge of result.edges) {
17237
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17238
+ const list = outbound.get(edge.sourceId) ?? [];
17239
+ list.push(...targets);
17240
+ outbound.set(edge.sourceId, list);
17241
+ }
17242
+ const reachable = /* @__PURE__ */ new Set();
17243
+ const queue = [];
17244
+ for (const entry of entries) {
17245
+ reachable.add(entry.id);
17246
+ queue.push(entry.id);
17247
+ }
17248
+ while (queue.length > 0) {
17249
+ const current = queue.shift();
17250
+ for (const next of outbound.get(current) ?? []) {
17251
+ if (!reachable.has(next)) {
17252
+ reachable.add(next);
17253
+ queue.push(next);
17254
+ }
17255
+ }
17256
+ }
17257
+ for (const node of result.nodes) {
17258
+ if (node.isEdge || reachable.has(node.id)) continue;
17259
+ result.diagnostics.push(
17260
+ makeDgmoError(
17261
+ node.lineNumber,
17262
+ `'${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.`,
17263
+ "warning",
17264
+ "W_INFRA_UNREACHABLE"
17265
+ )
17266
+ );
17267
+ }
17268
+ }
16908
17269
  function stripNodeDecorations(name) {
16909
17270
  let s = name.trim();
16910
17271
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17194,7 +17555,12 @@ function parseGantt(content, palette) {
17194
17555
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17195
17556
  if (eraEntryMatch) {
17196
17557
  const eraLabelRaw = eraEntryMatch[3].trim();
17197
- const eraExtracted = extractColor(eraLabelRaw, palette);
17558
+ const eraExtracted = extractColor(
17559
+ eraLabelRaw,
17560
+ palette,
17561
+ diagnostics,
17562
+ lineNumber
17563
+ );
17198
17564
  result.eras.push({
17199
17565
  startDate: eraEntryMatch[1],
17200
17566
  endDate: eraEntryMatch[2],
@@ -17216,7 +17582,12 @@ function parseGantt(content, palette) {
17216
17582
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17217
17583
  if (markerEntryMatch) {
17218
17584
  const markerLabelRaw = markerEntryMatch[2].trim();
17219
- const markerExtracted = extractColor(markerLabelRaw, palette);
17585
+ const markerExtracted = extractColor(
17586
+ markerLabelRaw,
17587
+ palette,
17588
+ diagnostics,
17589
+ lineNumber
17590
+ );
17220
17591
  result.markers.push({
17221
17592
  date: markerEntryMatch[1],
17222
17593
  label: markerExtracted.label,
@@ -17237,7 +17608,12 @@ function parseGantt(content, palette) {
17237
17608
  } else {
17238
17609
  if (COMMENT_RE.test(line11)) continue;
17239
17610
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17240
- const extracted = extractColor(cleanEntry, palette);
17611
+ const extracted = extractColor(
17612
+ cleanEntry,
17613
+ palette,
17614
+ diagnostics,
17615
+ lineNumber
17616
+ );
17241
17617
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17242
17618
  const isFirstEntry = currentTagGroup.entries.length === 0;
17243
17619
  currentTagGroup.entries.push({
@@ -17432,7 +17808,12 @@ function parseGantt(content, palette) {
17432
17808
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17433
17809
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17434
17810
  const eraLabelRaw = eraOffsetMatch[3].trim();
17435
- const eraExtracted = extractColor(eraLabelRaw, palette);
17811
+ const eraExtracted = extractColor(
17812
+ eraLabelRaw,
17813
+ palette,
17814
+ diagnostics,
17815
+ lineNumber
17816
+ );
17436
17817
  result.eras.push({
17437
17818
  startDate: "",
17438
17819
  endDate: "",
@@ -17448,7 +17829,12 @@ function parseGantt(content, palette) {
17448
17829
  if (markerOffsetMatch) {
17449
17830
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17450
17831
  const markerLabelRaw = markerOffsetMatch[2].trim();
17451
- const markerExtracted = extractColor(markerLabelRaw, palette);
17832
+ const markerExtracted = extractColor(
17833
+ markerLabelRaw,
17834
+ palette,
17835
+ diagnostics,
17836
+ lineNumber
17837
+ );
17452
17838
  result.markers.push({
17453
17839
  date: "",
17454
17840
  label: markerExtracted.label,
@@ -17522,7 +17908,12 @@ function parseGantt(content, palette) {
17522
17908
  const eraMatch = line11.match(ERA_RE);
17523
17909
  if (eraMatch) {
17524
17910
  const eraLabelRaw = eraMatch[3].trim();
17525
- const eraExtracted = extractColor(eraLabelRaw, palette);
17911
+ const eraExtracted = extractColor(
17912
+ eraLabelRaw,
17913
+ palette,
17914
+ diagnostics,
17915
+ lineNumber
17916
+ );
17526
17917
  result.eras.push({
17527
17918
  startDate: eraMatch[1],
17528
17919
  endDate: eraMatch[2],
@@ -17540,7 +17931,12 @@ function parseGantt(content, palette) {
17540
17931
  const markerMatch = line11.match(MARKER_RE);
17541
17932
  if (markerMatch) {
17542
17933
  const markerLabelRaw = markerMatch[2].trim();
17543
- const markerExtracted = extractColor(markerLabelRaw, palette);
17934
+ const markerExtracted = extractColor(
17935
+ markerLabelRaw,
17936
+ palette,
17937
+ diagnostics,
17938
+ lineNumber
17939
+ );
17544
17940
  result.markers.push({
17545
17941
  date: markerMatch[1],
17546
17942
  label: markerExtracted.label,
@@ -19012,7 +19408,7 @@ function parseBoxesAndLines(content, palette) {
19012
19408
  const trimmed = raw.trim();
19013
19409
  const indent = measureIndent2(raw);
19014
19410
  if (!trimmed || trimmed.startsWith("//")) continue;
19015
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19411
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
19016
19412
  result.diagnostics.push(
19017
19413
  makeDgmoError(
19018
19414
  lineNum,
@@ -19141,7 +19537,12 @@ function parseBoxesAndLines(content, palette) {
19141
19537
  if (tagBlockMatch.inlineValues) {
19142
19538
  for (const rawVal of tagBlockMatch.inlineValues) {
19143
19539
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19144
- const { label, color } = extractColor(cleanVal);
19540
+ const { label, color } = extractColor(
19541
+ cleanVal,
19542
+ palette,
19543
+ result.diagnostics,
19544
+ lineNum
19545
+ );
19145
19546
  newTagGroup.entries.push({
19146
19547
  value: label,
19147
19548
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19158,7 +19559,12 @@ function parseBoxesAndLines(content, palette) {
19158
19559
  }
19159
19560
  if (currentTagGroup && !contentStarted && indent > 0) {
19160
19561
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19161
- const { label, color } = extractColor(cleanEntry);
19562
+ const { label, color } = extractColor(
19563
+ cleanEntry,
19564
+ palette,
19565
+ result.diagnostics,
19566
+ lineNum
19567
+ );
19162
19568
  currentTagGroup.entries.push({
19163
19569
  value: label,
19164
19570
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19756,7 +20162,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19756
20162
  metadata
19757
20163
  };
19758
20164
  }
19759
- var MAX_GROUP_DEPTH;
20165
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19760
20166
  var init_parser18 = __esm({
19761
20167
  "src/boxes-and-lines/parser.ts"() {
19762
20168
  "use strict";
@@ -19768,6 +20174,8 @@ var init_parser18 = __esm({
19768
20174
  init_reserved_key_registry();
19769
20175
  init_notes();
19770
20176
  MAX_GROUP_DEPTH = 2;
20177
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20178
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19771
20179
  }
19772
20180
  });
19773
20181
 
@@ -19787,12 +20195,7 @@ function parseMindmap(content, palette) {
19787
20195
  diagnostics: [],
19788
20196
  error: null
19789
20197
  };
19790
- const fail = (line11, message) => {
19791
- const diag = makeDgmoError(line11, message);
19792
- result.diagnostics.push(diag);
19793
- result.error = formatDgmoError(diag);
19794
- return result;
19795
- };
20198
+ const fail = makeFail(result);
19796
20199
  const pushError = (line11, message) => {
19797
20200
  const diag = makeDgmoError(line11, message);
19798
20201
  result.diagnostics.push(diag);
@@ -19905,7 +20308,12 @@ function parseMindmap(content, palette) {
19905
20308
  const indent2 = measureIndent(line11);
19906
20309
  if (indent2 > 0) {
19907
20310
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19908
- const { label, color } = extractColor(cleanEntry, palette);
20311
+ const { label, color } = extractColor(
20312
+ cleanEntry,
20313
+ palette,
20314
+ result.diagnostics,
20315
+ lineNumber
20316
+ );
19909
20317
  if (isDefault) {
19910
20318
  currentTagGroup.defaultValue = label;
19911
20319
  } else if (currentTagGroup.entries.length === 0) {
@@ -19982,6 +20390,7 @@ function parseMindmap(content, palette) {
19982
20390
  collectAll(result.roots);
19983
20391
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
19984
20392
  validateTagGroupNames(result.tagGroups, pushWarning);
20393
+ cascadeTagMetadata(result.roots, result.tagGroups);
19985
20394
  }
19986
20395
  if (result.roots.length === 0 && !result.error) {
19987
20396
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20588,7 +20997,12 @@ function parseWireframe(content) {
20588
20997
  }
20589
20998
  if (indent > 0 && currentTagGroup) {
20590
20999
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20591
- const { label, color } = extractColor(cleanEntry);
21000
+ const { label, color } = extractColor(
21001
+ cleanEntry,
21002
+ void 0,
21003
+ diagnostics,
21004
+ lineNumber
21005
+ );
20592
21006
  currentTagGroup.entries.push({
20593
21007
  value: label,
20594
21008
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20836,12 +21250,7 @@ function parseTechRadar(content) {
20836
21250
  diagnostics: [],
20837
21251
  error: null
20838
21252
  };
20839
- const fail = (line11, message) => {
20840
- const diag = makeDgmoError(line11, message);
20841
- result.diagnostics.push(diag);
20842
- result.error = formatDgmoError(diag);
20843
- return result;
20844
- };
21253
+ const fail = makeFail(result);
20845
21254
  const warn2 = (line11, message) => {
20846
21255
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20847
21256
  };
@@ -21209,12 +21618,7 @@ function parseCycle(content) {
21209
21618
  let state = "top";
21210
21619
  let currentNode = null;
21211
21620
  let currentEdge = null;
21212
- const fail = (line11, message) => {
21213
- const diag = makeDgmoError(line11, message);
21214
- result.diagnostics.push(diag);
21215
- result.error = formatDgmoError(diag);
21216
- return result;
21217
- };
21621
+ const fail = makeFail(result);
21218
21622
  const warn2 = (line11, message, severity = "warning") => {
21219
21623
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21220
21624
  };
@@ -21491,12 +21895,7 @@ function parseJourneyMap(content, palette) {
21491
21895
  diagnostics: [],
21492
21896
  error: null
21493
21897
  };
21494
- const fail = (line11, message) => {
21495
- const diag = makeDgmoError(line11, message);
21496
- result.diagnostics.push(diag);
21497
- result.error = formatDgmoError(diag);
21498
- return result;
21499
- };
21898
+ const fail = makeFail(result);
21500
21899
  const warn2 = (line11, message) => {
21501
21900
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21502
21901
  };
@@ -21645,7 +22044,12 @@ function parseJourneyMap(content, palette) {
21645
22044
  if (currentTagGroup && !contentStarted) {
21646
22045
  if (indent > 0) {
21647
22046
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21648
- const { label, color } = extractColor(cleanEntry, palette);
22047
+ const { label, color } = extractColor(
22048
+ cleanEntry,
22049
+ palette,
22050
+ result.diagnostics,
22051
+ lineNumber
22052
+ );
21649
22053
  if (isDefault) {
21650
22054
  currentTagGroup.defaultValue = label;
21651
22055
  } else if (currentTagGroup.entries.length === 0) {
@@ -22017,12 +22421,7 @@ function parsePyramid(content) {
22017
22421
  const lines = content.split("\n");
22018
22422
  let headerParsed = false;
22019
22423
  let currentLayer = null;
22020
- const fail = (line11, message) => {
22021
- const diag = makeDgmoError(line11, message);
22022
- result.diagnostics.push(diag);
22023
- result.error = formatDgmoError(diag);
22024
- return result;
22025
- };
22424
+ const fail = makeFail(result);
22026
22425
  const warn2 = (line11, message, severity = "warning") => {
22027
22426
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22028
22427
  };
@@ -22169,12 +22568,7 @@ function parseRing(content) {
22169
22568
  const lines = content.split("\n");
22170
22569
  let headerParsed = false;
22171
22570
  let currentLayer = null;
22172
- const fail = (line11, message) => {
22173
- const diag = makeDgmoError(line11, message);
22174
- result.diagnostics.push(diag);
22175
- result.error = formatDgmoError(diag);
22176
- return result;
22177
- };
22571
+ const fail = makeFail(result);
22178
22572
  const warn2 = (line11, message, severity = "warning") => {
22179
22573
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22180
22574
  };
@@ -22638,12 +23032,7 @@ function parseRaci(content, palette) {
22638
23032
  diagnostics: [],
22639
23033
  error: null
22640
23034
  };
22641
- const fail = (line11, message) => {
22642
- const diag = makeDgmoError(line11, message);
22643
- result.diagnostics.push(diag);
22644
- result.error = formatDgmoError(diag);
22645
- return result;
22646
- };
23035
+ const fail = makeFail(result);
22647
23036
  const warn2 = (line11, message, code) => {
22648
23037
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22649
23038
  };
@@ -23246,6 +23635,81 @@ function measureInfra(content) {
23246
23635
  const parsed = parseInfra(content);
23247
23636
  return { nodes: parsed.nodes.length };
23248
23637
  }
23638
+ function minDimsSequence(c) {
23639
+ return {
23640
+ width: Math.max((c.participants ?? 2) * 80, 320),
23641
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23642
+ };
23643
+ }
23644
+ function minDimsRaci(c) {
23645
+ return {
23646
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23647
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23648
+ };
23649
+ }
23650
+ function minDimsMindmap(c) {
23651
+ return {
23652
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23653
+ height: Math.max((c.depth ?? 2) * 60, 200)
23654
+ };
23655
+ }
23656
+ function minDimsTechRadar() {
23657
+ return { width: 360, height: 400 };
23658
+ }
23659
+ function minDimsHeatmap(c) {
23660
+ return {
23661
+ width: Math.max((c.columns ?? 3) * 40, 300),
23662
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23663
+ };
23664
+ }
23665
+ function minDimsArc(c) {
23666
+ return {
23667
+ width: 300,
23668
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23669
+ };
23670
+ }
23671
+ function minDimsOrg(c) {
23672
+ return {
23673
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23674
+ height: Math.max((c.depth ?? 2) * 80, 200)
23675
+ };
23676
+ }
23677
+ function minDimsGantt(c) {
23678
+ return {
23679
+ width: 400,
23680
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23681
+ };
23682
+ }
23683
+ function minDimsKanban(c) {
23684
+ return {
23685
+ width: Math.max((c.columns ?? 3) * 120, 360),
23686
+ height: 300
23687
+ };
23688
+ }
23689
+ function minDimsEntities(c) {
23690
+ return {
23691
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23692
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23693
+ };
23694
+ }
23695
+ function minDimsGraph(c) {
23696
+ return {
23697
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23698
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23699
+ };
23700
+ }
23701
+ function minDimsPert(c) {
23702
+ return {
23703
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23704
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23705
+ };
23706
+ }
23707
+ function minDimsInfra(c) {
23708
+ return {
23709
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23710
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23711
+ };
23712
+ }
23249
23713
  function isExtendedChartParser(parse) {
23250
23714
  return EXTENDED_CHART_DOORS.has(parse);
23251
23715
  }
@@ -23289,33 +23753,50 @@ var init_chart_type_registry = __esm({
23289
23753
  id: "sequence",
23290
23754
  category: "diagram",
23291
23755
  parse: parseSequenceDgmo,
23292
- measure: measureSequence
23756
+ measure: measureSequence,
23757
+ minDims: minDimsSequence
23293
23758
  },
23294
23759
  {
23295
23760
  id: "flowchart",
23296
23761
  category: "diagram",
23297
23762
  parse: parseFlowchart,
23298
- measure: measureFlowchart
23763
+ measure: measureFlowchart,
23764
+ minDims: minDimsGraph
23299
23765
  },
23300
23766
  {
23301
23767
  id: "class",
23302
23768
  category: "diagram",
23303
23769
  parse: parseClassDiagram,
23304
- measure: measureClass
23770
+ measure: measureClass,
23771
+ minDims: minDimsEntities
23772
+ },
23773
+ {
23774
+ id: "er",
23775
+ category: "diagram",
23776
+ parse: parseERDiagram,
23777
+ measure: measureER,
23778
+ minDims: minDimsEntities
23305
23779
  },
23306
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23307
23780
  {
23308
23781
  id: "state",
23309
23782
  category: "diagram",
23310
23783
  parse: parseState,
23311
- measure: measureStateGraph
23784
+ measure: measureStateGraph,
23785
+ minDims: minDimsGraph
23786
+ },
23787
+ {
23788
+ id: "org",
23789
+ category: "diagram",
23790
+ parse: parseOrg,
23791
+ measure: measureOrg,
23792
+ minDims: minDimsOrg
23312
23793
  },
23313
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23314
23794
  {
23315
23795
  id: "kanban",
23316
23796
  category: "diagram",
23317
23797
  parse: parseKanban,
23318
- measure: measureKanban
23798
+ measure: measureKanban,
23799
+ minDims: minDimsKanban
23319
23800
  },
23320
23801
  { id: "c4", category: "diagram", parse: parseC4 },
23321
23802
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23323,25 +23804,40 @@ var init_chart_type_registry = __esm({
23323
23804
  id: "infra",
23324
23805
  category: "diagram",
23325
23806
  parse: parseInfra,
23326
- measure: measureInfra
23807
+ measure: measureInfra,
23808
+ minDims: minDimsInfra
23327
23809
  },
23328
23810
  {
23329
23811
  id: "gantt",
23330
23812
  category: "diagram",
23331
23813
  parse: parseGantt,
23332
- measure: measureGantt
23814
+ measure: measureGantt,
23815
+ minDims: minDimsGantt
23816
+ },
23817
+ {
23818
+ id: "pert",
23819
+ category: "diagram",
23820
+ parse: parsePert,
23821
+ measure: measurePert,
23822
+ minDims: minDimsPert
23333
23823
  },
23334
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23335
23824
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23336
23825
  {
23337
23826
  id: "mindmap",
23338
23827
  category: "diagram",
23339
23828
  parse: parseMindmap,
23340
- measure: measureMindmap
23829
+ measure: measureMindmap,
23830
+ minDims: minDimsMindmap
23341
23831
  },
23342
23832
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23343
23833
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23344
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23834
+ {
23835
+ id: "raci",
23836
+ category: "diagram",
23837
+ parse: parseRaci,
23838
+ measure: measureRaci,
23839
+ minDims: minDimsRaci
23840
+ },
23345
23841
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23346
23842
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23347
23843
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23363,7 +23859,8 @@ var init_chart_type_registry = __esm({
23363
23859
  id: "heatmap",
23364
23860
  category: "data-chart",
23365
23861
  parse: parseHeatmap,
23366
- measure: measureHeatmap
23862
+ measure: measureHeatmap,
23863
+ minDims: minDimsHeatmap
23367
23864
  },
23368
23865
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23369
23866
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23373,7 +23870,8 @@ var init_chart_type_registry = __esm({
23373
23870
  id: "arc",
23374
23871
  category: "visualization",
23375
23872
  parse: parseArc,
23376
- measure: measureArc
23873
+ measure: measureArc,
23874
+ minDims: minDimsArc
23377
23875
  },
23378
23876
  { id: "timeline", category: "visualization", parse: parseTimeline },
23379
23877
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23383,7 +23881,8 @@ var init_chart_type_registry = __esm({
23383
23881
  id: "tech-radar",
23384
23882
  category: "visualization",
23385
23883
  parse: parseTechRadar,
23386
- measure: measureTechRadar
23884
+ measure: measureTechRadar,
23885
+ minDims: minDimsTechRadar
23387
23886
  },
23388
23887
  { id: "cycle", category: "visualization", parse: parseCycle },
23389
23888
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24605,7 +25104,7 @@ function nodeStroke(palette, nodeColor2) {
24605
25104
  }
24606
25105
  function containerFill(palette, isDark, nodeColor2) {
24607
25106
  if (nodeColor2) {
24608
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25107
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24609
25108
  }
24610
25109
  return mix(palette.surface, palette.bg, 40);
24611
25110
  }
@@ -24763,10 +25262,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24763
25262
  const iconY = iconPad;
24764
25263
  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})`);
24765
25264
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25265
+ const iconColor = contrastText(
25266
+ fill2,
25267
+ palette.textOnFillLight,
25268
+ palette.textOnFillDark
25269
+ );
24766
25270
  const cx = iconSize / 2;
24767
25271
  const cy = iconSize / 2;
24768
- 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);
24769
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25272
+ 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);
25273
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24770
25274
  }
24771
25275
  }
24772
25276
  for (const edge of layout.edges) {
@@ -24855,10 +25359,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24855
25359
  const iconY = iconPad;
24856
25360
  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})`);
24857
25361
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25362
+ const iconColor = labelColor;
24858
25363
  const cx = iconSize / 2;
24859
25364
  const cy = iconSize / 2;
24860
- 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);
24861
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25365
+ 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);
25366
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24862
25367
  }
24863
25368
  }
24864
25369
  if (hasAncestorTrail) {
@@ -25780,7 +26285,7 @@ function nodeStroke2(_palette, nodeColor2) {
25780
26285
  }
25781
26286
  function containerFill2(palette, isDark, nodeColor2) {
25782
26287
  if (nodeColor2) {
25783
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26288
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25784
26289
  }
25785
26290
  return mix(palette.surface, palette.bg, 40);
25786
26291
  }
@@ -28587,7 +29092,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28587
29092
  onToggleDescriptions,
28588
29093
  onToggleControlsExpand,
28589
29094
  exportMode = false,
28590
- controlsHost
29095
+ controlsHost,
29096
+ rampDomain
28591
29097
  } = options ?? {};
28592
29098
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28593
29099
  const width = exportDims?.width ?? container.clientWidth;
@@ -28607,8 +29113,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28607
29113
  const sTitleY = sctx.structural(TITLE_Y);
28608
29114
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28609
29115
  const hasRamp = nodeValues.length > 0;
28610
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28611
- const rampMax = Math.max(...nodeValues);
29116
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29117
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28612
29118
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28613
29119
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28614
29120
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28760,7 +29266,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28760
29266
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28761
29267
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28762
29268
  if (group.collapsed) {
28763
- const fillColor = isDark ? palette.surface : palette.bg;
29269
+ const fillColor = themeBaseBg(palette, isDark);
28764
29270
  const strokeColor = palette.border;
28765
29271
  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);
28766
29272
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -29116,6 +29622,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29116
29622
  });
29117
29623
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29118
29624
  }
29625
+ if (!exportDims && !exportMode) {
29626
+ const iconSize = 14;
29627
+ 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");
29628
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29629
+ const cx = iconSize / 2;
29630
+ const cy = iconSize / 2;
29631
+ 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);
29632
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29633
+ }
29119
29634
  }
29120
29635
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29121
29636
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29712,7 +30227,7 @@ function shuffle(a, r) {
29712
30227
  return x;
29713
30228
  }
29714
30229
  function flatten(d) {
29715
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30230
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29716
30231
  const pts = [];
29717
30232
  let i = 0, cx = 0, cy = 0, cmd = "";
29718
30233
  const num = () => parseFloat(toks[i++]);
@@ -29756,17 +30271,10 @@ function flatten(d) {
29756
30271
  }
29757
30272
  return pts;
29758
30273
  }
29759
- function segPoint(p1, p2, p3, p4) {
29760
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29761
- if (Math.abs(den) < 1e-9) return null;
29762
- 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;
29763
- 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;
29764
- }
29765
- function countSplineCrossings(layout) {
29766
- const center = /* @__PURE__ */ new Map();
29767
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29768
- for (const g of layout.groups)
29769
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30274
+ function flatPolys(layout) {
30275
+ const key = layout.edges;
30276
+ const hit = FLAT_CACHE.get(key);
30277
+ if (hit) return hit;
29770
30278
  const polys = layout.edges.map((e) => {
29771
30279
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29772
30280
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29776,8 +30284,24 @@ function countSplineCrossings(layout) {
29776
30284
  if (p.y < y0) y0 = p.y;
29777
30285
  if (p.y > y1) y1 = p.y;
29778
30286
  }
29779
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30287
+ return { pts, x0, y0, x1, y1 };
29780
30288
  });
30289
+ FLAT_CACHE.set(key, polys);
30290
+ return polys;
30291
+ }
30292
+ function segPoint(p1, p2, p3, p4) {
30293
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30294
+ if (Math.abs(den) < 1e-9) return null;
30295
+ 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;
30296
+ 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;
30297
+ }
30298
+ function countSplineCrossings(layout, floor = Infinity) {
30299
+ const center = /* @__PURE__ */ new Map();
30300
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30301
+ for (const g of layout.groups)
30302
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30303
+ const polys = flatPolys(layout);
30304
+ const edges = layout.edges;
29781
30305
  const R = 34;
29782
30306
  let total = 0;
29783
30307
  for (let a = 0; a < polys.length; a++)
@@ -29785,23 +30309,33 @@ function countSplineCrossings(layout) {
29785
30309
  const A = polys[a], B = polys[b];
29786
30310
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29787
30311
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29788
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30312
+ const ea = edges[a], eb = edges[b];
30313
+ let sh0, sh1;
30314
+ if (ea.source === eb.source || ea.source === eb.target)
30315
+ sh0 = center.get(ea.source);
30316
+ if (ea.target === eb.source || ea.target === eb.target)
30317
+ sh1 = center.get(ea.target);
29789
30318
  const hits = [];
29790
- for (let i = 1; i < A.pts.length; i++)
29791
- for (let j = 1; j < B.pts.length; j++) {
29792
- const p = segPoint(
29793
- A.pts[i - 1],
29794
- A.pts[i],
29795
- B.pts[j - 1],
29796
- B.pts[j]
29797
- );
30319
+ const ap = A.pts, bp = B.pts;
30320
+ for (let i = 1; i < ap.length; i++) {
30321
+ const a0 = ap[i - 1], a1 = ap[i];
30322
+ 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;
30323
+ for (let j = 1; j < bp.length; j++) {
30324
+ const b0 = bp[j - 1], b1 = bp[j];
30325
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30326
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30327
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30328
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30329
+ const p = segPoint(a0, a1, b0, b1);
29798
30330
  if (!p) continue;
29799
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29800
- continue;
30331
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30332
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29801
30333
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29802
30334
  hits.push(p);
29803
30335
  }
30336
+ }
29804
30337
  total += hits.length;
30338
+ if (total > floor) return total;
29805
30339
  }
29806
30340
  return total;
29807
30341
  }
@@ -29839,17 +30373,8 @@ function detectEdgeOverlaps(layout, opts) {
29839
30373
  w: g.width,
29840
30374
  h: g.height
29841
30375
  });
29842
- const polys = layout.edges.map((e) => {
29843
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29844
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29845
- for (const p of pts) {
29846
- if (p.x < x0) x0 = p.x;
29847
- if (p.x > x1) x1 = p.x;
29848
- if (p.y < y0) y0 = p.y;
29849
- if (p.y > y1) y1 = p.y;
29850
- }
29851
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29852
- });
30376
+ const polys = flatPolys(layout);
30377
+ const edges = layout.edges;
29853
30378
  const runs = [];
29854
30379
  for (let a = 0; a < polys.length; a++)
29855
30380
  for (let b = a + 1; b < polys.length; b++) {
@@ -29857,7 +30382,12 @@ function detectEdgeOverlaps(layout, opts) {
29857
30382
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29858
30383
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29859
30384
  continue;
29860
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30385
+ const ea = edges[a], eb = edges[b];
30386
+ let shr0, shr1;
30387
+ if (ea.source === eb.source || ea.source === eb.target)
30388
+ shr0 = rect.get(ea.source);
30389
+ if (ea.target === eb.source || ea.target === eb.target)
30390
+ shr1 = rect.get(ea.target);
29861
30391
  let run2 = [];
29862
30392
  let runLen = 0;
29863
30393
  const flush = () => {
@@ -29871,7 +30401,7 @@ function detectEdgeOverlaps(layout, opts) {
29871
30401
  runLen = 0;
29872
30402
  };
29873
30403
  for (const p of A.pts) {
29874
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30404
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
29875
30405
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29876
30406
  if (covered) {
29877
30407
  if (run2.length)
@@ -29906,9 +30436,10 @@ function detectEdgeNodePierces(layout, opts) {
29906
30436
  });
29907
30437
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
29908
30438
  const out = [];
30439
+ const polys = flatPolys(layout);
29909
30440
  layout.edges.forEach((e, idx) => {
29910
30441
  if (e.points.length < 2) return;
29911
- const poly = flatten(splineGen(e.points) ?? "");
30442
+ const poly = polys[idx].pts;
29912
30443
  for (const r of rects) {
29913
30444
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29914
30445
  continue;
@@ -30255,8 +30786,10 @@ function edgeLength(layout) {
30255
30786
  );
30256
30787
  return total;
30257
30788
  }
30258
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30789
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30259
30790
  const hideDescriptions = opts?.hideDescriptions ?? false;
30791
+ const onProgress = opts?.onProgress;
30792
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30260
30793
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30261
30794
  if (collapseInfo) {
30262
30795
  const missing = /* @__PURE__ */ new Set();
@@ -30670,17 +31203,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30670
31203
  seed: s
30671
31204
  });
30672
31205
  const badness = (lay, floor) => {
30673
- const x = countSplineCrossings(lay);
31206
+ const x = countSplineCrossings(lay, floor);
30674
31207
  if (x > floor) return Infinity;
30675
31208
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30676
31209
  };
30677
31210
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31211
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31212
+ let progressDone = 0;
31213
+ const step = async (phase) => {
31214
+ if (!onProgress) return;
31215
+ onProgress(++progressDone, progressTotal, phase);
31216
+ await tick();
31217
+ };
30678
31218
  const pool = [];
30679
31219
  for (const cfg of configs) {
30680
31220
  try {
30681
31221
  pool.push(place(cfg));
30682
31222
  } catch {
30683
31223
  }
31224
+ await step("Optimizing layout");
30684
31225
  }
30685
31226
  if (!pool.length)
30686
31227
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30689,9 +31230,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30689
31230
  layered = layeredCandidates(parsed, sizes);
30690
31231
  } catch {
30691
31232
  }
30692
- pool.sort(
30693
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30694
- );
31233
+ const fastKey = /* @__PURE__ */ new Map();
31234
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31235
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30695
31236
  const refineK = Math.min(REFINE_K, pool.length);
30696
31237
  let best = pool[0];
30697
31238
  let bestObj = Infinity;
@@ -30706,7 +31247,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30706
31247
  best = lay;
30707
31248
  }
30708
31249
  };
30709
- for (const lay of pool.slice(0, refineK)) consider(lay);
31250
+ for (const lay of pool.slice(0, refineK)) {
31251
+ consider(lay);
31252
+ await step("Refining layout");
31253
+ }
30710
31254
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30711
31255
  const extra = [];
30712
31256
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30722,9 +31266,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30722
31266
  } catch {
30723
31267
  }
30724
31268
  }
30725
- extra.sort(
30726
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30727
- );
31269
+ const extraKey = /* @__PURE__ */ new Map();
31270
+ for (const lay of extra)
31271
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31272
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30728
31273
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30729
31274
  }
30730
31275
  for (const lay of layered) {
@@ -30751,7 +31296,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30751
31296
  }
30752
31297
  return best;
30753
31298
  }
30754
- var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31299
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30755
31300
  var init_layout_search = __esm({
30756
31301
  "src/boxes-and-lines/layout-search.ts"() {
30757
31302
  "use strict";
@@ -30763,6 +31308,8 @@ var init_layout_search = __esm({
30763
31308
  ESCALATE_SEEDS = 18;
30764
31309
  ESCALATE_REFINE = 10;
30765
31310
  splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
31311
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31312
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30766
31313
  GROUP_LABEL_ZONE2 = 32;
30767
31314
  }
30768
31315
  });
@@ -30847,12 +31394,15 @@ function computeNodeSize(node, reserveValueRow) {
30847
31394
  }
30848
31395
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30849
31396
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30850
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31397
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30851
31398
  ...layoutOptions?.hideDescriptions !== void 0 && {
30852
31399
  hideDescriptions: layoutOptions.hideDescriptions
30853
31400
  },
30854
31401
  ...layoutOptions?.previousPositions !== void 0 && {
30855
31402
  previousPositions: layoutOptions.previousPositions
31403
+ },
31404
+ ...layoutOptions?.onProgress !== void 0 && {
31405
+ onProgress: layoutOptions.onProgress
30856
31406
  }
30857
31407
  });
30858
31408
  return attachNotes(
@@ -31637,7 +32187,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31637
32187
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31638
32188
  const availWidth = containerWidth;
31639
32189
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31640
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32190
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32191
+ availWidth,
32192
+ layout.width,
32193
+ availHeight,
32194
+ layout.height
32195
+ );
31641
32196
  let renderLayout = layout;
31642
32197
  if (ctx.factor < 1) {
31643
32198
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31646,17 +32201,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31646
32201
  hiddenCounts.set(n.id, n.hiddenCount);
31647
32202
  }
31648
32203
  }
31649
- renderLayout = layoutMindmap(parsed, palette, {
32204
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31650
32205
  interactive: !isExport,
31651
32206
  ...hiddenCounts.size > 0 && { hiddenCounts },
31652
32207
  activeTagGroup: activeTagGroup ?? null,
31653
32208
  ...hideDescriptions !== void 0 && { hideDescriptions },
31654
- ctx
32209
+ ctx: c
31655
32210
  });
32211
+ renderLayout = relayout(ctx);
32212
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32213
+ const refit = Math.min(
32214
+ availWidth / renderLayout.width,
32215
+ availHeight / renderLayout.height
32216
+ );
32217
+ if (refit >= 0.999) break;
32218
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32219
+ renderLayout = relayout(ctx);
32220
+ }
31656
32221
  }
31657
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31658
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31659
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32222
+ const fitScale = isExport ? 1 : Math.min(
32223
+ 1,
32224
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32225
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32226
+ );
32227
+ const scaledWidth = renderLayout.width * fitScale;
32228
+ const scaledHeight = renderLayout.height * fitScale;
32229
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32230
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32231
+ const mainG = svg.append("g").attr(
32232
+ "transform",
32233
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32234
+ );
31660
32235
  if (ctx.isBelowFloor) {
31661
32236
  svg.attr("width", "100%");
31662
32237
  }
@@ -41000,7 +41575,7 @@ __export(renderer_exports11, {
41000
41575
  import * as d3Selection17 from "d3-selection";
41001
41576
  import * as d3Shape9 from "d3-shape";
41002
41577
  function analysisBlockChrome(palette, isDark) {
41003
- const surfaceBg = isDark ? palette.surface : palette.bg;
41578
+ const surfaceBg = themeBaseBg(palette, isDark);
41004
41579
  return {
41005
41580
  fill: mix(palette.surface, palette.bg, 40),
41006
41581
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -45711,7 +46286,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45711
46286
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45712
46287
  const gw = group.width + sGroupExtraPadding * 2;
45713
46288
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45714
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46289
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45715
46290
  const strokeColor = group.color ?? palette.textMuted;
45716
46291
  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");
45717
46292
  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");
@@ -49683,6 +50258,16 @@ __export(resolver_exports, {
49683
50258
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49684
50259
  resolveMap: () => resolveMap
49685
50260
  });
50261
+ function containerOvershoot(span, usOriented) {
50262
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50263
+ return Math.max(
50264
+ CONTAINER_OVERSHOOT_MIN,
50265
+ Math.min(
50266
+ CONTAINER_OVERSHOOT_MAX,
50267
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50268
+ )
50269
+ );
50270
+ }
49686
50271
  function bboxArea(b) {
49687
50272
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49688
50273
  }
@@ -50179,7 +50764,7 @@ function resolveMap(parsed, data) {
50179
50764
  const containerUnion = unionExtent(containerBoxes, points);
50180
50765
  if (containerUnion)
50181
50766
  extent3 = pad(
50182
- clampContainerToCluster(containerUnion, points),
50767
+ clampContainerToCluster(containerUnion, points, usOriented),
50183
50768
  PAD_FRACTION
50184
50769
  );
50185
50770
  }
@@ -50263,17 +50848,21 @@ function mostCommonCountry(regions, poiCountries) {
50263
50848
  }
50264
50849
  return best;
50265
50850
  }
50266
- function clampContainerToCluster(container, points) {
50851
+ function clampContainerToCluster(container, points, usOriented) {
50267
50852
  const poi = unionExtent([], points);
50268
50853
  if (!poi) return container;
50269
50854
  let [[west, south], [east, north]] = container;
50270
50855
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50271
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50272
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50273
- if (east <= 180 && pEast <= 180) {
50274
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50275
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50276
- }
50856
+ const over = containerOvershoot(
50857
+ Math.max(pEast - pWest, pNorth - pSouth),
50858
+ usOriented
50859
+ );
50860
+ south = Math.max(south, pSouth - over);
50861
+ north = Math.min(north, pNorth + over);
50862
+ const wOver = pWest - over;
50863
+ const eOver = pEast + over;
50864
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
50865
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
50277
50866
  return [
50278
50867
  [west, south],
50279
50868
  [east, north]
@@ -50291,7 +50880,7 @@ function firstError(diags) {
50291
50880
  const e = diags.find((d) => d.severity === "error");
50292
50881
  return e ? formatDgmoError(e) : null;
50293
50882
  }
50294
- 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;
50883
+ 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;
50295
50884
  var init_resolver2 = __esm({
50296
50885
  "src/map/resolver.ts"() {
50297
50886
  "use strict";
@@ -50304,7 +50893,9 @@ var init_resolver2 = __esm({
50304
50893
  WORLD_LAT_SOUTH = -58;
50305
50894
  WORLD_LAT_NORTH = 78;
50306
50895
  POI_ZOOM_FLOOR_DEG = 7;
50307
- CONTAINER_OVERSHOOT_DEG = 8;
50896
+ CONTAINER_OVERSHOOT_MAX = 8;
50897
+ CONTAINER_OVERSHOOT_MIN = 3;
50898
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50308
50899
  US_NATIONAL_LON_SPAN = 48;
50309
50900
  CONUS_BBOX = [
50310
50901
  [-125, 25],
@@ -50470,12 +51061,12 @@ function tierBand(maxSpanDeg) {
50470
51061
  }
50471
51062
  function labelBudget(width, height, band) {
50472
51063
  const bandCap = {
50473
- world: 7,
50474
- continental: 6,
50475
- regional: 5,
50476
- local: 4
51064
+ world: 10,
51065
+ continental: 9,
51066
+ regional: 7,
51067
+ local: 6
50477
51068
  };
50478
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51069
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50479
51070
  return Math.max(0, Math.min(area2, bandCap[band]));
50480
51071
  }
50481
51072
  function waterEligible(tier, kind, band) {
@@ -50529,6 +51120,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50529
51120
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50530
51121
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50531
51122
  }
51123
+ function rectDist2(px, py, x0, y0, x1, y1) {
51124
+ const dx = Math.max(x0 - px, 0, px - x1);
51125
+ const dy = Math.max(y0 - py, 0, py - y1);
51126
+ return dx * dx + dy * dy;
51127
+ }
50532
51128
  function rectFits(r, width, height) {
50533
51129
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50534
51130
  }
@@ -50547,6 +51143,7 @@ function placeContextLabels(args) {
50547
51143
  palette,
50548
51144
  project,
50549
51145
  collides,
51146
+ contentPoints,
50550
51147
  overLand
50551
51148
  } = args;
50552
51149
  void projection;
@@ -50617,8 +51214,17 @@ function placeContextLabels(args) {
50617
51214
  const [x0, y0, x1, y1] = c.bbox;
50618
51215
  const w = x1 - x0;
50619
51216
  const h = y1 - y0;
50620
- return { c, w, h, area: w * h };
50621
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51217
+ let dist = Infinity;
51218
+ if (contentPoints?.length) {
51219
+ for (const p of contentPoints) {
51220
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51221
+ if (d < dist) dist = d;
51222
+ }
51223
+ }
51224
+ return { c, w, h, area: w * h, dist };
51225
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51226
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51227
+ );
50622
51228
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50623
51229
  let ci = 0;
50624
51230
  for (const r of ranked) {
@@ -50635,7 +51241,7 @@ function placeContextLabels(args) {
50635
51241
  );
50636
51242
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50637
51243
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50638
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51244
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50639
51245
  const text = c.name;
50640
51246
  const tw = labelWidth(text, 0, fontSize);
50641
51247
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50648,6 +51254,9 @@ function placeContextLabels(args) {
50648
51254
  letterSpacing: 0,
50649
51255
  color,
50650
51256
  fontSize,
51257
+ // Multi-position dodging: carry the ordered interior positions through to the
51258
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51259
+ ...c.positions ? { positions: c.positions } : {},
50651
51260
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50652
51261
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50653
51262
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50662,39 +51271,53 @@ function placeContextLabels(args) {
50662
51271
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50663
51272
  const waterCap = budget - Math.min(2, countryCount);
50664
51273
  let waterPlaced = 0;
50665
- for (const cand of candidates) {
50666
- if (placed.length >= budget) break;
50667
- if (cand.italic && waterPlaced >= waterCap) continue;
51274
+ const gateAt = (cx, cy, cand) => {
50668
51275
  const rect = rectAround(
50669
- cand.cx,
50670
- cand.cy,
51276
+ cx,
51277
+ cy,
50671
51278
  cand.lines,
50672
51279
  cand.letterSpacing,
50673
51280
  cand.fontSize
50674
51281
  );
50675
- if (!rectFits(rect, width, height)) continue;
51282
+ if (!rectFits(rect, width, height)) return null;
50676
51283
  if (cand.italic && overLand) {
50677
51284
  const inset = 2;
50678
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51285
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50679
51286
  const touchesLand = cand.lines.some((line11, li) => {
50680
51287
  const lw = labelWidth(line11, cand.letterSpacing);
50681
- const x0 = cand.cx - lw / 2 + inset;
50682
- const x1 = cand.cx + lw / 2 - inset;
50683
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51288
+ const x0 = cx - lw / 2 + inset;
51289
+ const x1 = cx + lw / 2 - inset;
51290
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
50684
51291
  const base = top + li * LINE_HEIGHT;
50685
51292
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50686
51293
  (y) => xs.some((x) => overLand(x, y))
50687
51294
  );
50688
51295
  });
50689
- if (touchesLand) continue;
51296
+ if (touchesLand) return null;
50690
51297
  }
50691
- if (collides(rect)) continue;
50692
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50693
- placedRects.push(rect);
51298
+ if (collides(rect)) return null;
51299
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51300
+ return null;
51301
+ return rect;
51302
+ };
51303
+ for (const cand of candidates) {
51304
+ if (placed.length >= budget) break;
51305
+ if (cand.italic && waterPlaced >= waterCap) continue;
51306
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51307
+ let chosen = null;
51308
+ for (const [px, py] of positions) {
51309
+ const rect = gateAt(px, py, cand);
51310
+ if (rect) {
51311
+ chosen = { x: px, y: py, rect };
51312
+ break;
51313
+ }
51314
+ }
51315
+ if (!chosen) continue;
51316
+ placedRects.push(chosen.rect);
50694
51317
  if (cand.italic) waterPlaced++;
50695
51318
  placed.push({
50696
- x: cand.cx,
50697
- y: cand.cy,
51319
+ x: chosen.x,
51320
+ y: chosen.y,
50698
51321
  text: cand.text,
50699
51322
  anchor: "middle",
50700
51323
  color: cand.color,
@@ -50712,7 +51335,7 @@ function placeContextLabels(args) {
50712
51335
  }
50713
51336
  return placed;
50714
51337
  }
50715
- 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;
51338
+ 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;
50716
51339
  var init_context_labels = __esm({
50717
51340
  "src/map/context-labels.ts"() {
50718
51341
  "use strict";
@@ -50730,6 +51353,9 @@ var init_context_labels = __esm({
50730
51353
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50731
51354
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50732
51355
  COUNTRY_FADE_MAX = 45;
51356
+ MAX_COUNTRY_POSITIONS = 4;
51357
+ COUNTRY_POS_GRID = 5;
51358
+ COUNTRY_POS_TOPN_MARGIN = 3;
50733
51359
  KIND_ORDER = {
50734
51360
  ocean: 0,
50735
51361
  sea: 1,
@@ -50823,6 +51449,84 @@ function decodeLayer(topo) {
50823
51449
  decodeCache.set(topo, out);
50824
51450
  return out;
50825
51451
  }
51452
+ function countryLabelPositions(args) {
51453
+ const { geometry, bounds, project, width, height, curated } = args;
51454
+ const w0 = bounds[0][0];
51455
+ const s0 = bounds[0][1];
51456
+ const e0 = bounds[1][0];
51457
+ const n0 = bounds[1][1];
51458
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51459
+ return mkCurated(curated, project);
51460
+ }
51461
+ const N = COUNTRY_POS_GRID;
51462
+ const onLand = [];
51463
+ const kept = [];
51464
+ for (let i = 0; i < N; i++) {
51465
+ onLand[i] = [];
51466
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51467
+ for (let j = 0; j < N; j++) {
51468
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51469
+ const land = pointInGeometry(geometry, lon, lat);
51470
+ onLand[i][j] = land;
51471
+ if (!land) continue;
51472
+ const p = project(lon, lat);
51473
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51474
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51475
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51476
+ }
51477
+ }
51478
+ if (!kept.length) return mkCurated(curated, project);
51479
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51480
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51481
+ const interiorness = (c) => {
51482
+ let n = 0;
51483
+ for (let di = -1; di <= 1; di++)
51484
+ for (let dj = -1; dj <= 1; dj++) {
51485
+ if (di === 0 && dj === 0) continue;
51486
+ const ni = c.i + di;
51487
+ const nj = c.j + dj;
51488
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51489
+ }
51490
+ return n;
51491
+ };
51492
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51493
+ const pool = [...kept];
51494
+ pool.sort((a, b) => {
51495
+ const d = interiorness(b) - interiorness(a);
51496
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51497
+ });
51498
+ const ordered = [pool.shift()];
51499
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51500
+ let bestIdx = 0;
51501
+ let bestMin = -1;
51502
+ for (let k = 0; k < pool.length; k++) {
51503
+ const c = pool[k];
51504
+ let minD = Infinity;
51505
+ for (const o of ordered) {
51506
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51507
+ if (d < minD) minD = d;
51508
+ }
51509
+ if (minD > bestMin) {
51510
+ bestMin = minD;
51511
+ bestIdx = k;
51512
+ }
51513
+ }
51514
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51515
+ }
51516
+ const grid = ordered.map((c) => ({
51517
+ lonLat: [c.lon, c.lat],
51518
+ screen: [c.sx, c.sy]
51519
+ }));
51520
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51521
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51522
+ return out;
51523
+ }
51524
+ function mkCurated(curated, project) {
51525
+ if (!curated) return [];
51526
+ const p = project(curated[0], curated[1]);
51527
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51528
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51529
+ }
50826
51530
  function projectionFor(family, extent3) {
50827
51531
  switch (family) {
50828
51532
  case "albers-usa":
@@ -51650,6 +52354,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51650
52354
  if (p[1] < minY) minY = p[1];
51651
52355
  if (p[1] > maxY) maxY = p[1];
51652
52356
  }
52357
+ r.bbox = [minX, minY, maxX, maxY];
52358
+ r.rings = rings;
51653
52359
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51654
52360
  });
51655
52361
  const fillAt = (x, y) => {
@@ -52840,6 +53546,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52840
53546
  for (const box of insets)
52841
53547
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52842
53548
  const countryCandidates = [];
53549
+ const rawCountries = [];
52843
53550
  for (const f of worldLayer.values()) {
52844
53551
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52845
53552
  if (!iso || regionById.has(iso)) continue;
@@ -52856,11 +53563,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52856
53563
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52857
53564
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52858
53565
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52859
- countryCandidates.push({
53566
+ rawCountries.push({
53567
+ f,
53568
+ iso,
52860
53569
  name: f.properties?.name ?? iso,
52861
53570
  bbox: [x0, y0, x1, y1],
52862
53571
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52863
- curatedAnchor: !!anchorLngLat
53572
+ curatedLngLat: anchorLngLat ?? null,
53573
+ area: (x1 - x0) * (y1 - y0)
53574
+ });
53575
+ }
53576
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53577
+ const cBudget = labelBudget(width, height, cBand);
53578
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53579
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53580
+ const rankOrder = rawCountries.map((r, idx) => {
53581
+ let dist = Infinity;
53582
+ const [x0, y0, x1, y1] = r.bbox;
53583
+ for (const [px, py] of contentPoints) {
53584
+ const dx = Math.max(x0 - px, 0, px - x1);
53585
+ const dy = Math.max(y0 - py, 0, py - y1);
53586
+ const d = dx * dx + dy * dy;
53587
+ if (d < dist) dist = d;
53588
+ }
53589
+ return { idx, area: r.area, dist };
53590
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53591
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53592
+ ).slice(0, topN);
53593
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53594
+ for (let i = 0; i < rawCountries.length; i++) {
53595
+ const r = rawCountries[i];
53596
+ let anchor = r.anchor;
53597
+ let positions;
53598
+ if (genIdx.has(i) && anchor) {
53599
+ const gb = geoBounds2(r.f);
53600
+ const gen = countryLabelPositions({
53601
+ geometry: r.f.geometry,
53602
+ bounds: gb,
53603
+ project,
53604
+ width,
53605
+ height,
53606
+ curated: r.curatedLngLat
53607
+ });
53608
+ if (gen.length) {
53609
+ positions = gen.map((p) => p.screen);
53610
+ anchor = positions[0];
53611
+ }
53612
+ }
53613
+ countryCandidates.push({
53614
+ name: r.name,
53615
+ bbox: r.bbox,
53616
+ anchor,
53617
+ curatedAnchor: !!r.curatedLngLat,
53618
+ ...positions ? { positions } : {}
52864
53619
  });
52865
53620
  }
52866
53621
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52894,6 +53649,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52894
53649
  palette,
52895
53650
  project,
52896
53651
  collides,
53652
+ contentPoints,
52897
53653
  // Water labels must stay over open water — `fillAt` returns the ocean
52898
53654
  // backdrop colour off-land and a region fill on-land (lakes/states count
52899
53655
  // as land here, which is the safe side for an ocean name).
@@ -53146,7 +53902,7 @@ function ringToCoastPaths(ring, frame) {
53146
53902
  function coastlineOuterRings(regions, minExtent, frame) {
53147
53903
  const paths = [];
53148
53904
  for (const r of regions) {
53149
- const rings = parsePathRings(r.d);
53905
+ const rings = r.rings ?? parsePathRings(r.d);
53150
53906
  for (let i = 0; i < rings.length; i++) {
53151
53907
  const ring = rings[i];
53152
53908
  if (ring.length < 3) continue;
@@ -53341,14 +54097,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53341
54097
  if (decoCluster !== void 0)
53342
54098
  gPatch.attr("data-cluster-deco", decoCluster);
53343
54099
  for (const r of layout.regions) {
53344
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53345
- for (const ring of parsePathRings(r.d))
53346
- for (const [px, py] of ring) {
53347
- if (px < minX) minX = px;
53348
- if (px > maxX) maxX = px;
53349
- if (py < minY) minY = py;
53350
- if (py > maxY) maxY = py;
53351
- }
54100
+ let minX, minY, maxX, maxY;
54101
+ if (r.bbox) {
54102
+ [minX, minY, maxX, maxY] = r.bbox;
54103
+ } else {
54104
+ minX = Infinity;
54105
+ minY = Infinity;
54106
+ maxX = -Infinity;
54107
+ maxY = -Infinity;
54108
+ for (const ring of parsePathRings(r.d))
54109
+ for (const [px, py] of ring) {
54110
+ if (px < minX) minX = px;
54111
+ if (px > maxX) maxX = px;
54112
+ if (py < minY) minY = py;
54113
+ if (py > maxY) maxY = py;
54114
+ }
54115
+ }
53352
54116
  const hit = blobRects.some(
53353
54117
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53354
54118
  );
@@ -54575,7 +55339,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54575
55339
  const tasksAll = [...allTasks(parsed)];
54576
55340
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54577
55341
  const solid = parsed.options["solid-fill"] === "on";
54578
- const surfaceBg = isDark ? palette.surface : palette.bg;
55342
+ const surfaceBg = themeBaseBg(palette, isDark);
54579
55343
  const roleCount = Math.max(1, parsed.roles.length);
54580
55344
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54581
55345
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54753,10 +55517,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54753
55517
  const cx = roleX(i) + sColumnInset;
54754
55518
  const cw = roleColW - 2 * sColumnInset;
54755
55519
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54756
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55520
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54757
55521
  const headerFill = mix(
54758
55522
  roleColor,
54759
- isDark ? palette.surface : palette.bg,
55523
+ themeBaseBg(palette, isDark),
54760
55524
  30
54761
55525
  );
54762
55526
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -56483,7 +57247,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56483
57247
  const groupTagColor = getTagColor(groupTagValue || void 0);
56484
57248
  const fillColor = groupTagColor ? mix(
56485
57249
  groupTagColor,
56486
- isDark ? palette.surface : palette.bg,
57250
+ themeBaseBg(palette, isDark),
56487
57251
  isDark ? 15 : 20
56488
57252
  ) : isDark ? palette.surface : palette.bg;
56489
57253
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56537,7 +57301,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56537
57301
  participantG.append("title").text("Click to expand");
56538
57302
  const pFill = effectiveTagColor ? mix(
56539
57303
  effectiveTagColor,
56540
- isDark ? palette.surface : palette.bg,
57304
+ themeBaseBg(palette, isDark),
56541
57305
  isDark ? 30 : 40
56542
57306
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56543
57307
  const pStroke = effectiveTagColor || palette.border;
@@ -56744,7 +57508,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56744
57508
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56745
57509
  const actTagColor = getTagColor(actTagValue);
56746
57510
  const actBaseColor = actTagColor || palette.primary;
56747
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57511
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
56748
57512
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56749
57513
  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");
56750
57514
  if (tagKey && actTagValue) {
@@ -58445,7 +59209,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58445
59209
  const textColor = palette.text;
58446
59210
  const mutedColor = palette.border;
58447
59211
  const bgColor = palette.bg;
58448
- const bg = isDark ? palette.surface : palette.bg;
59212
+ const bg = themeBaseBg(palette, isDark);
58449
59213
  const colors = getSeriesColors(palette);
58450
59214
  const groupColorMap = /* @__PURE__ */ new Map();
58451
59215
  timelineGroups.forEach((grp, i) => {
@@ -58860,9 +59624,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58860
59624
  drawLegend2();
58861
59625
  }
58862
59626
  }
58863
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59627
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58864
59628
  const {
58865
59629
  width,
59630
+ height,
58866
59631
  tooltip,
58867
59632
  solid,
58868
59633
  textColor,
@@ -58914,9 +59679,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58914
59679
  const rowH = ctx.structural(28);
58915
59680
  const innerHeight = rowH * sorted.length;
58916
59681
  const usedHeight = margin.top + innerHeight + margin.bottom;
59682
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59683
+ const svgHeight = fitToContainer ? height : usedHeight;
58917
59684
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58918
- 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);
58919
- if (ctx.isBelowFloor) {
59685
+ 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);
59686
+ if (ctx.isBelowFloor && !fitToContainer) {
58920
59687
  svg.attr("width", "100%");
58921
59688
  }
58922
59689
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60590,7 +61357,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60590
61357
  onClickItem
60591
61358
  );
60592
61359
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60593
- const bg = isDark ? palette.surface : palette.bg;
61360
+ const bg = themeBaseBg(palette, isDark);
60594
61361
  const getQuadrantColor = (label, defaultIdx) => {
60595
61362
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60596
61363
  };
@@ -60927,18 +61694,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60927
61694
  palette,
60928
61695
  viewState,
60929
61696
  options,
60930
- exportMode
61697
+ exportMode,
61698
+ isDark: theme === "dark"
60931
61699
  };
60932
61700
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
60933
61701
  return (handler ?? exportVisualization)(ctx);
60934
61702
  }
61703
+ function ctxTagOverride(ctx) {
61704
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61705
+ }
60935
61706
  async function exportOrg(ctx) {
60936
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61707
+ const { content, theme, palette, viewState, exportMode } = ctx;
60937
61708
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
60938
61709
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
60939
61710
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
60940
61711
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
60941
- const isDark = theme === "dark";
61712
+ const isDark = ctx.isDark;
60942
61713
  const effectivePalette = await resolveExportPalette(theme, palette);
60943
61714
  const orgParsed = parseOrg2(content, effectivePalette);
60944
61715
  if (orgParsed.error) return "";
@@ -60946,7 +61717,7 @@ async function exportOrg(ctx) {
60946
61717
  const activeTagGroup = resolveActiveTagGroup(
60947
61718
  orgParsed.tagGroups,
60948
61719
  orgParsed.options["active-tag"],
60949
- viewState?.tag ?? options?.tagGroup
61720
+ ctxTagOverride(ctx)
60950
61721
  );
60951
61722
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60952
61723
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60979,12 +61750,12 @@ async function exportOrg(ctx) {
60979
61750
  return finalizeSvgExport(container, theme, effectivePalette);
60980
61751
  }
60981
61752
  async function exportSitemap(ctx) {
60982
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61753
+ const { content, theme, palette, viewState, exportMode } = ctx;
60983
61754
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
60984
61755
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
60985
61756
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
60986
61757
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
60987
- const isDark = theme === "dark";
61758
+ const isDark = ctx.isDark;
60988
61759
  const effectivePalette = await resolveExportPalette(theme, palette);
60989
61760
  const sitemapParsed = parseSitemap2(content, effectivePalette);
60990
61761
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -60992,7 +61763,7 @@ async function exportSitemap(ctx) {
60992
61763
  const activeTagGroup = resolveActiveTagGroup(
60993
61764
  sitemapParsed.tagGroups,
60994
61765
  sitemapParsed.options["active-tag"],
60995
- viewState?.tag ?? options?.tagGroup
61766
+ ctxTagOverride(ctx)
60996
61767
  );
60997
61768
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60998
61769
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61023,7 +61794,7 @@ async function exportSitemap(ctx) {
61023
61794
  return finalizeSvgExport(container, theme, effectivePalette);
61024
61795
  }
61025
61796
  async function exportKanban(ctx) {
61026
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61797
+ const { content, theme, palette, viewState, exportMode } = ctx;
61027
61798
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
61028
61799
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
61029
61800
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61035,11 +61806,11 @@ async function exportKanban(ctx) {
61035
61806
  document.body.appendChild(container);
61036
61807
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
61037
61808
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
61038
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61809
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
61039
61810
  activeTagGroup: resolveActiveTagGroup(
61040
61811
  kanbanParsed.tagGroups,
61041
61812
  kanbanParsed.options["active-tag"],
61042
- viewState?.tag ?? options?.tagGroup
61813
+ ctxTagOverride(ctx)
61043
61814
  ),
61044
61815
  currentSwimlaneGroup: viewState?.swim ?? null,
61045
61816
  ...kanbanCollapsedLanes !== void 0 && {
@@ -61072,7 +61843,7 @@ async function exportClass(ctx) {
61072
61843
  classParsed,
61073
61844
  classLayout,
61074
61845
  effectivePalette,
61075
- theme === "dark",
61846
+ ctx.isDark,
61076
61847
  void 0,
61077
61848
  { width: exportWidth, height: exportHeight },
61078
61849
  void 0,
@@ -61081,7 +61852,7 @@ async function exportClass(ctx) {
61081
61852
  return finalizeSvgExport(container, theme, effectivePalette);
61082
61853
  }
61083
61854
  async function exportEr(ctx) {
61084
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61855
+ const { content, theme, palette, viewState, exportMode } = ctx;
61085
61856
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
61086
61857
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
61087
61858
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -61099,13 +61870,13 @@ async function exportEr(ctx) {
61099
61870
  erParsed,
61100
61871
  erLayout,
61101
61872
  effectivePalette,
61102
- theme === "dark",
61873
+ ctx.isDark,
61103
61874
  void 0,
61104
61875
  { width: exportWidth, height: exportHeight },
61105
61876
  resolveActiveTagGroup(
61106
61877
  erParsed.tagGroups,
61107
61878
  erParsed.options["active-tag"],
61108
- viewState?.tag ?? options?.tagGroup
61879
+ ctxTagOverride(ctx)
61109
61880
  ),
61110
61881
  viewState?.sem,
61111
61882
  exportMode
@@ -61113,7 +61884,7 @@ async function exportEr(ctx) {
61113
61884
  return finalizeSvgExport(container, theme, effectivePalette);
61114
61885
  }
61115
61886
  async function exportBoxesAndLines(ctx) {
61116
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61887
+ const { content, theme, palette, viewState, exportMode } = ctx;
61117
61888
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
61118
61889
  const effectivePalette = await resolveExportPalette(theme, palette);
61119
61890
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -61133,13 +61904,13 @@ async function exportBoxesAndLines(ctx) {
61133
61904
  const exportWidth = blLayout.width + PADDING3 * 2;
61134
61905
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
61135
61906
  const container = createExportContainer(exportWidth, exportHeight);
61136
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61907
+ const blActiveTagGroup = ctxTagOverride(ctx);
61137
61908
  renderBoxesAndLinesForExport2(
61138
61909
  container,
61139
61910
  blParsed,
61140
61911
  blLayout,
61141
61912
  effectivePalette,
61142
- theme === "dark",
61913
+ ctx.isDark,
61143
61914
  {
61144
61915
  exportDims: { width: exportWidth, height: exportHeight },
61145
61916
  ...blActiveTagGroup !== void 0 && {
@@ -61154,12 +61925,12 @@ async function exportBoxesAndLines(ctx) {
61154
61925
  return finalizeSvgExport(container, theme, effectivePalette);
61155
61926
  }
61156
61927
  async function exportMindmap(ctx) {
61157
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61928
+ const { content, theme, palette, viewState, exportMode } = ctx;
61158
61929
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
61159
61930
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
61160
61931
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
61161
61932
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
61162
- const isDark = theme === "dark";
61933
+ const isDark = ctx.isDark;
61163
61934
  const effectivePalette = await resolveExportPalette(theme, palette);
61164
61935
  const mmParsed = parseMindmap2(content, effectivePalette);
61165
61936
  if (mmParsed.error) return "";
@@ -61167,7 +61938,7 @@ async function exportMindmap(ctx) {
61167
61938
  const activeTagGroup = resolveActiveTagGroup(
61168
61939
  mmParsed.tagGroups,
61169
61940
  mmParsed.options["active-tag"],
61170
- viewState?.tag ?? options?.tagGroup
61941
+ ctxTagOverride(ctx)
61171
61942
  );
61172
61943
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
61173
61944
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61217,7 +61988,7 @@ async function exportWireframe(ctx) {
61217
61988
  wireframeParsed,
61218
61989
  wireframeLayout,
61219
61990
  effectivePalette,
61220
- theme === "dark",
61991
+ ctx.isDark,
61221
61992
  void 0,
61222
61993
  { width: exportWidth, height: exportHeight },
61223
61994
  theme
@@ -61225,7 +61996,7 @@ async function exportWireframe(ctx) {
61225
61996
  return finalizeSvgExport(container, theme, effectivePalette);
61226
61997
  }
61227
61998
  async function exportC4(ctx) {
61228
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61999
+ const { content, theme, palette, viewState, exportMode } = ctx;
61229
62000
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61230
62001
  const {
61231
62002
  layoutC4Context: layoutC4Context2,
@@ -61237,9 +62008,9 @@ async function exportC4(ctx) {
61237
62008
  const effectivePalette = await resolveExportPalette(theme, palette);
61238
62009
  const c4Parsed = parseC42(content, effectivePalette);
61239
62010
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61240
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61241
- const c4System = options?.c4System ?? viewState?.c4s;
61242
- const c4Container = options?.c4Container ?? viewState?.c4c;
62011
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62012
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62013
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61243
62014
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61244
62015
  if (c4Layout.nodes.length === 0) return "";
61245
62016
  const PADDING3 = 20;
@@ -61253,13 +62024,13 @@ async function exportC4(ctx) {
61253
62024
  c4Parsed,
61254
62025
  c4Layout,
61255
62026
  effectivePalette,
61256
- theme === "dark",
62027
+ ctx.isDark,
61257
62028
  void 0,
61258
62029
  { width: exportWidth, height: exportHeight },
61259
62030
  resolveActiveTagGroup(
61260
62031
  c4Parsed.tagGroups,
61261
62032
  c4Parsed.options["active-tag"],
61262
- viewState?.tag ?? options?.tagGroup
62033
+ ctxTagOverride(ctx)
61263
62034
  ),
61264
62035
  exportMode
61265
62036
  );
@@ -61280,14 +62051,14 @@ async function exportFlowchart(ctx) {
61280
62051
  fcParsed,
61281
62052
  layout,
61282
62053
  effectivePalette,
61283
- theme === "dark",
62054
+ ctx.isDark,
61284
62055
  void 0,
61285
62056
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61286
62057
  );
61287
62058
  return finalizeSvgExport(container, theme, effectivePalette);
61288
62059
  }
61289
62060
  async function exportInfra(ctx) {
61290
- const { content, theme, palette, viewState, options } = ctx;
62061
+ const { content, theme, palette, viewState } = ctx;
61291
62062
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61292
62063
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61293
62064
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61300,7 +62071,7 @@ async function exportInfra(ctx) {
61300
62071
  const activeTagGroup = resolveActiveTagGroup(
61301
62072
  infraParsed.tagGroups,
61302
62073
  infraParsed.options["active-tag"],
61303
- viewState?.tag ?? options?.tagGroup
62074
+ ctxTagOverride(ctx)
61304
62075
  );
61305
62076
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61306
62077
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61318,7 +62089,7 @@ async function exportInfra(ctx) {
61318
62089
  container,
61319
62090
  infraLayout,
61320
62091
  effectivePalette,
61321
- theme === "dark",
62092
+ ctx.isDark,
61322
62093
  showInfraTitle ? infraParsed.title : null,
61323
62094
  showInfraTitle ? infraParsed.titleLineNumber : null,
61324
62095
  infraTagGroups,
@@ -61365,7 +62136,7 @@ async function exportPert(ctx) {
61365
62136
  pertResolved,
61366
62137
  pertLayout,
61367
62138
  effectivePalette,
61368
- theme === "dark",
62139
+ ctx.isDark,
61369
62140
  {
61370
62141
  title: pertParsed.title,
61371
62142
  exportDims: { width: exportW, height: exportH },
@@ -61378,7 +62149,7 @@ async function exportPert(ctx) {
61378
62149
  return finalizeSvgExport(container, theme, effectivePalette);
61379
62150
  }
61380
62151
  async function exportGantt(ctx) {
61381
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62152
+ const { content, theme, palette, viewState, exportMode } = ctx;
61382
62153
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61383
62154
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61384
62155
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61396,7 +62167,7 @@ async function exportGantt(ctx) {
61396
62167
  container,
61397
62168
  resolved,
61398
62169
  effectivePalette,
61399
- theme === "dark",
62170
+ ctx.isDark,
61400
62171
  {
61401
62172
  ...ganttCollapsedGroups !== void 0 && {
61402
62173
  collapsedGroups: ganttCollapsedGroups
@@ -61410,7 +62181,7 @@ async function exportGantt(ctx) {
61410
62181
  currentActiveGroup: resolveActiveTagGroup(
61411
62182
  resolved.tagGroups,
61412
62183
  resolved.options.activeTag ?? void 0,
61413
- viewState?.tag ?? options?.tagGroup
62184
+ ctxTagOverride(ctx)
61414
62185
  ),
61415
62186
  exportMode
61416
62187
  },
@@ -61433,7 +62204,7 @@ async function exportState(ctx) {
61433
62204
  stateParsed,
61434
62205
  layout,
61435
62206
  effectivePalette,
61436
- theme === "dark",
62207
+ ctx.isDark,
61437
62208
  void 0,
61438
62209
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61439
62210
  );
@@ -61453,7 +62224,7 @@ async function exportTechRadar(ctx) {
61453
62224
  container,
61454
62225
  radarParsed,
61455
62226
  effectivePalette,
61456
- theme === "dark",
62227
+ ctx.isDark,
61457
62228
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61458
62229
  viewState,
61459
62230
  exportMode
@@ -61470,13 +62241,13 @@ async function exportJourneyMap(ctx) {
61470
62241
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61471
62242
  return "";
61472
62243
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61473
- isDark: theme === "dark"
62244
+ isDark: ctx.isDark
61474
62245
  });
61475
62246
  const container = createExportContainer(
61476
62247
  jmLayout.totalWidth,
61477
62248
  jmLayout.totalHeight
61478
62249
  );
61479
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62250
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61480
62251
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61481
62252
  exportMode
61482
62253
  });
@@ -61494,7 +62265,7 @@ async function exportCycle(ctx) {
61494
62265
  container,
61495
62266
  cycleParsed,
61496
62267
  effectivePalette,
61497
- theme === "dark",
62268
+ ctx.isDark,
61498
62269
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61499
62270
  viewState,
61500
62271
  exportMode
@@ -61531,7 +62302,7 @@ async function exportMap(ctx) {
61531
62302
  mapResolved,
61532
62303
  mapData,
61533
62304
  effectivePalette,
61534
- theme === "dark",
62305
+ ctx.isDark,
61535
62306
  dims
61536
62307
  );
61537
62308
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61548,7 +62319,7 @@ async function exportPyramid(ctx) {
61548
62319
  container,
61549
62320
  pyramidParsed,
61550
62321
  effectivePalette,
61551
- theme === "dark",
62322
+ ctx.isDark,
61552
62323
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61553
62324
  );
61554
62325
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61565,7 +62336,7 @@ async function exportRing(ctx) {
61565
62336
  container,
61566
62337
  ringParsed,
61567
62338
  effectivePalette,
61568
- theme === "dark",
62339
+ ctx.isDark,
61569
62340
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61570
62341
  );
61571
62342
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61582,7 +62353,7 @@ async function exportRaci(ctx) {
61582
62353
  container,
61583
62354
  raciParsed,
61584
62355
  effectivePalette,
61585
- theme === "dark",
62356
+ ctx.isDark,
61586
62357
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61587
62358
  );
61588
62359
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61605,7 +62376,7 @@ async function exportSlope(ctx) {
61605
62376
  container,
61606
62377
  parsed,
61607
62378
  effectivePalette,
61608
- theme === "dark",
62379
+ ctx.isDark,
61609
62380
  void 0,
61610
62381
  dims
61611
62382
  );
@@ -61621,14 +62392,14 @@ async function exportArc(ctx) {
61621
62392
  container,
61622
62393
  parsed,
61623
62394
  effectivePalette,
61624
- theme === "dark",
62395
+ ctx.isDark,
61625
62396
  void 0,
61626
62397
  dims
61627
62398
  );
61628
62399
  return finalizeSvgExport(container, theme, effectivePalette);
61629
62400
  }
61630
62401
  async function exportTimeline(ctx) {
61631
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62402
+ const { content, theme, palette, viewState, exportMode } = ctx;
61632
62403
  const parsed = parseTimeline(content, palette);
61633
62404
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61634
62405
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61637,13 +62408,13 @@ async function exportTimeline(ctx) {
61637
62408
  container,
61638
62409
  parsed,
61639
62410
  effectivePalette,
61640
- theme === "dark",
62411
+ ctx.isDark,
61641
62412
  void 0,
61642
62413
  dims,
61643
62414
  resolveActiveTagGroup(
61644
62415
  parsed.timelineTagGroups,
61645
62416
  parsed.timelineActiveTag,
61646
- viewState?.tag ?? options?.tagGroup
62417
+ ctxTagOverride(ctx)
61647
62418
  ),
61648
62419
  viewState?.swim,
61649
62420
  void 0,
@@ -61662,7 +62433,7 @@ async function exportWordcloud(ctx) {
61662
62433
  container,
61663
62434
  parsed,
61664
62435
  effectivePalette,
61665
- theme === "dark",
62436
+ ctx.isDark,
61666
62437
  dims
61667
62438
  );
61668
62439
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61677,7 +62448,7 @@ async function exportVenn(ctx) {
61677
62448
  container,
61678
62449
  parsed,
61679
62450
  effectivePalette,
61680
- theme === "dark",
62451
+ ctx.isDark,
61681
62452
  void 0,
61682
62453
  dims
61683
62454
  );
@@ -61693,14 +62464,14 @@ async function exportQuadrant(ctx) {
61693
62464
  container,
61694
62465
  parsed,
61695
62466
  effectivePalette,
61696
- theme === "dark",
62467
+ ctx.isDark,
61697
62468
  void 0,
61698
62469
  dims
61699
62470
  );
61700
62471
  return finalizeSvgExport(container, theme, effectivePalette);
61701
62472
  }
61702
62473
  async function exportVisualization(ctx) {
61703
- const { content, theme, palette, viewState, options } = ctx;
62474
+ const { content, theme, palette, viewState } = ctx;
61704
62475
  const parsed = parseVisualization(content, palette);
61705
62476
  if (parsed.type !== "sequence") {
61706
62477
  if (parsed.error) {
@@ -61711,7 +62482,7 @@ async function exportVisualization(ctx) {
61711
62482
  }
61712
62483
  }
61713
62484
  const effectivePalette = await resolveExportPalette(theme, palette);
61714
- const isDark = theme === "dark";
62485
+ const isDark = ctx.isDark;
61715
62486
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61716
62487
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61717
62488
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61719,7 +62490,7 @@ async function exportVisualization(ctx) {
61719
62490
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61720
62491
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61721
62492
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61722
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62493
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61723
62494
  renderSequenceDiagram2(
61724
62495
  container,
61725
62496
  seqParsed,
@@ -62493,7 +63264,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
62493
63264
 
62494
63265
  // src/auto/index.ts
62495
63266
  init_safe_href();
62496
- var VERSION = "0.31.0";
63267
+ var VERSION = "0.32.0";
62497
63268
  var DEFAULTS = {
62498
63269
  theme: "auto",
62499
63270
  palette: "slate",