@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.cjs CHANGED
@@ -40,6 +40,14 @@ function makeDgmoError(line11, message, severity = "error", code) {
40
40
  function formatDgmoError(err) {
41
41
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
42
42
  }
43
+ function makeFail(result) {
44
+ return (line11, message) => {
45
+ const diag = makeDgmoError(line11, message);
46
+ result.diagnostics.push(diag);
47
+ result.error = formatDgmoError(diag);
48
+ return result;
49
+ };
50
+ }
43
51
  function levenshtein(a, b) {
44
52
  const m = a.length;
45
53
  const n = b.length;
@@ -306,21 +314,99 @@ function resolveColor(color, palette) {
306
314
  }
307
315
  return colorNames[lower] ?? null;
308
316
  }
317
+ function nearestNamedColor(input) {
318
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
319
+ if (cssHex) input = cssHex;
320
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
321
+ if (!m) return null;
322
+ let h = m[1].toLowerCase();
323
+ if (h.length === 3)
324
+ h = h.split("").map((c) => c + c).join("");
325
+ const r = parseInt(h.slice(0, 2), 16) / 255;
326
+ const g = parseInt(h.slice(2, 4), 16) / 255;
327
+ const b = parseInt(h.slice(4, 6), 16) / 255;
328
+ const max = Math.max(r, g, b);
329
+ const min = Math.min(r, g, b);
330
+ const delta = max - min;
331
+ const l = (max + min) / 2;
332
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
333
+ if (s < 0.15) {
334
+ if (l < 0.2) return "black";
335
+ if (l > 0.85) return "white";
336
+ return "gray";
337
+ }
338
+ let hue;
339
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
340
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
341
+ else hue = 60 * ((r - g) / delta + 4);
342
+ if (hue < 0) hue += 360;
343
+ const anchors = [
344
+ ["red", 0],
345
+ ["orange", 30],
346
+ ["yellow", 55],
347
+ ["green", 120],
348
+ ["teal", 170],
349
+ ["cyan", 190],
350
+ ["blue", 225],
351
+ ["purple", 285],
352
+ ["red", 360]
353
+ ];
354
+ let best = "red";
355
+ let bestD = Infinity;
356
+ for (const [name, deg] of anchors) {
357
+ const d = Math.abs(hue - deg);
358
+ if (d < bestD) {
359
+ bestD = d;
360
+ best = name;
361
+ }
362
+ }
363
+ return best;
364
+ }
365
+ function isInvalidColorToken(token) {
366
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
367
+ const lower = token.toLowerCase();
368
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
369
+ }
370
+ function invalidColorDiagnostic(token, line11) {
371
+ if (!isInvalidColorToken(token)) return null;
372
+ const nearest = nearestNamedColor(token);
373
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
374
+ return makeDgmoError(
375
+ line11,
376
+ `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}`,
377
+ "warning",
378
+ INVALID_COLOR_CODE
379
+ );
380
+ }
309
381
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
310
382
  const resolved = resolveColor(color, palette);
311
383
  if (resolved !== null) return resolved;
384
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
385
+ const nearest = nearestNamedColor(color);
386
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
387
+ diagnostics.push(
388
+ makeDgmoError(
389
+ line11,
390
+ `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}`,
391
+ "error",
392
+ INVALID_COLOR_CODE
393
+ )
394
+ );
395
+ return void 0;
396
+ }
312
397
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
313
398
  const suggestion = hint ? ` ${hint}` : "";
314
399
  diagnostics.push(
315
400
  makeDgmoError(
316
401
  line11,
317
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
318
- "warning"
402
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
403
+ "warning",
404
+ INVALID_COLOR_CODE
319
405
  )
320
406
  );
321
407
  return void 0;
322
408
  }
323
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, seriesColors;
409
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
324
410
  var init_colors = __esm({
325
411
  "src/colors.ts"() {
326
412
  "use strict";
@@ -388,6 +474,92 @@ var init_colors = __esm({
388
474
  "orange",
389
475
  "cyan"
390
476
  ]);
477
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
478
+ INVALID_CSS_COLOR_HEX = Object.freeze({
479
+ pink: "#ffc0cb",
480
+ hotpink: "#ff69b4",
481
+ deeppink: "#ff1493",
482
+ lightpink: "#ffb6c1",
483
+ palevioletred: "#db7093",
484
+ crimson: "#dc143c",
485
+ scarlet: "#ff2400",
486
+ firebrick: "#b22222",
487
+ darkred: "#8b0000",
488
+ maroon: "#800000",
489
+ salmon: "#fa8072",
490
+ lightsalmon: "#ffa07a",
491
+ darksalmon: "#e9967a",
492
+ coral: "#ff7f50",
493
+ lightcoral: "#f08080",
494
+ tomato: "#ff6347",
495
+ orangered: "#ff4500",
496
+ darkorange: "#ff8c00",
497
+ gold: "#ffd700",
498
+ goldenrod: "#daa520",
499
+ darkgoldenrod: "#b8860b",
500
+ khaki: "#f0e68c",
501
+ darkkhaki: "#bdb76b",
502
+ amber: "#ffbf00",
503
+ lavender: "#e6e6fa",
504
+ violet: "#ee82ee",
505
+ magenta: "#ff00ff",
506
+ fuchsia: "#ff00ff",
507
+ orchid: "#da70d6",
508
+ plum: "#dda0dd",
509
+ indigo: "#4b0082",
510
+ navy: "#000080",
511
+ midnightblue: "#191970",
512
+ darkblue: "#00008b",
513
+ mediumblue: "#0000cd",
514
+ royalblue: "#4169e1",
515
+ cornflowerblue: "#6495ed",
516
+ dodgerblue: "#1e90ff",
517
+ deepskyblue: "#00bfff",
518
+ skyblue: "#87ceeb",
519
+ lightskyblue: "#87cefa",
520
+ lightblue: "#add8e6",
521
+ powderblue: "#b0e0e6",
522
+ steelblue: "#4682b4",
523
+ slateblue: "#6a5acd",
524
+ cadetblue: "#5f9ea0",
525
+ turquoise: "#40e0d0",
526
+ aqua: "#00ffff",
527
+ aquamarine: "#7fffd4",
528
+ lime: "#00ff00",
529
+ limegreen: "#32cd32",
530
+ lightgreen: "#90ee90",
531
+ palegreen: "#98fb98",
532
+ seagreen: "#2e8b57",
533
+ mediumseagreen: "#3cb371",
534
+ forestgreen: "#228b22",
535
+ darkgreen: "#006400",
536
+ olive: "#808000",
537
+ olivedrab: "#6b8e23",
538
+ darkolivegreen: "#556b2f",
539
+ chartreuse: "#7fff00",
540
+ lawngreen: "#7cfc00",
541
+ springgreen: "#00ff7f",
542
+ greenyellow: "#adff2f",
543
+ brown: "#a52a2a",
544
+ sienna: "#a0522d",
545
+ chocolate: "#d2691e",
546
+ peru: "#cd853f",
547
+ tan: "#d2b48c",
548
+ beige: "#f5f5dc",
549
+ wheat: "#f5deb3",
550
+ ivory: "#fffff0",
551
+ silver: "#c0c0c0",
552
+ lightgray: "#d3d3d3",
553
+ lightgrey: "#d3d3d3",
554
+ darkgray: "#a9a9a9",
555
+ darkgrey: "#a9a9a9",
556
+ dimgray: "#696969",
557
+ dimgrey: "#696969",
558
+ slategray: "#708090",
559
+ slategrey: "#708090",
560
+ gainsboro: "#dcdcdc",
561
+ grey: "#808080"
562
+ });
391
563
  seriesColors = [
392
564
  nord.nord10,
393
565
  // blue
@@ -562,7 +734,13 @@ function extractColor(label, palette, diagnostics, line11) {
562
734
  );
563
735
  if (lastSpaceIdx < 0) return { label };
564
736
  const trailing = label.substring(lastSpaceIdx + 1);
565
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
737
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
738
+ if (diagnostics && line11 !== void 0) {
739
+ const diag = invalidColorDiagnostic(trailing, line11);
740
+ if (diag) diagnostics.push(diag);
741
+ }
742
+ return { label };
743
+ }
566
744
  let color;
567
745
  if (diagnostics && line11 !== void 0) {
568
746
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1186,6 +1364,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1186
1364
  }
1187
1365
  }
1188
1366
  }
1367
+ function cascadeTagMetadata(roots, tagGroups) {
1368
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1369
+ if (keys.length === 0) return;
1370
+ const walk = (node, inherited) => {
1371
+ const childInherited = { ...inherited };
1372
+ for (const key of keys) {
1373
+ const own = node.metadata[key];
1374
+ if (own) {
1375
+ childInherited[key] = own;
1376
+ } else if (inherited[key]) {
1377
+ node.metadata[key] = inherited[key];
1378
+ }
1379
+ }
1380
+ for (const child of node.children) walk(child, childInherited);
1381
+ };
1382
+ for (const root of roots) walk(root, {});
1383
+ }
1189
1384
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1190
1385
  const defaults = [];
1191
1386
  for (const group of tagGroups) {
@@ -1724,7 +1919,12 @@ function parseVisualizationFull(content, palette) {
1724
1919
  }
1725
1920
  if (currentTimelineTagGroup && indent > 0) {
1726
1921
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1727
- const { label, color } = extractColor(entryText, palette);
1922
+ const { label, color } = extractColor(
1923
+ entryText,
1924
+ palette,
1925
+ result.diagnostics,
1926
+ lineNumber
1927
+ );
1728
1928
  if (color) {
1729
1929
  if (isDefault) {
1730
1930
  currentTimelineTagGroup.defaultValue = label;
@@ -3025,9 +3225,12 @@ function contrastText(bg, lightText, darkText) {
3025
3225
  }
3026
3226
  return lightText;
3027
3227
  }
3228
+ function themeBaseBg(palette, isDark) {
3229
+ return isDark ? palette.surface : palette.bg;
3230
+ }
3028
3231
  function shapeFill(palette, intent, isDark, opts) {
3029
3232
  if (opts?.solid) return intent;
3030
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3233
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3031
3234
  }
3032
3235
  function getSeriesColors(palette) {
3033
3236
  const c = palette.colors;
@@ -3058,7 +3261,7 @@ function getSegmentColors(palette, count) {
3058
3261
  }
3059
3262
  function politicalTints(palette, count, isDark) {
3060
3263
  if (count <= 0) return [];
3061
- const base = isDark ? palette.surface : palette.bg;
3264
+ const base = themeBaseBg(palette, isDark);
3062
3265
  const c = palette.colors;
3063
3266
  const swatches = [
3064
3267
  .../* @__PURE__ */ new Set([
@@ -3868,6 +4071,7 @@ __export(palettes_exports, {
3868
4071
  shade: () => shade,
3869
4072
  shapeFill: () => shapeFill,
3870
4073
  slatePalette: () => slatePalette,
4074
+ themeBaseBg: () => themeBaseBg,
3871
4075
  tidewaterPalette: () => tidewaterPalette,
3872
4076
  tint: () => tint,
3873
4077
  tokyoNightPalette: () => tokyoNightPalette
@@ -4101,6 +4305,30 @@ var init_scaling = __esm({
4101
4305
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4102
4306
  return new _ScaleContext(clamped, minScaleFactor);
4103
4307
  }
4308
+ /**
4309
+ * Fit content into a bounding box, scaling by whichever dimension is more
4310
+ * constraining (the smaller of the width- and height-fit ratios) so the
4311
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4312
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4313
+ * never shrunk past the readability floor).
4314
+ */
4315
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4316
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4317
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4318
+ const raw = Math.min(wRaw, hRaw);
4319
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4320
+ return new _ScaleContext(clamped, minScaleFactor);
4321
+ }
4322
+ /**
4323
+ * Build a context from an explicit raw factor (clamped to
4324
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4325
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4326
+ * can still overflow — re-measure the laid-out result and tighten.
4327
+ */
4328
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4329
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4330
+ return new _ScaleContext(clamped, minScaleFactor);
4331
+ }
4104
4332
  static identity() {
4105
4333
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4106
4334
  }
@@ -5801,12 +6029,7 @@ function parseSequenceDgmo(content, palette) {
5801
6029
  const trimmed = token.trim();
5802
6030
  return nameAliasMap.get(trimmed) ?? trimmed;
5803
6031
  };
5804
- const fail = (line11, message) => {
5805
- const diag = makeDgmoError(line11, message);
5806
- result.diagnostics.push(diag);
5807
- result.error = formatDgmoError(diag);
5808
- return result;
5809
- };
6032
+ const fail = makeFail(result);
5810
6033
  const pushError = (line11, message) => {
5811
6034
  const diag = makeDgmoError(line11, message);
5812
6035
  result.diagnostics.push(diag);
@@ -5821,6 +6044,7 @@ function parseSequenceDgmo(content, palette) {
5821
6044
  const lines = content.split("\n");
5822
6045
  let hasExplicitChart = false;
5823
6046
  let contentStarted = false;
6047
+ let bodyStarted = false;
5824
6048
  let firstLineIndex = -1;
5825
6049
  for (let fi = 0; fi < lines.length; fi++) {
5826
6050
  const fl = lines[fi].trim();
@@ -6132,6 +6356,7 @@ function parseSequenceDgmo(content, palette) {
6132
6356
  );
6133
6357
  }
6134
6358
  contentStarted = true;
6359
+ bodyStarted = true;
6135
6360
  const section = {
6136
6361
  kind: "section",
6137
6362
  // Capture group 1 guaranteed present after successful match.
@@ -6347,7 +6572,7 @@ function parseSequenceDgmo(content, palette) {
6347
6572
  alias: bareAlias
6348
6573
  } = splitPipe(trimmed, lineNumber);
6349
6574
  const inGroup = activeGroup && measureIndent(raw) > 0;
6350
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6575
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6351
6576
  contentStarted = true;
6352
6577
  const id = bareCore;
6353
6578
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6394,6 +6619,7 @@ function parseSequenceDgmo(content, palette) {
6394
6619
  }
6395
6620
  if (labeledArrow) {
6396
6621
  contentStarted = true;
6622
+ bodyStarted = true;
6397
6623
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6398
6624
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6399
6625
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6458,6 +6684,7 @@ function parseSequenceDgmo(content, palette) {
6458
6684
  const bareCall = bareCallSync || bareCallAsync;
6459
6685
  if (bareCall) {
6460
6686
  contentStarted = true;
6687
+ bodyStarted = true;
6461
6688
  const from = bareCall[1];
6462
6689
  const to = bareCall[2];
6463
6690
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6479,6 +6706,7 @@ function parseSequenceDgmo(content, palette) {
6479
6706
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6480
6707
  if (ifMatch) {
6481
6708
  contentStarted = true;
6709
+ bodyStarted = true;
6482
6710
  const block = {
6483
6711
  kind: "block",
6484
6712
  type: "if",
@@ -6495,6 +6723,7 @@ function parseSequenceDgmo(content, palette) {
6495
6723
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6496
6724
  if (loopMatch) {
6497
6725
  contentStarted = true;
6726
+ bodyStarted = true;
6498
6727
  const block = {
6499
6728
  kind: "block",
6500
6729
  type: "loop",
@@ -6511,6 +6740,7 @@ function parseSequenceDgmo(content, palette) {
6511
6740
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6512
6741
  if (parallelMatch) {
6513
6742
  contentStarted = true;
6743
+ bodyStarted = true;
6514
6744
  const block = {
6515
6745
  kind: "block",
6516
6746
  type: "parallel",
@@ -6607,6 +6837,7 @@ function parseSequenceDgmo(content, palette) {
6607
6837
  lineNumber,
6608
6838
  endLineNumber: lineNumber
6609
6839
  };
6840
+ bodyStarted = true;
6610
6841
  currentContainer().push(note);
6611
6842
  continue;
6612
6843
  }
@@ -6631,6 +6862,7 @@ function parseSequenceDgmo(content, palette) {
6631
6862
  endLineNumber: i + 1
6632
6863
  // i has advanced past the body lines (1-based)
6633
6864
  };
6865
+ bodyStarted = true;
6634
6866
  currentContainer().push(note);
6635
6867
  continue;
6636
6868
  }
@@ -7475,6 +7707,15 @@ function parseNodeRef(text) {
7475
7707
  }
7476
7708
  return null;
7477
7709
  }
7710
+ function parseNodeRefLoose(text) {
7711
+ const t = text.trim();
7712
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7713
+ const m = t.match(shapeRe);
7714
+ if (!m) return null;
7715
+ const ref = parseNodeRef(m[1]);
7716
+ if (!ref) return null;
7717
+ return { ref, trailing: m[2].trim() };
7718
+ }
7478
7719
  function splitArrows(line11) {
7479
7720
  const segments = [];
7480
7721
  const arrowPositions = [];
@@ -7570,6 +7811,7 @@ function parseFlowchart(content, palette) {
7570
7811
  const notes = [];
7571
7812
  let contentStarted = false;
7572
7813
  let firstLineParsed = false;
7814
+ let prevLineLastNodeId = null;
7573
7815
  const nameAliasMap = /* @__PURE__ */ new Map();
7574
7816
  function peelAlias2(seg) {
7575
7817
  const trimmed = seg.trim();
@@ -7577,6 +7819,19 @@ function parseFlowchart(content, palette) {
7577
7819
  if (!m) return { seg: trimmed };
7578
7820
  return { seg: m[1].trim(), alias: m[2] };
7579
7821
  }
7822
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7823
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7824
+ if (suffixWarnedLines.has(lineNumber)) return;
7825
+ suffixWarnedLines.add(lineNumber);
7826
+ result.diagnostics.push(
7827
+ makeDgmoError(
7828
+ lineNumber,
7829
+ `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.`,
7830
+ "warning",
7831
+ "W_FLOWCHART_NODE_SUFFIX"
7832
+ )
7833
+ );
7834
+ }
7580
7835
  function getOrCreateNode(ref, lineNumber) {
7581
7836
  const key = ref.id;
7582
7837
  const existing = nodeMap.get(key);
@@ -7635,6 +7890,8 @@ function parseFlowchart(content, palette) {
7635
7890
  indentStack[indentStack.length - 1].nodeId
7636
7891
  ) : null;
7637
7892
  const segments = splitArrows(trimmed);
7893
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7894
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7638
7895
  if (segments.length === 1) {
7639
7896
  const peeled = peelAlias2(segments[0]);
7640
7897
  const ref = parseNodeRef(peeled.seg);
@@ -7644,6 +7901,13 @@ function parseFlowchart(content, palette) {
7644
7901
  indentStack.push({ nodeId: node.id, indent });
7645
7902
  return node.id;
7646
7903
  }
7904
+ const loose = parseNodeRefLoose(peeled.seg);
7905
+ if (loose) {
7906
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7907
+ const node = getOrCreateNode(loose.ref, lineNumber);
7908
+ indentStack.push({ nodeId: node.id, indent });
7909
+ return node.id;
7910
+ }
7647
7911
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7648
7912
  if (aliasResolved !== void 0) {
7649
7913
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7679,16 +7943,23 @@ function parseFlowchart(content, palette) {
7679
7943
  }
7680
7944
  }
7681
7945
  }
7946
+ if (!ref) {
7947
+ const loose = parseNodeRefLoose(peeled.seg);
7948
+ if (loose) {
7949
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7950
+ ref = loose.ref;
7951
+ }
7952
+ }
7682
7953
  if (!ref) continue;
7683
7954
  const node = getOrCreateNode(ref, lineNumber);
7684
7955
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7685
7956
  if (pendingArrow !== null) {
7686
- const sourceId = lastNodeId ?? implicitSourceId;
7957
+ const sourceId = lastNodeId ?? effectiveSource;
7687
7958
  if (sourceId) {
7688
7959
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7689
7960
  }
7690
7961
  pendingArrow = null;
7691
- } else if (lastNodeId === null && implicitSourceId === null) {
7962
+ } else if (lastNodeId === null && effectiveSource === null) {
7692
7963
  }
7693
7964
  lastNodeId = node.id;
7694
7965
  }
@@ -7770,7 +8041,8 @@ function parseFlowchart(content, palette) {
7770
8041
  continue;
7771
8042
  }
7772
8043
  }
7773
- processContentLine(trimmed, lineNumber, indent);
8044
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8045
+ if (lastId) prevLineLastNodeId = lastId;
7774
8046
  }
7775
8047
  if (result.nodes.length === 0 && !result.error) {
7776
8048
  const diag = makeDgmoError(
@@ -8853,7 +9125,12 @@ function parseERDiagram(content, palette) {
8853
9125
  }
8854
9126
  if (currentTagGroup && !contentStarted && indent > 0) {
8855
9127
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8856
- const { label, color } = extractColor(cleanEntry, palette);
9128
+ const { label, color } = extractColor(
9129
+ cleanEntry,
9130
+ palette,
9131
+ result.diagnostics,
9132
+ lineNumber
9133
+ );
8857
9134
  if (isDefault) {
8858
9135
  currentTagGroup.defaultValue = label;
8859
9136
  } else if (currentTagGroup.entries.length === 0) {
@@ -9171,12 +9448,7 @@ function parseOrg(content, palette) {
9171
9448
  diagnostics: [],
9172
9449
  error: null
9173
9450
  };
9174
- const fail = (line11, message) => {
9175
- const diag = makeDgmoError(line11, message);
9176
- result.diagnostics.push(diag);
9177
- result.error = formatDgmoError(diag);
9178
- return result;
9179
- };
9451
+ const fail = makeFail(result);
9180
9452
  const pushError = (line11, message) => {
9181
9453
  const diag = makeDgmoError(line11, message);
9182
9454
  result.diagnostics.push(diag);
@@ -9285,7 +9557,12 @@ function parseOrg(content, palette) {
9285
9557
  const indent2 = measureIndent(line11);
9286
9558
  if (indent2 > 0) {
9287
9559
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9288
- const { label, color } = extractColor(cleanEntry, palette);
9560
+ const { label, color } = extractColor(
9561
+ cleanEntry,
9562
+ palette,
9563
+ result.diagnostics,
9564
+ lineNumber
9565
+ );
9289
9566
  if (isDefault) {
9290
9567
  currentTagGroup.defaultValue = label;
9291
9568
  } else if (currentTagGroup.entries.length === 0) {
@@ -9571,12 +9848,7 @@ function parseSitemap(content, palette) {
9571
9848
  diagnostics: [],
9572
9849
  error: null
9573
9850
  };
9574
- const fail = (line11, message) => {
9575
- const diag = makeDgmoError(line11, message);
9576
- result.diagnostics.push(diag);
9577
- result.error = formatDgmoError(diag);
9578
- return result;
9579
- };
9851
+ const fail = makeFail(result);
9580
9852
  const pushError = (line11, message) => {
9581
9853
  const diag = makeDgmoError(line11, message);
9582
9854
  result.diagnostics.push(diag);
@@ -9677,7 +9949,12 @@ function parseSitemap(content, palette) {
9677
9949
  const indent2 = measureIndent(line11);
9678
9950
  if (indent2 > 0) {
9679
9951
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9680
- const { label, color } = extractColor(cleanEntry, palette);
9952
+ const { label, color } = extractColor(
9953
+ cleanEntry,
9954
+ palette,
9955
+ result.diagnostics,
9956
+ lineNumber
9957
+ );
9681
9958
  currentTagGroup.entries.push({
9682
9959
  value: label,
9683
9960
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11392,7 +11669,7 @@ var init_chart_types = __esm({
11392
11669
  },
11393
11670
  {
11394
11671
  id: "sequence",
11395
- description: "Message / interaction flows",
11672
+ description: "Message request and response interaction flows",
11396
11673
  fallback: true
11397
11674
  },
11398
11675
  {
@@ -11466,7 +11743,7 @@ var init_chart_types = __esm({
11466
11743
  },
11467
11744
  {
11468
11745
  id: "map",
11469
- description: "Geographic map: a value or count per country, state, or region (choropleth); points of interest; routes. Use when categories are real-world places."
11746
+ description: "Geographic concept map: highlight/score regions, drop points of interest, connect with routes or edges"
11470
11747
  },
11471
11748
  // ── Tier 3 — Specialized analytical charts ────────────────
11472
11749
  {
@@ -11483,7 +11760,7 @@ var init_chart_types = __esm({
11483
11760
  },
11484
11761
  {
11485
11762
  id: "slope",
11486
- description: "Change for multiple things between exactly two periods"
11763
+ description: "Change between 2 time periods"
11487
11764
  },
11488
11765
  {
11489
11766
  id: "sankey",
@@ -11512,7 +11789,7 @@ var init_chart_types = __esm({
11512
11789
  // ── Tier 4 — General-purpose data charts ──────────────────
11513
11790
  {
11514
11791
  id: "bar",
11515
- description: "Categorical comparisons",
11792
+ description: "Categorical comparisons for 3 - 5 figures",
11516
11793
  fallback: true
11517
11794
  },
11518
11795
  {
@@ -11774,7 +12051,9 @@ function parseChart(content, palette) {
11774
12051
  if (dataValues) {
11775
12052
  const { label: rawLabel, color: pointColor } = extractColor(
11776
12053
  dataValues.label,
11777
- palette
12054
+ palette,
12055
+ result.diagnostics,
12056
+ lineNumber
11778
12057
  );
11779
12058
  const [first, ...rest] = dataValues.values;
11780
12059
  result.data.push({
@@ -11839,6 +12118,12 @@ function parseChart(content, palette) {
11839
12118
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11840
12119
  );
11841
12120
  }
12121
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12122
+ warn2(
12123
+ result.seriesLineNumber ?? 1,
12124
+ `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.`
12125
+ );
12126
+ }
11842
12127
  if (!result.error && result.seriesNames) {
11843
12128
  const expectedCount = result.seriesNames.length;
11844
12129
  for (const dp of result.data) {
@@ -12087,7 +12372,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12087
12372
  if (!dataRow || dataRow.values.length < 2) return null;
12088
12373
  const { label: rawLabel, color: pointColor } = extractColor(
12089
12374
  dataRow.label,
12090
- palette
12375
+ palette,
12376
+ diagnostics,
12377
+ lineNumber
12091
12378
  );
12092
12379
  return {
12093
12380
  name: rawLabel,
@@ -12225,11 +12512,15 @@ function parseExtendedChartFull(content, palette) {
12225
12512
  const targetResolved = resolveSlot(rawTarget);
12226
12513
  const { label: source, color: sourceColor } = extractColor(
12227
12514
  sourceResolved,
12228
- palette
12515
+ palette,
12516
+ result.diagnostics,
12517
+ lineNumber
12229
12518
  );
12230
12519
  const { label: target, color: targetColor } = extractColor(
12231
12520
  targetResolved,
12232
- palette
12521
+ palette,
12522
+ result.diagnostics,
12523
+ lineNumber
12233
12524
  );
12234
12525
  if (sourceColor || targetColor) {
12235
12526
  if (!result.nodeColors) result.nodeColors = {};
@@ -12281,7 +12572,9 @@ function parseExtendedChartFull(content, palette) {
12281
12572
  const targetResolved = resolveSlot(dataRow2.label);
12282
12573
  const { label: target, color: targetColor } = extractColor(
12283
12574
  targetResolved,
12284
- palette
12575
+ palette,
12576
+ result.diagnostics,
12577
+ lineNumber
12285
12578
  );
12286
12579
  if (targetColor) {
12287
12580
  if (!result.nodeColors) result.nodeColors = {};
@@ -12314,7 +12607,9 @@ function parseExtendedChartFull(content, palette) {
12314
12607
  const trimmedResolved = resolveSlot(trimmed);
12315
12608
  const { label: nodeName, color: nodeColor2 } = extractColor(
12316
12609
  trimmedResolved,
12317
- palette
12610
+ palette,
12611
+ result.diagnostics,
12612
+ lineNumber
12318
12613
  );
12319
12614
  if (nodeColor2) {
12320
12615
  if (!result.nodeColors) result.nodeColors = {};
@@ -12404,8 +12699,11 @@ function parseExtendedChartFull(content, palette) {
12404
12699
  min: parseFloat(rangeMatch[1]),
12405
12700
  max: parseFloat(rangeMatch[2])
12406
12701
  };
12702
+ continue;
12703
+ }
12704
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12705
+ continue;
12407
12706
  }
12408
- continue;
12409
12707
  }
12410
12708
  }
12411
12709
  if (firstToken === "no-name") {
@@ -12465,7 +12763,9 @@ function parseExtendedChartFull(content, palette) {
12465
12763
  if (colonIndex >= 0) {
12466
12764
  const { label: fnName, color: fnColor } = extractColor(
12467
12765
  trimmed.substring(0, colonIndex).trim(),
12468
- palette
12766
+ palette,
12767
+ result.diagnostics,
12768
+ lineNumber
12469
12769
  );
12470
12770
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12471
12771
  if (!result.functions) result.functions = [];
@@ -12513,7 +12813,9 @@ function parseExtendedChartFull(content, palette) {
12513
12813
  if (dataRow?.values.length === 1) {
12514
12814
  const { label: rawLabel, color: pointColor } = extractColor(
12515
12815
  dataRow.label,
12516
- palette
12816
+ palette,
12817
+ result.diagnostics,
12818
+ lineNumber
12517
12819
  );
12518
12820
  result.data.push({
12519
12821
  label: rawLabel,
@@ -12612,11 +12914,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12612
12914
  const sc = ctx ?? ScaleContext.identity();
12613
12915
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12614
12916
  if (parsed.type === "sankey") {
12615
- const bg = isDark ? palette.surface : palette.bg;
12917
+ const bg = themeBaseBg(palette, isDark);
12616
12918
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12617
12919
  }
12618
12920
  if (parsed.type === "chord") {
12619
- const bg = isDark ? palette.surface : palette.bg;
12921
+ const bg = themeBaseBg(palette, isDark);
12620
12922
  return buildChordOption(
12621
12923
  parsed,
12622
12924
  palette,
@@ -12657,7 +12959,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12657
12959
  );
12658
12960
  }
12659
12961
  if (parsed.type === "funnel") {
12660
- const bg = isDark ? palette.surface : palette.bg;
12962
+ const bg = themeBaseBg(palette, isDark);
12661
12963
  return buildFunnelOption(
12662
12964
  parsed,
12663
12965
  palette,
@@ -13253,7 +13555,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13253
13555
  const gridLeft = parsed.ylabel ? 12 : 3;
13254
13556
  const gridRight = 4;
13255
13557
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13256
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13558
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13559
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13257
13560
  let graphic;
13258
13561
  if (showLabels && points.length > 0) {
13259
13562
  const labelPoints = [];
@@ -13393,7 +13696,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13393
13696
  }
13394
13697
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13395
13698
  const sc = ctx ?? ScaleContext.identity();
13396
- const bg = isDark ? palette.surface : palette.bg;
13699
+ const bg = themeBaseBg(palette, isDark);
13397
13700
  const heatmapRows = parsed.heatmapRows ?? [];
13398
13701
  const columns = parsed.columns ?? [];
13399
13702
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13712,7 +14015,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13712
14015
  colors,
13713
14016
  titleConfig
13714
14017
  } = buildChartCommons(parsed, palette, isDark, sc);
13715
- const bg = isDark ? palette.surface : palette.bg;
14018
+ const bg = themeBaseBg(palette, isDark);
13716
14019
  switch (parsed.type) {
13717
14020
  case "bar":
13718
14021
  return buildBarOption(
@@ -14664,12 +14967,7 @@ function parseKanban(content, palette) {
14664
14967
  diagnostics: [],
14665
14968
  error: null
14666
14969
  };
14667
- const fail = (line11, message) => {
14668
- const diag = makeDgmoError(line11, message);
14669
- result.diagnostics.push(diag);
14670
- result.error = formatDgmoError(diag);
14671
- return result;
14672
- };
14970
+ const fail = makeFail(result);
14673
14971
  const warn2 = (line11, message) => {
14674
14972
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14675
14973
  };
@@ -14772,7 +15070,12 @@ function parseKanban(content, palette) {
14772
15070
  const indent2 = measureIndent(line11);
14773
15071
  if (indent2 > 0) {
14774
15072
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14775
- const { label, color } = extractColor(cleanEntry, palette);
15073
+ const { label, color } = extractColor(
15074
+ cleanEntry,
15075
+ palette,
15076
+ result.diagnostics,
15077
+ lineNumber
15078
+ );
14776
15079
  if (isDefault) {
14777
15080
  currentTagGroup.defaultValue = label;
14778
15081
  } else if (currentTagGroup.entries.length === 0) {
@@ -15116,12 +15419,7 @@ function parseC4(content, palette) {
15116
15419
  if (!result.error && severity === "error")
15117
15420
  result.error = formatDgmoError(diag);
15118
15421
  };
15119
- const fail = (line11, message) => {
15120
- const diag = makeDgmoError(line11, message);
15121
- result.diagnostics.push(diag);
15122
- result.error = formatDgmoError(diag);
15123
- return result;
15124
- };
15422
+ const fail = makeFail(result);
15125
15423
  if (!content?.trim()) {
15126
15424
  return fail(0, "No content provided");
15127
15425
  }
@@ -15222,7 +15520,12 @@ function parseC4(content, palette) {
15222
15520
  const indent2 = measureIndent(line11);
15223
15521
  if (indent2 > 0) {
15224
15522
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15225
- const { label, color } = extractColor(cleanEntry, palette);
15523
+ const { label, color } = extractColor(
15524
+ cleanEntry,
15525
+ palette,
15526
+ result.diagnostics,
15527
+ lineNumber
15528
+ );
15226
15529
  if (isDefault) {
15227
15530
  currentTagGroup.defaultValue = label;
15228
15531
  } else if (currentTagGroup.entries.length === 0) {
@@ -16887,8 +17190,66 @@ function parseInfra(content) {
16887
17190
  }
16888
17191
  }
16889
17192
  validateTagGroupNames(result.tagGroups, warn2, setError);
17193
+ checkReachability(result);
16890
17194
  return result;
16891
17195
  }
17196
+ function checkReachability(result) {
17197
+ if (result.nodes.length === 0) return;
17198
+ const entries = result.nodes.filter((n) => n.isEdge);
17199
+ if (entries.length === 0) {
17200
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17201
+ result.diagnostics.push(
17202
+ makeDgmoError(
17203
+ line11,
17204
+ `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.`,
17205
+ "warning",
17206
+ "W_INFRA_NO_ENTRY"
17207
+ )
17208
+ );
17209
+ return;
17210
+ }
17211
+ const groupChildMap = /* @__PURE__ */ new Map();
17212
+ for (const node of result.nodes) {
17213
+ if (node.groupId) {
17214
+ const list = groupChildMap.get(node.groupId) ?? [];
17215
+ list.push(node.id);
17216
+ groupChildMap.set(node.groupId, list);
17217
+ }
17218
+ }
17219
+ const outbound = /* @__PURE__ */ new Map();
17220
+ for (const edge of result.edges) {
17221
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17222
+ const list = outbound.get(edge.sourceId) ?? [];
17223
+ list.push(...targets);
17224
+ outbound.set(edge.sourceId, list);
17225
+ }
17226
+ const reachable = /* @__PURE__ */ new Set();
17227
+ const queue = [];
17228
+ for (const entry of entries) {
17229
+ reachable.add(entry.id);
17230
+ queue.push(entry.id);
17231
+ }
17232
+ while (queue.length > 0) {
17233
+ const current = queue.shift();
17234
+ for (const next of outbound.get(current) ?? []) {
17235
+ if (!reachable.has(next)) {
17236
+ reachable.add(next);
17237
+ queue.push(next);
17238
+ }
17239
+ }
17240
+ }
17241
+ for (const node of result.nodes) {
17242
+ if (node.isEdge || reachable.has(node.id)) continue;
17243
+ result.diagnostics.push(
17244
+ makeDgmoError(
17245
+ node.lineNumber,
17246
+ `'${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.`,
17247
+ "warning",
17248
+ "W_INFRA_UNREACHABLE"
17249
+ )
17250
+ );
17251
+ }
17252
+ }
16892
17253
  function stripNodeDecorations(name) {
16893
17254
  let s = name.trim();
16894
17255
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17178,7 +17539,12 @@ function parseGantt(content, palette) {
17178
17539
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17179
17540
  if (eraEntryMatch) {
17180
17541
  const eraLabelRaw = eraEntryMatch[3].trim();
17181
- const eraExtracted = extractColor(eraLabelRaw, palette);
17542
+ const eraExtracted = extractColor(
17543
+ eraLabelRaw,
17544
+ palette,
17545
+ diagnostics,
17546
+ lineNumber
17547
+ );
17182
17548
  result.eras.push({
17183
17549
  startDate: eraEntryMatch[1],
17184
17550
  endDate: eraEntryMatch[2],
@@ -17200,7 +17566,12 @@ function parseGantt(content, palette) {
17200
17566
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17201
17567
  if (markerEntryMatch) {
17202
17568
  const markerLabelRaw = markerEntryMatch[2].trim();
17203
- const markerExtracted = extractColor(markerLabelRaw, palette);
17569
+ const markerExtracted = extractColor(
17570
+ markerLabelRaw,
17571
+ palette,
17572
+ diagnostics,
17573
+ lineNumber
17574
+ );
17204
17575
  result.markers.push({
17205
17576
  date: markerEntryMatch[1],
17206
17577
  label: markerExtracted.label,
@@ -17221,7 +17592,12 @@ function parseGantt(content, palette) {
17221
17592
  } else {
17222
17593
  if (COMMENT_RE.test(line11)) continue;
17223
17594
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17224
- const extracted = extractColor(cleanEntry, palette);
17595
+ const extracted = extractColor(
17596
+ cleanEntry,
17597
+ palette,
17598
+ diagnostics,
17599
+ lineNumber
17600
+ );
17225
17601
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17226
17602
  const isFirstEntry = currentTagGroup.entries.length === 0;
17227
17603
  currentTagGroup.entries.push({
@@ -17416,7 +17792,12 @@ function parseGantt(content, palette) {
17416
17792
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17417
17793
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17418
17794
  const eraLabelRaw = eraOffsetMatch[3].trim();
17419
- const eraExtracted = extractColor(eraLabelRaw, palette);
17795
+ const eraExtracted = extractColor(
17796
+ eraLabelRaw,
17797
+ palette,
17798
+ diagnostics,
17799
+ lineNumber
17800
+ );
17420
17801
  result.eras.push({
17421
17802
  startDate: "",
17422
17803
  endDate: "",
@@ -17432,7 +17813,12 @@ function parseGantt(content, palette) {
17432
17813
  if (markerOffsetMatch) {
17433
17814
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17434
17815
  const markerLabelRaw = markerOffsetMatch[2].trim();
17435
- const markerExtracted = extractColor(markerLabelRaw, palette);
17816
+ const markerExtracted = extractColor(
17817
+ markerLabelRaw,
17818
+ palette,
17819
+ diagnostics,
17820
+ lineNumber
17821
+ );
17436
17822
  result.markers.push({
17437
17823
  date: "",
17438
17824
  label: markerExtracted.label,
@@ -17506,7 +17892,12 @@ function parseGantt(content, palette) {
17506
17892
  const eraMatch = line11.match(ERA_RE);
17507
17893
  if (eraMatch) {
17508
17894
  const eraLabelRaw = eraMatch[3].trim();
17509
- const eraExtracted = extractColor(eraLabelRaw, palette);
17895
+ const eraExtracted = extractColor(
17896
+ eraLabelRaw,
17897
+ palette,
17898
+ diagnostics,
17899
+ lineNumber
17900
+ );
17510
17901
  result.eras.push({
17511
17902
  startDate: eraMatch[1],
17512
17903
  endDate: eraMatch[2],
@@ -17524,7 +17915,12 @@ function parseGantt(content, palette) {
17524
17915
  const markerMatch = line11.match(MARKER_RE);
17525
17916
  if (markerMatch) {
17526
17917
  const markerLabelRaw = markerMatch[2].trim();
17527
- const markerExtracted = extractColor(markerLabelRaw, palette);
17918
+ const markerExtracted = extractColor(
17919
+ markerLabelRaw,
17920
+ palette,
17921
+ diagnostics,
17922
+ lineNumber
17923
+ );
17528
17924
  result.markers.push({
17529
17925
  date: markerMatch[1],
17530
17926
  label: markerExtracted.label,
@@ -18996,7 +19392,7 @@ function parseBoxesAndLines(content, palette) {
18996
19392
  const trimmed = raw.trim();
18997
19393
  const indent = measureIndent2(raw);
18998
19394
  if (!trimmed || trimmed.startsWith("//")) continue;
18999
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19395
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
19000
19396
  result.diagnostics.push(
19001
19397
  makeDgmoError(
19002
19398
  lineNum,
@@ -19125,7 +19521,12 @@ function parseBoxesAndLines(content, palette) {
19125
19521
  if (tagBlockMatch.inlineValues) {
19126
19522
  for (const rawVal of tagBlockMatch.inlineValues) {
19127
19523
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19128
- const { label, color } = extractColor(cleanVal);
19524
+ const { label, color } = extractColor(
19525
+ cleanVal,
19526
+ palette,
19527
+ result.diagnostics,
19528
+ lineNum
19529
+ );
19129
19530
  newTagGroup.entries.push({
19130
19531
  value: label,
19131
19532
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19142,7 +19543,12 @@ function parseBoxesAndLines(content, palette) {
19142
19543
  }
19143
19544
  if (currentTagGroup && !contentStarted && indent > 0) {
19144
19545
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19145
- const { label, color } = extractColor(cleanEntry);
19546
+ const { label, color } = extractColor(
19547
+ cleanEntry,
19548
+ palette,
19549
+ result.diagnostics,
19550
+ lineNum
19551
+ );
19146
19552
  currentTagGroup.entries.push({
19147
19553
  value: label,
19148
19554
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19740,7 +20146,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19740
20146
  metadata
19741
20147
  };
19742
20148
  }
19743
- var MAX_GROUP_DEPTH;
20149
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19744
20150
  var init_parser18 = __esm({
19745
20151
  "src/boxes-and-lines/parser.ts"() {
19746
20152
  "use strict";
@@ -19752,6 +20158,8 @@ var init_parser18 = __esm({
19752
20158
  init_reserved_key_registry();
19753
20159
  init_notes();
19754
20160
  MAX_GROUP_DEPTH = 2;
20161
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20162
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19755
20163
  }
19756
20164
  });
19757
20165
 
@@ -19771,12 +20179,7 @@ function parseMindmap(content, palette) {
19771
20179
  diagnostics: [],
19772
20180
  error: null
19773
20181
  };
19774
- const fail = (line11, message) => {
19775
- const diag = makeDgmoError(line11, message);
19776
- result.diagnostics.push(diag);
19777
- result.error = formatDgmoError(diag);
19778
- return result;
19779
- };
20182
+ const fail = makeFail(result);
19780
20183
  const pushError = (line11, message) => {
19781
20184
  const diag = makeDgmoError(line11, message);
19782
20185
  result.diagnostics.push(diag);
@@ -19889,7 +20292,12 @@ function parseMindmap(content, palette) {
19889
20292
  const indent2 = measureIndent(line11);
19890
20293
  if (indent2 > 0) {
19891
20294
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19892
- const { label, color } = extractColor(cleanEntry, palette);
20295
+ const { label, color } = extractColor(
20296
+ cleanEntry,
20297
+ palette,
20298
+ result.diagnostics,
20299
+ lineNumber
20300
+ );
19893
20301
  if (isDefault) {
19894
20302
  currentTagGroup.defaultValue = label;
19895
20303
  } else if (currentTagGroup.entries.length === 0) {
@@ -19966,6 +20374,7 @@ function parseMindmap(content, palette) {
19966
20374
  collectAll(result.roots);
19967
20375
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
19968
20376
  validateTagGroupNames(result.tagGroups, pushWarning);
20377
+ cascadeTagMetadata(result.roots, result.tagGroups);
19969
20378
  }
19970
20379
  if (result.roots.length === 0 && !result.error) {
19971
20380
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20572,7 +20981,12 @@ function parseWireframe(content) {
20572
20981
  }
20573
20982
  if (indent > 0 && currentTagGroup) {
20574
20983
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20575
- const { label, color } = extractColor(cleanEntry);
20984
+ const { label, color } = extractColor(
20985
+ cleanEntry,
20986
+ void 0,
20987
+ diagnostics,
20988
+ lineNumber
20989
+ );
20576
20990
  currentTagGroup.entries.push({
20577
20991
  value: label,
20578
20992
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20820,12 +21234,7 @@ function parseTechRadar(content) {
20820
21234
  diagnostics: [],
20821
21235
  error: null
20822
21236
  };
20823
- const fail = (line11, message) => {
20824
- const diag = makeDgmoError(line11, message);
20825
- result.diagnostics.push(diag);
20826
- result.error = formatDgmoError(diag);
20827
- return result;
20828
- };
21237
+ const fail = makeFail(result);
20829
21238
  const warn2 = (line11, message) => {
20830
21239
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20831
21240
  };
@@ -21193,12 +21602,7 @@ function parseCycle(content) {
21193
21602
  let state = "top";
21194
21603
  let currentNode = null;
21195
21604
  let currentEdge = null;
21196
- const fail = (line11, message) => {
21197
- const diag = makeDgmoError(line11, message);
21198
- result.diagnostics.push(diag);
21199
- result.error = formatDgmoError(diag);
21200
- return result;
21201
- };
21605
+ const fail = makeFail(result);
21202
21606
  const warn2 = (line11, message, severity = "warning") => {
21203
21607
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21204
21608
  };
@@ -21475,12 +21879,7 @@ function parseJourneyMap(content, palette) {
21475
21879
  diagnostics: [],
21476
21880
  error: null
21477
21881
  };
21478
- const fail = (line11, message) => {
21479
- const diag = makeDgmoError(line11, message);
21480
- result.diagnostics.push(diag);
21481
- result.error = formatDgmoError(diag);
21482
- return result;
21483
- };
21882
+ const fail = makeFail(result);
21484
21883
  const warn2 = (line11, message) => {
21485
21884
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21486
21885
  };
@@ -21629,7 +22028,12 @@ function parseJourneyMap(content, palette) {
21629
22028
  if (currentTagGroup && !contentStarted) {
21630
22029
  if (indent > 0) {
21631
22030
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21632
- const { label, color } = extractColor(cleanEntry, palette);
22031
+ const { label, color } = extractColor(
22032
+ cleanEntry,
22033
+ palette,
22034
+ result.diagnostics,
22035
+ lineNumber
22036
+ );
21633
22037
  if (isDefault) {
21634
22038
  currentTagGroup.defaultValue = label;
21635
22039
  } else if (currentTagGroup.entries.length === 0) {
@@ -22001,12 +22405,7 @@ function parsePyramid(content) {
22001
22405
  const lines = content.split("\n");
22002
22406
  let headerParsed = false;
22003
22407
  let currentLayer = null;
22004
- const fail = (line11, message) => {
22005
- const diag = makeDgmoError(line11, message);
22006
- result.diagnostics.push(diag);
22007
- result.error = formatDgmoError(diag);
22008
- return result;
22009
- };
22408
+ const fail = makeFail(result);
22010
22409
  const warn2 = (line11, message, severity = "warning") => {
22011
22410
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22012
22411
  };
@@ -22153,12 +22552,7 @@ function parseRing(content) {
22153
22552
  const lines = content.split("\n");
22154
22553
  let headerParsed = false;
22155
22554
  let currentLayer = null;
22156
- const fail = (line11, message) => {
22157
- const diag = makeDgmoError(line11, message);
22158
- result.diagnostics.push(diag);
22159
- result.error = formatDgmoError(diag);
22160
- return result;
22161
- };
22555
+ const fail = makeFail(result);
22162
22556
  const warn2 = (line11, message, severity = "warning") => {
22163
22557
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22164
22558
  };
@@ -22622,12 +23016,7 @@ function parseRaci(content, palette) {
22622
23016
  diagnostics: [],
22623
23017
  error: null
22624
23018
  };
22625
- const fail = (line11, message) => {
22626
- const diag = makeDgmoError(line11, message);
22627
- result.diagnostics.push(diag);
22628
- result.error = formatDgmoError(diag);
22629
- return result;
22630
- };
23019
+ const fail = makeFail(result);
22631
23020
  const warn2 = (line11, message, code) => {
22632
23021
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22633
23022
  };
@@ -23230,6 +23619,81 @@ function measureInfra(content) {
23230
23619
  const parsed = parseInfra(content);
23231
23620
  return { nodes: parsed.nodes.length };
23232
23621
  }
23622
+ function minDimsSequence(c) {
23623
+ return {
23624
+ width: Math.max((c.participants ?? 2) * 80, 320),
23625
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23626
+ };
23627
+ }
23628
+ function minDimsRaci(c) {
23629
+ return {
23630
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23631
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23632
+ };
23633
+ }
23634
+ function minDimsMindmap(c) {
23635
+ return {
23636
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23637
+ height: Math.max((c.depth ?? 2) * 60, 200)
23638
+ };
23639
+ }
23640
+ function minDimsTechRadar() {
23641
+ return { width: 360, height: 400 };
23642
+ }
23643
+ function minDimsHeatmap(c) {
23644
+ return {
23645
+ width: Math.max((c.columns ?? 3) * 40, 300),
23646
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23647
+ };
23648
+ }
23649
+ function minDimsArc(c) {
23650
+ return {
23651
+ width: 300,
23652
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23653
+ };
23654
+ }
23655
+ function minDimsOrg(c) {
23656
+ return {
23657
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23658
+ height: Math.max((c.depth ?? 2) * 80, 200)
23659
+ };
23660
+ }
23661
+ function minDimsGantt(c) {
23662
+ return {
23663
+ width: 400,
23664
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23665
+ };
23666
+ }
23667
+ function minDimsKanban(c) {
23668
+ return {
23669
+ width: Math.max((c.columns ?? 3) * 120, 360),
23670
+ height: 300
23671
+ };
23672
+ }
23673
+ function minDimsEntities(c) {
23674
+ return {
23675
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23676
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23677
+ };
23678
+ }
23679
+ function minDimsGraph(c) {
23680
+ return {
23681
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23682
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23683
+ };
23684
+ }
23685
+ function minDimsPert(c) {
23686
+ return {
23687
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23688
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23689
+ };
23690
+ }
23691
+ function minDimsInfra(c) {
23692
+ return {
23693
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23694
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23695
+ };
23696
+ }
23233
23697
  function isExtendedChartParser(parse) {
23234
23698
  return EXTENDED_CHART_DOORS.has(parse);
23235
23699
  }
@@ -23273,33 +23737,50 @@ var init_chart_type_registry = __esm({
23273
23737
  id: "sequence",
23274
23738
  category: "diagram",
23275
23739
  parse: parseSequenceDgmo,
23276
- measure: measureSequence
23740
+ measure: measureSequence,
23741
+ minDims: minDimsSequence
23277
23742
  },
23278
23743
  {
23279
23744
  id: "flowchart",
23280
23745
  category: "diagram",
23281
23746
  parse: parseFlowchart,
23282
- measure: measureFlowchart
23747
+ measure: measureFlowchart,
23748
+ minDims: minDimsGraph
23283
23749
  },
23284
23750
  {
23285
23751
  id: "class",
23286
23752
  category: "diagram",
23287
23753
  parse: parseClassDiagram,
23288
- measure: measureClass
23754
+ measure: measureClass,
23755
+ minDims: minDimsEntities
23756
+ },
23757
+ {
23758
+ id: "er",
23759
+ category: "diagram",
23760
+ parse: parseERDiagram,
23761
+ measure: measureER,
23762
+ minDims: minDimsEntities
23289
23763
  },
23290
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23291
23764
  {
23292
23765
  id: "state",
23293
23766
  category: "diagram",
23294
23767
  parse: parseState,
23295
- measure: measureStateGraph
23768
+ measure: measureStateGraph,
23769
+ minDims: minDimsGraph
23770
+ },
23771
+ {
23772
+ id: "org",
23773
+ category: "diagram",
23774
+ parse: parseOrg,
23775
+ measure: measureOrg,
23776
+ minDims: minDimsOrg
23296
23777
  },
23297
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23298
23778
  {
23299
23779
  id: "kanban",
23300
23780
  category: "diagram",
23301
23781
  parse: parseKanban,
23302
- measure: measureKanban
23782
+ measure: measureKanban,
23783
+ minDims: minDimsKanban
23303
23784
  },
23304
23785
  { id: "c4", category: "diagram", parse: parseC4 },
23305
23786
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23307,25 +23788,40 @@ var init_chart_type_registry = __esm({
23307
23788
  id: "infra",
23308
23789
  category: "diagram",
23309
23790
  parse: parseInfra,
23310
- measure: measureInfra
23791
+ measure: measureInfra,
23792
+ minDims: minDimsInfra
23311
23793
  },
23312
23794
  {
23313
23795
  id: "gantt",
23314
23796
  category: "diagram",
23315
23797
  parse: parseGantt,
23316
- measure: measureGantt
23798
+ measure: measureGantt,
23799
+ minDims: minDimsGantt
23800
+ },
23801
+ {
23802
+ id: "pert",
23803
+ category: "diagram",
23804
+ parse: parsePert,
23805
+ measure: measurePert,
23806
+ minDims: minDimsPert
23317
23807
  },
23318
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23319
23808
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23320
23809
  {
23321
23810
  id: "mindmap",
23322
23811
  category: "diagram",
23323
23812
  parse: parseMindmap,
23324
- measure: measureMindmap
23813
+ measure: measureMindmap,
23814
+ minDims: minDimsMindmap
23325
23815
  },
23326
23816
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23327
23817
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23328
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23818
+ {
23819
+ id: "raci",
23820
+ category: "diagram",
23821
+ parse: parseRaci,
23822
+ measure: measureRaci,
23823
+ minDims: minDimsRaci
23824
+ },
23329
23825
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23330
23826
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23331
23827
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23347,7 +23843,8 @@ var init_chart_type_registry = __esm({
23347
23843
  id: "heatmap",
23348
23844
  category: "data-chart",
23349
23845
  parse: parseHeatmap,
23350
- measure: measureHeatmap
23846
+ measure: measureHeatmap,
23847
+ minDims: minDimsHeatmap
23351
23848
  },
23352
23849
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23353
23850
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23357,7 +23854,8 @@ var init_chart_type_registry = __esm({
23357
23854
  id: "arc",
23358
23855
  category: "visualization",
23359
23856
  parse: parseArc,
23360
- measure: measureArc
23857
+ measure: measureArc,
23858
+ minDims: minDimsArc
23361
23859
  },
23362
23860
  { id: "timeline", category: "visualization", parse: parseTimeline },
23363
23861
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23367,7 +23865,8 @@ var init_chart_type_registry = __esm({
23367
23865
  id: "tech-radar",
23368
23866
  category: "visualization",
23369
23867
  parse: parseTechRadar,
23370
- measure: measureTechRadar
23868
+ measure: measureTechRadar,
23869
+ minDims: minDimsTechRadar
23371
23870
  },
23372
23871
  { id: "cycle", category: "visualization", parse: parseCycle },
23373
23872
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24588,7 +25087,7 @@ function nodeStroke(palette, nodeColor2) {
24588
25087
  }
24589
25088
  function containerFill(palette, isDark, nodeColor2) {
24590
25089
  if (nodeColor2) {
24591
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25090
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24592
25091
  }
24593
25092
  return mix(palette.surface, palette.bg, 40);
24594
25093
  }
@@ -24746,10 +25245,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24746
25245
  const iconY = iconPad;
24747
25246
  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})`);
24748
25247
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25248
+ const iconColor = contrastText(
25249
+ fill2,
25250
+ palette.textOnFillLight,
25251
+ palette.textOnFillDark
25252
+ );
24749
25253
  const cx = iconSize / 2;
24750
25254
  const cy = iconSize / 2;
24751
- 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);
24752
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25255
+ 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);
25256
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24753
25257
  }
24754
25258
  }
24755
25259
  for (const edge of layout.edges) {
@@ -24838,10 +25342,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24838
25342
  const iconY = iconPad;
24839
25343
  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})`);
24840
25344
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25345
+ const iconColor = labelColor;
24841
25346
  const cx = iconSize / 2;
24842
25347
  const cy = iconSize / 2;
24843
- 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);
24844
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25348
+ 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);
25349
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24845
25350
  }
24846
25351
  }
24847
25352
  if (hasAncestorTrail) {
@@ -25762,7 +26267,7 @@ function nodeStroke2(_palette, nodeColor2) {
25762
26267
  }
25763
26268
  function containerFill2(palette, isDark, nodeColor2) {
25764
26269
  if (nodeColor2) {
25765
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26270
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25766
26271
  }
25767
26272
  return mix(palette.surface, palette.bg, 40);
25768
26273
  }
@@ -28569,7 +29074,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28569
29074
  onToggleDescriptions,
28570
29075
  onToggleControlsExpand,
28571
29076
  exportMode = false,
28572
- controlsHost
29077
+ controlsHost,
29078
+ rampDomain
28573
29079
  } = options ?? {};
28574
29080
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28575
29081
  const width = exportDims?.width ?? container.clientWidth;
@@ -28589,8 +29095,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28589
29095
  const sTitleY = sctx.structural(TITLE_Y);
28590
29096
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28591
29097
  const hasRamp = nodeValues.length > 0;
28592
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28593
- const rampMax = Math.max(...nodeValues);
29098
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29099
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28594
29100
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28595
29101
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28596
29102
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28742,7 +29248,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28742
29248
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28743
29249
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28744
29250
  if (group.collapsed) {
28745
- const fillColor = isDark ? palette.surface : palette.bg;
29251
+ const fillColor = themeBaseBg(palette, isDark);
28746
29252
  const strokeColor = palette.border;
28747
29253
  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);
28748
29254
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -29098,6 +29604,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29098
29604
  });
29099
29605
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29100
29606
  }
29607
+ if (!exportDims && !exportMode) {
29608
+ const iconSize = 14;
29609
+ 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");
29610
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29611
+ const cx = iconSize / 2;
29612
+ const cy = iconSize / 2;
29613
+ 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);
29614
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29615
+ }
29101
29616
  }
29102
29617
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29103
29618
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29694,7 +30209,7 @@ function shuffle(a, r) {
29694
30209
  return x;
29695
30210
  }
29696
30211
  function flatten(d) {
29697
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30212
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29698
30213
  const pts = [];
29699
30214
  let i = 0, cx = 0, cy = 0, cmd = "";
29700
30215
  const num = () => parseFloat(toks[i++]);
@@ -29738,17 +30253,10 @@ function flatten(d) {
29738
30253
  }
29739
30254
  return pts;
29740
30255
  }
29741
- function segPoint(p1, p2, p3, p4) {
29742
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29743
- if (Math.abs(den) < 1e-9) return null;
29744
- 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;
29745
- 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;
29746
- }
29747
- function countSplineCrossings(layout) {
29748
- const center = /* @__PURE__ */ new Map();
29749
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29750
- for (const g of layout.groups)
29751
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30256
+ function flatPolys(layout) {
30257
+ const key = layout.edges;
30258
+ const hit = FLAT_CACHE.get(key);
30259
+ if (hit) return hit;
29752
30260
  const polys = layout.edges.map((e) => {
29753
30261
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29754
30262
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29758,8 +30266,24 @@ function countSplineCrossings(layout) {
29758
30266
  if (p.y < y0) y0 = p.y;
29759
30267
  if (p.y > y1) y1 = p.y;
29760
30268
  }
29761
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30269
+ return { pts, x0, y0, x1, y1 };
29762
30270
  });
30271
+ FLAT_CACHE.set(key, polys);
30272
+ return polys;
30273
+ }
30274
+ function segPoint(p1, p2, p3, p4) {
30275
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30276
+ if (Math.abs(den) < 1e-9) return null;
30277
+ 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;
30278
+ 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;
30279
+ }
30280
+ function countSplineCrossings(layout, floor = Infinity) {
30281
+ const center = /* @__PURE__ */ new Map();
30282
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30283
+ for (const g of layout.groups)
30284
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30285
+ const polys = flatPolys(layout);
30286
+ const edges = layout.edges;
29763
30287
  const R = 34;
29764
30288
  let total = 0;
29765
30289
  for (let a = 0; a < polys.length; a++)
@@ -29767,23 +30291,33 @@ function countSplineCrossings(layout) {
29767
30291
  const A = polys[a], B = polys[b];
29768
30292
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29769
30293
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29770
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30294
+ const ea = edges[a], eb = edges[b];
30295
+ let sh0, sh1;
30296
+ if (ea.source === eb.source || ea.source === eb.target)
30297
+ sh0 = center.get(ea.source);
30298
+ if (ea.target === eb.source || ea.target === eb.target)
30299
+ sh1 = center.get(ea.target);
29771
30300
  const hits = [];
29772
- for (let i = 1; i < A.pts.length; i++)
29773
- for (let j = 1; j < B.pts.length; j++) {
29774
- const p = segPoint(
29775
- A.pts[i - 1],
29776
- A.pts[i],
29777
- B.pts[j - 1],
29778
- B.pts[j]
29779
- );
30301
+ const ap = A.pts, bp = B.pts;
30302
+ for (let i = 1; i < ap.length; i++) {
30303
+ const a0 = ap[i - 1], a1 = ap[i];
30304
+ 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;
30305
+ for (let j = 1; j < bp.length; j++) {
30306
+ const b0 = bp[j - 1], b1 = bp[j];
30307
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30308
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30309
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30310
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30311
+ const p = segPoint(a0, a1, b0, b1);
29780
30312
  if (!p) continue;
29781
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29782
- continue;
30313
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30314
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29783
30315
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29784
30316
  hits.push(p);
29785
30317
  }
30318
+ }
29786
30319
  total += hits.length;
30320
+ if (total > floor) return total;
29787
30321
  }
29788
30322
  return total;
29789
30323
  }
@@ -29821,17 +30355,8 @@ function detectEdgeOverlaps(layout, opts) {
29821
30355
  w: g.width,
29822
30356
  h: g.height
29823
30357
  });
29824
- const polys = layout.edges.map((e) => {
29825
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29826
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29827
- for (const p of pts) {
29828
- if (p.x < x0) x0 = p.x;
29829
- if (p.x > x1) x1 = p.x;
29830
- if (p.y < y0) y0 = p.y;
29831
- if (p.y > y1) y1 = p.y;
29832
- }
29833
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29834
- });
30358
+ const polys = flatPolys(layout);
30359
+ const edges = layout.edges;
29835
30360
  const runs = [];
29836
30361
  for (let a = 0; a < polys.length; a++)
29837
30362
  for (let b = a + 1; b < polys.length; b++) {
@@ -29839,7 +30364,12 @@ function detectEdgeOverlaps(layout, opts) {
29839
30364
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29840
30365
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29841
30366
  continue;
29842
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30367
+ const ea = edges[a], eb = edges[b];
30368
+ let shr0, shr1;
30369
+ if (ea.source === eb.source || ea.source === eb.target)
30370
+ shr0 = rect.get(ea.source);
30371
+ if (ea.target === eb.source || ea.target === eb.target)
30372
+ shr1 = rect.get(ea.target);
29843
30373
  let run2 = [];
29844
30374
  let runLen = 0;
29845
30375
  const flush = () => {
@@ -29853,7 +30383,7 @@ function detectEdgeOverlaps(layout, opts) {
29853
30383
  runLen = 0;
29854
30384
  };
29855
30385
  for (const p of A.pts) {
29856
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30386
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
29857
30387
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29858
30388
  if (covered) {
29859
30389
  if (run2.length)
@@ -29888,9 +30418,10 @@ function detectEdgeNodePierces(layout, opts) {
29888
30418
  });
29889
30419
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
29890
30420
  const out = [];
30421
+ const polys = flatPolys(layout);
29891
30422
  layout.edges.forEach((e, idx) => {
29892
30423
  if (e.points.length < 2) return;
29893
- const poly = flatten(splineGen(e.points) ?? "");
30424
+ const poly = polys[idx].pts;
29894
30425
  for (const r of rects) {
29895
30426
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29896
30427
  continue;
@@ -30237,8 +30768,10 @@ function edgeLength(layout) {
30237
30768
  );
30238
30769
  return total;
30239
30770
  }
30240
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30771
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30241
30772
  const hideDescriptions = opts?.hideDescriptions ?? false;
30773
+ const onProgress = opts?.onProgress;
30774
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30242
30775
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30243
30776
  if (collapseInfo) {
30244
30777
  const missing = /* @__PURE__ */ new Set();
@@ -30652,17 +31185,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30652
31185
  seed: s
30653
31186
  });
30654
31187
  const badness = (lay, floor) => {
30655
- const x = countSplineCrossings(lay);
31188
+ const x = countSplineCrossings(lay, floor);
30656
31189
  if (x > floor) return Infinity;
30657
31190
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30658
31191
  };
30659
31192
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31193
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31194
+ let progressDone = 0;
31195
+ const step = async (phase) => {
31196
+ if (!onProgress) return;
31197
+ onProgress(++progressDone, progressTotal, phase);
31198
+ await tick();
31199
+ };
30660
31200
  const pool = [];
30661
31201
  for (const cfg of configs) {
30662
31202
  try {
30663
31203
  pool.push(place(cfg));
30664
31204
  } catch {
30665
31205
  }
31206
+ await step("Optimizing layout");
30666
31207
  }
30667
31208
  if (!pool.length)
30668
31209
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30671,9 +31212,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30671
31212
  layered = layeredCandidates(parsed, sizes);
30672
31213
  } catch {
30673
31214
  }
30674
- pool.sort(
30675
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30676
- );
31215
+ const fastKey = /* @__PURE__ */ new Map();
31216
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31217
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30677
31218
  const refineK = Math.min(REFINE_K, pool.length);
30678
31219
  let best = pool[0];
30679
31220
  let bestObj = Infinity;
@@ -30688,7 +31229,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30688
31229
  best = lay;
30689
31230
  }
30690
31231
  };
30691
- for (const lay of pool.slice(0, refineK)) consider(lay);
31232
+ for (const lay of pool.slice(0, refineK)) {
31233
+ consider(lay);
31234
+ await step("Refining layout");
31235
+ }
30692
31236
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30693
31237
  const extra = [];
30694
31238
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30704,9 +31248,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30704
31248
  } catch {
30705
31249
  }
30706
31250
  }
30707
- extra.sort(
30708
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30709
- );
31251
+ const extraKey = /* @__PURE__ */ new Map();
31252
+ for (const lay of extra)
31253
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31254
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30710
31255
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30711
31256
  }
30712
31257
  for (const lay of layered) {
@@ -30733,7 +31278,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30733
31278
  }
30734
31279
  return best;
30735
31280
  }
30736
- var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31281
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30737
31282
  var init_layout_search = __esm({
30738
31283
  "src/boxes-and-lines/layout-search.ts"() {
30739
31284
  "use strict";
@@ -30747,6 +31292,8 @@ var init_layout_search = __esm({
30747
31292
  ESCALATE_SEEDS = 18;
30748
31293
  ESCALATE_REFINE = 10;
30749
31294
  splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
31295
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31296
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30750
31297
  GROUP_LABEL_ZONE2 = 32;
30751
31298
  }
30752
31299
  });
@@ -30831,12 +31378,15 @@ function computeNodeSize(node, reserveValueRow) {
30831
31378
  }
30832
31379
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30833
31380
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30834
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31381
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30835
31382
  ...layoutOptions?.hideDescriptions !== void 0 && {
30836
31383
  hideDescriptions: layoutOptions.hideDescriptions
30837
31384
  },
30838
31385
  ...layoutOptions?.previousPositions !== void 0 && {
30839
31386
  previousPositions: layoutOptions.previousPositions
31387
+ },
31388
+ ...layoutOptions?.onProgress !== void 0 && {
31389
+ onProgress: layoutOptions.onProgress
30840
31390
  }
30841
31391
  });
30842
31392
  return attachNotes(
@@ -31620,7 +32170,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31620
32170
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31621
32171
  const availWidth = containerWidth;
31622
32172
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31623
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32173
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32174
+ availWidth,
32175
+ layout.width,
32176
+ availHeight,
32177
+ layout.height
32178
+ );
31624
32179
  let renderLayout = layout;
31625
32180
  if (ctx.factor < 1) {
31626
32181
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31629,17 +32184,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31629
32184
  hiddenCounts.set(n.id, n.hiddenCount);
31630
32185
  }
31631
32186
  }
31632
- renderLayout = layoutMindmap(parsed, palette, {
32187
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31633
32188
  interactive: !isExport,
31634
32189
  ...hiddenCounts.size > 0 && { hiddenCounts },
31635
32190
  activeTagGroup: activeTagGroup ?? null,
31636
32191
  ...hideDescriptions !== void 0 && { hideDescriptions },
31637
- ctx
32192
+ ctx: c
31638
32193
  });
32194
+ renderLayout = relayout(ctx);
32195
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32196
+ const refit = Math.min(
32197
+ availWidth / renderLayout.width,
32198
+ availHeight / renderLayout.height
32199
+ );
32200
+ if (refit >= 0.999) break;
32201
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32202
+ renderLayout = relayout(ctx);
32203
+ }
31639
32204
  }
31640
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31641
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31642
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32205
+ const fitScale = isExport ? 1 : Math.min(
32206
+ 1,
32207
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32208
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32209
+ );
32210
+ const scaledWidth = renderLayout.width * fitScale;
32211
+ const scaledHeight = renderLayout.height * fitScale;
32212
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32213
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32214
+ const mainG = svg.append("g").attr(
32215
+ "transform",
32216
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32217
+ );
31643
32218
  if (ctx.isBelowFloor) {
31644
32219
  svg.attr("width", "100%");
31645
32220
  }
@@ -40982,7 +41557,7 @@ __export(renderer_exports11, {
40982
41557
  renderPertForExport: () => renderPertForExport
40983
41558
  });
40984
41559
  function analysisBlockChrome(palette, isDark) {
40985
- const surfaceBg = isDark ? palette.surface : palette.bg;
41560
+ const surfaceBg = themeBaseBg(palette, isDark);
40986
41561
  return {
40987
41562
  fill: mix(palette.surface, palette.bg, 40),
40988
41563
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -45694,7 +46269,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45694
46269
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45695
46270
  const gw = group.width + sGroupExtraPadding * 2;
45696
46271
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45697
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46272
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45698
46273
  const strokeColor = group.color ?? palette.textMuted;
45699
46274
  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");
45700
46275
  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");
@@ -49667,6 +50242,16 @@ __export(resolver_exports, {
49667
50242
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49668
50243
  resolveMap: () => resolveMap
49669
50244
  });
50245
+ function containerOvershoot(span, usOriented) {
50246
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50247
+ return Math.max(
50248
+ CONTAINER_OVERSHOOT_MIN,
50249
+ Math.min(
50250
+ CONTAINER_OVERSHOOT_MAX,
50251
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50252
+ )
50253
+ );
50254
+ }
49670
50255
  function bboxArea(b) {
49671
50256
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49672
50257
  }
@@ -50163,7 +50748,7 @@ function resolveMap(parsed, data) {
50163
50748
  const containerUnion = unionExtent(containerBoxes, points);
50164
50749
  if (containerUnion)
50165
50750
  extent3 = pad(
50166
- clampContainerToCluster(containerUnion, points),
50751
+ clampContainerToCluster(containerUnion, points, usOriented),
50167
50752
  PAD_FRACTION
50168
50753
  );
50169
50754
  }
@@ -50247,17 +50832,21 @@ function mostCommonCountry(regions, poiCountries) {
50247
50832
  }
50248
50833
  return best;
50249
50834
  }
50250
- function clampContainerToCluster(container, points) {
50835
+ function clampContainerToCluster(container, points, usOriented) {
50251
50836
  const poi = unionExtent([], points);
50252
50837
  if (!poi) return container;
50253
50838
  let [[west, south], [east, north]] = container;
50254
50839
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50255
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50256
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50257
- if (east <= 180 && pEast <= 180) {
50258
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50259
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50260
- }
50840
+ const over = containerOvershoot(
50841
+ Math.max(pEast - pWest, pNorth - pSouth),
50842
+ usOriented
50843
+ );
50844
+ south = Math.max(south, pSouth - over);
50845
+ north = Math.min(north, pNorth + over);
50846
+ const wOver = pWest - over;
50847
+ const eOver = pEast + over;
50848
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
50849
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
50261
50850
  return [
50262
50851
  [west, south],
50263
50852
  [east, north]
@@ -50275,7 +50864,7 @@ function firstError(diags) {
50275
50864
  const e = diags.find((d) => d.severity === "error");
50276
50865
  return e ? formatDgmoError(e) : null;
50277
50866
  }
50278
- 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;
50867
+ 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;
50279
50868
  var init_resolver2 = __esm({
50280
50869
  "src/map/resolver.ts"() {
50281
50870
  "use strict";
@@ -50288,7 +50877,9 @@ var init_resolver2 = __esm({
50288
50877
  WORLD_LAT_SOUTH = -58;
50289
50878
  WORLD_LAT_NORTH = 78;
50290
50879
  POI_ZOOM_FLOOR_DEG = 7;
50291
- CONTAINER_OVERSHOOT_DEG = 8;
50880
+ CONTAINER_OVERSHOOT_MAX = 8;
50881
+ CONTAINER_OVERSHOOT_MIN = 3;
50882
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50292
50883
  US_NATIONAL_LON_SPAN = 48;
50293
50884
  CONUS_BBOX = [
50294
50885
  [-125, 25],
@@ -50454,12 +51045,12 @@ function tierBand(maxSpanDeg) {
50454
51045
  }
50455
51046
  function labelBudget(width, height, band) {
50456
51047
  const bandCap = {
50457
- world: 7,
50458
- continental: 6,
50459
- regional: 5,
50460
- local: 4
51048
+ world: 10,
51049
+ continental: 9,
51050
+ regional: 7,
51051
+ local: 6
50461
51052
  };
50462
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51053
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50463
51054
  return Math.max(0, Math.min(area2, bandCap[band]));
50464
51055
  }
50465
51056
  function waterEligible(tier, kind, band) {
@@ -50513,6 +51104,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50513
51104
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50514
51105
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50515
51106
  }
51107
+ function rectDist2(px, py, x0, y0, x1, y1) {
51108
+ const dx = Math.max(x0 - px, 0, px - x1);
51109
+ const dy = Math.max(y0 - py, 0, py - y1);
51110
+ return dx * dx + dy * dy;
51111
+ }
50516
51112
  function rectFits(r, width, height) {
50517
51113
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50518
51114
  }
@@ -50531,6 +51127,7 @@ function placeContextLabels(args) {
50531
51127
  palette,
50532
51128
  project,
50533
51129
  collides,
51130
+ contentPoints,
50534
51131
  overLand
50535
51132
  } = args;
50536
51133
  void projection;
@@ -50601,8 +51198,17 @@ function placeContextLabels(args) {
50601
51198
  const [x0, y0, x1, y1] = c.bbox;
50602
51199
  const w = x1 - x0;
50603
51200
  const h = y1 - y0;
50604
- return { c, w, h, area: w * h };
50605
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51201
+ let dist = Infinity;
51202
+ if (contentPoints?.length) {
51203
+ for (const p of contentPoints) {
51204
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51205
+ if (d < dist) dist = d;
51206
+ }
51207
+ }
51208
+ return { c, w, h, area: w * h, dist };
51209
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51210
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51211
+ );
50606
51212
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50607
51213
  let ci = 0;
50608
51214
  for (const r of ranked) {
@@ -50619,7 +51225,7 @@ function placeContextLabels(args) {
50619
51225
  );
50620
51226
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50621
51227
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50622
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51228
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50623
51229
  const text = c.name;
50624
51230
  const tw = labelWidth(text, 0, fontSize);
50625
51231
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50632,6 +51238,9 @@ function placeContextLabels(args) {
50632
51238
  letterSpacing: 0,
50633
51239
  color,
50634
51240
  fontSize,
51241
+ // Multi-position dodging: carry the ordered interior positions through to the
51242
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51243
+ ...c.positions ? { positions: c.positions } : {},
50635
51244
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50636
51245
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50637
51246
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50646,39 +51255,53 @@ function placeContextLabels(args) {
50646
51255
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50647
51256
  const waterCap = budget - Math.min(2, countryCount);
50648
51257
  let waterPlaced = 0;
50649
- for (const cand of candidates) {
50650
- if (placed.length >= budget) break;
50651
- if (cand.italic && waterPlaced >= waterCap) continue;
51258
+ const gateAt = (cx, cy, cand) => {
50652
51259
  const rect = rectAround(
50653
- cand.cx,
50654
- cand.cy,
51260
+ cx,
51261
+ cy,
50655
51262
  cand.lines,
50656
51263
  cand.letterSpacing,
50657
51264
  cand.fontSize
50658
51265
  );
50659
- if (!rectFits(rect, width, height)) continue;
51266
+ if (!rectFits(rect, width, height)) return null;
50660
51267
  if (cand.italic && overLand) {
50661
51268
  const inset = 2;
50662
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51269
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50663
51270
  const touchesLand = cand.lines.some((line11, li) => {
50664
51271
  const lw = labelWidth(line11, cand.letterSpacing);
50665
- const x0 = cand.cx - lw / 2 + inset;
50666
- const x1 = cand.cx + lw / 2 - inset;
50667
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51272
+ const x0 = cx - lw / 2 + inset;
51273
+ const x1 = cx + lw / 2 - inset;
51274
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
50668
51275
  const base = top + li * LINE_HEIGHT;
50669
51276
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50670
51277
  (y) => xs.some((x) => overLand(x, y))
50671
51278
  );
50672
51279
  });
50673
- if (touchesLand) continue;
51280
+ if (touchesLand) return null;
50674
51281
  }
50675
- if (collides(rect)) continue;
50676
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50677
- placedRects.push(rect);
51282
+ if (collides(rect)) return null;
51283
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51284
+ return null;
51285
+ return rect;
51286
+ };
51287
+ for (const cand of candidates) {
51288
+ if (placed.length >= budget) break;
51289
+ if (cand.italic && waterPlaced >= waterCap) continue;
51290
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51291
+ let chosen = null;
51292
+ for (const [px, py] of positions) {
51293
+ const rect = gateAt(px, py, cand);
51294
+ if (rect) {
51295
+ chosen = { x: px, y: py, rect };
51296
+ break;
51297
+ }
51298
+ }
51299
+ if (!chosen) continue;
51300
+ placedRects.push(chosen.rect);
50678
51301
  if (cand.italic) waterPlaced++;
50679
51302
  placed.push({
50680
- x: cand.cx,
50681
- y: cand.cy,
51303
+ x: chosen.x,
51304
+ y: chosen.y,
50682
51305
  text: cand.text,
50683
51306
  anchor: "middle",
50684
51307
  color: cand.color,
@@ -50696,7 +51319,7 @@ function placeContextLabels(args) {
50696
51319
  }
50697
51320
  return placed;
50698
51321
  }
50699
- 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;
51322
+ 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;
50700
51323
  var init_context_labels = __esm({
50701
51324
  "src/map/context-labels.ts"() {
50702
51325
  "use strict";
@@ -50714,6 +51337,9 @@ var init_context_labels = __esm({
50714
51337
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50715
51338
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50716
51339
  COUNTRY_FADE_MAX = 45;
51340
+ MAX_COUNTRY_POSITIONS = 4;
51341
+ COUNTRY_POS_GRID = 5;
51342
+ COUNTRY_POS_TOPN_MARGIN = 3;
50717
51343
  KIND_ORDER = {
50718
51344
  ocean: 0,
50719
51345
  sea: 1,
@@ -50796,6 +51422,84 @@ function decodeLayer(topo) {
50796
51422
  decodeCache.set(topo, out);
50797
51423
  return out;
50798
51424
  }
51425
+ function countryLabelPositions(args) {
51426
+ const { geometry, bounds, project, width, height, curated } = args;
51427
+ const w0 = bounds[0][0];
51428
+ const s0 = bounds[0][1];
51429
+ const e0 = bounds[1][0];
51430
+ const n0 = bounds[1][1];
51431
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51432
+ return mkCurated(curated, project);
51433
+ }
51434
+ const N = COUNTRY_POS_GRID;
51435
+ const onLand = [];
51436
+ const kept = [];
51437
+ for (let i = 0; i < N; i++) {
51438
+ onLand[i] = [];
51439
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51440
+ for (let j = 0; j < N; j++) {
51441
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51442
+ const land = pointInGeometry(geometry, lon, lat);
51443
+ onLand[i][j] = land;
51444
+ if (!land) continue;
51445
+ const p = project(lon, lat);
51446
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51447
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51448
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51449
+ }
51450
+ }
51451
+ if (!kept.length) return mkCurated(curated, project);
51452
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51453
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51454
+ const interiorness = (c) => {
51455
+ let n = 0;
51456
+ for (let di = -1; di <= 1; di++)
51457
+ for (let dj = -1; dj <= 1; dj++) {
51458
+ if (di === 0 && dj === 0) continue;
51459
+ const ni = c.i + di;
51460
+ const nj = c.j + dj;
51461
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51462
+ }
51463
+ return n;
51464
+ };
51465
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51466
+ const pool = [...kept];
51467
+ pool.sort((a, b) => {
51468
+ const d = interiorness(b) - interiorness(a);
51469
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51470
+ });
51471
+ const ordered = [pool.shift()];
51472
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51473
+ let bestIdx = 0;
51474
+ let bestMin = -1;
51475
+ for (let k = 0; k < pool.length; k++) {
51476
+ const c = pool[k];
51477
+ let minD = Infinity;
51478
+ for (const o of ordered) {
51479
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51480
+ if (d < minD) minD = d;
51481
+ }
51482
+ if (minD > bestMin) {
51483
+ bestMin = minD;
51484
+ bestIdx = k;
51485
+ }
51486
+ }
51487
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51488
+ }
51489
+ const grid = ordered.map((c) => ({
51490
+ lonLat: [c.lon, c.lat],
51491
+ screen: [c.sx, c.sy]
51492
+ }));
51493
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51494
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51495
+ return out;
51496
+ }
51497
+ function mkCurated(curated, project) {
51498
+ if (!curated) return [];
51499
+ const p = project(curated[0], curated[1]);
51500
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51501
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51502
+ }
50799
51503
  function projectionFor(family, extent3) {
50800
51504
  switch (family) {
50801
51505
  case "albers-usa":
@@ -51623,6 +52327,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51623
52327
  if (p[1] < minY) minY = p[1];
51624
52328
  if (p[1] > maxY) maxY = p[1];
51625
52329
  }
52330
+ r.bbox = [minX, minY, maxX, maxY];
52331
+ r.rings = rings;
51626
52332
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51627
52333
  });
51628
52334
  const fillAt = (x, y) => {
@@ -52813,6 +53519,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52813
53519
  for (const box of insets)
52814
53520
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52815
53521
  const countryCandidates = [];
53522
+ const rawCountries = [];
52816
53523
  for (const f of worldLayer.values()) {
52817
53524
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52818
53525
  if (!iso || regionById.has(iso)) continue;
@@ -52829,11 +53536,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52829
53536
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52830
53537
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52831
53538
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52832
- countryCandidates.push({
53539
+ rawCountries.push({
53540
+ f,
53541
+ iso,
52833
53542
  name: f.properties?.name ?? iso,
52834
53543
  bbox: [x0, y0, x1, y1],
52835
53544
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52836
- curatedAnchor: !!anchorLngLat
53545
+ curatedLngLat: anchorLngLat ?? null,
53546
+ area: (x1 - x0) * (y1 - y0)
53547
+ });
53548
+ }
53549
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53550
+ const cBudget = labelBudget(width, height, cBand);
53551
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53552
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53553
+ const rankOrder = rawCountries.map((r, idx) => {
53554
+ let dist = Infinity;
53555
+ const [x0, y0, x1, y1] = r.bbox;
53556
+ for (const [px, py] of contentPoints) {
53557
+ const dx = Math.max(x0 - px, 0, px - x1);
53558
+ const dy = Math.max(y0 - py, 0, py - y1);
53559
+ const d = dx * dx + dy * dy;
53560
+ if (d < dist) dist = d;
53561
+ }
53562
+ return { idx, area: r.area, dist };
53563
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53564
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53565
+ ).slice(0, topN);
53566
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53567
+ for (let i = 0; i < rawCountries.length; i++) {
53568
+ const r = rawCountries[i];
53569
+ let anchor = r.anchor;
53570
+ let positions;
53571
+ if (genIdx.has(i) && anchor) {
53572
+ const gb = (0, import_d3_geo2.geoBounds)(r.f);
53573
+ const gen = countryLabelPositions({
53574
+ geometry: r.f.geometry,
53575
+ bounds: gb,
53576
+ project,
53577
+ width,
53578
+ height,
53579
+ curated: r.curatedLngLat
53580
+ });
53581
+ if (gen.length) {
53582
+ positions = gen.map((p) => p.screen);
53583
+ anchor = positions[0];
53584
+ }
53585
+ }
53586
+ countryCandidates.push({
53587
+ name: r.name,
53588
+ bbox: r.bbox,
53589
+ anchor,
53590
+ curatedAnchor: !!r.curatedLngLat,
53591
+ ...positions ? { positions } : {}
52837
53592
  });
52838
53593
  }
52839
53594
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52867,6 +53622,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52867
53622
  palette,
52868
53623
  project,
52869
53624
  collides,
53625
+ contentPoints,
52870
53626
  // Water labels must stay over open water — `fillAt` returns the ocean
52871
53627
  // backdrop colour off-land and a region fill on-land (lakes/states count
52872
53628
  // as land here, which is the safe side for an ocean name).
@@ -53120,7 +53876,7 @@ function ringToCoastPaths(ring, frame) {
53120
53876
  function coastlineOuterRings(regions, minExtent, frame) {
53121
53877
  const paths = [];
53122
53878
  for (const r of regions) {
53123
- const rings = parsePathRings(r.d);
53879
+ const rings = r.rings ?? parsePathRings(r.d);
53124
53880
  for (let i = 0; i < rings.length; i++) {
53125
53881
  const ring = rings[i];
53126
53882
  if (ring.length < 3) continue;
@@ -53315,14 +54071,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53315
54071
  if (decoCluster !== void 0)
53316
54072
  gPatch.attr("data-cluster-deco", decoCluster);
53317
54073
  for (const r of layout.regions) {
53318
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53319
- for (const ring of parsePathRings(r.d))
53320
- for (const [px, py] of ring) {
53321
- if (px < minX) minX = px;
53322
- if (px > maxX) maxX = px;
53323
- if (py < minY) minY = py;
53324
- if (py > maxY) maxY = py;
53325
- }
54074
+ let minX, minY, maxX, maxY;
54075
+ if (r.bbox) {
54076
+ [minX, minY, maxX, maxY] = r.bbox;
54077
+ } else {
54078
+ minX = Infinity;
54079
+ minY = Infinity;
54080
+ maxX = -Infinity;
54081
+ maxY = -Infinity;
54082
+ for (const ring of parsePathRings(r.d))
54083
+ for (const [px, py] of ring) {
54084
+ if (px < minX) minX = px;
54085
+ if (px > maxX) maxX = px;
54086
+ if (py < minY) minY = py;
54087
+ if (py > maxY) maxY = py;
54088
+ }
54089
+ }
53326
54090
  const hit = blobRects.some(
53327
54091
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53328
54092
  );
@@ -54550,7 +55314,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54550
55314
  const tasksAll = [...allTasks(parsed)];
54551
55315
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54552
55316
  const solid = parsed.options["solid-fill"] === "on";
54553
- const surfaceBg = isDark ? palette.surface : palette.bg;
55317
+ const surfaceBg = themeBaseBg(palette, isDark);
54554
55318
  const roleCount = Math.max(1, parsed.roles.length);
54555
55319
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54556
55320
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54728,10 +55492,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54728
55492
  const cx = roleX(i) + sColumnInset;
54729
55493
  const cw = roleColW - 2 * sColumnInset;
54730
55494
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54731
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55495
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54732
55496
  const headerFill = mix(
54733
55497
  roleColor,
54734
- isDark ? palette.surface : palette.bg,
55498
+ themeBaseBg(palette, isDark),
54735
55499
  30
54736
55500
  );
54737
55501
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -56458,7 +57222,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56458
57222
  const groupTagColor = getTagColor(groupTagValue || void 0);
56459
57223
  const fillColor = groupTagColor ? mix(
56460
57224
  groupTagColor,
56461
- isDark ? palette.surface : palette.bg,
57225
+ themeBaseBg(palette, isDark),
56462
57226
  isDark ? 15 : 20
56463
57227
  ) : isDark ? palette.surface : palette.bg;
56464
57228
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56512,7 +57276,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56512
57276
  participantG.append("title").text("Click to expand");
56513
57277
  const pFill = effectiveTagColor ? mix(
56514
57278
  effectiveTagColor,
56515
- isDark ? palette.surface : palette.bg,
57279
+ themeBaseBg(palette, isDark),
56516
57280
  isDark ? 30 : 40
56517
57281
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56518
57282
  const pStroke = effectiveTagColor || palette.border;
@@ -56719,7 +57483,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56719
57483
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56720
57484
  const actTagColor = getTagColor(actTagValue);
56721
57485
  const actBaseColor = actTagColor || palette.primary;
56722
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57486
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
56723
57487
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56724
57488
  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");
56725
57489
  if (tagKey && actTagValue) {
@@ -58435,7 +59199,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58435
59199
  const textColor = palette.text;
58436
59200
  const mutedColor = palette.border;
58437
59201
  const bgColor = palette.bg;
58438
- const bg = isDark ? palette.surface : palette.bg;
59202
+ const bg = themeBaseBg(palette, isDark);
58439
59203
  const colors = getSeriesColors(palette);
58440
59204
  const groupColorMap = /* @__PURE__ */ new Map();
58441
59205
  timelineGroups.forEach((grp, i) => {
@@ -58850,9 +59614,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58850
59614
  drawLegend2();
58851
59615
  }
58852
59616
  }
58853
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59617
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58854
59618
  const {
58855
59619
  width,
59620
+ height,
58856
59621
  tooltip,
58857
59622
  solid,
58858
59623
  textColor,
@@ -58904,9 +59669,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58904
59669
  const rowH = ctx.structural(28);
58905
59670
  const innerHeight = rowH * sorted.length;
58906
59671
  const usedHeight = margin.top + innerHeight + margin.bottom;
59672
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59673
+ const svgHeight = fitToContainer ? height : usedHeight;
58907
59674
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58908
- 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);
58909
- if (ctx.isBelowFloor) {
59675
+ 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);
59676
+ if (ctx.isBelowFloor && !fitToContainer) {
58910
59677
  svg.attr("width", "100%");
58911
59678
  }
58912
59679
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60580,7 +61347,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60580
61347
  onClickItem
60581
61348
  );
60582
61349
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60583
- const bg = isDark ? palette.surface : palette.bg;
61350
+ const bg = themeBaseBg(palette, isDark);
60584
61351
  const getQuadrantColor = (label, defaultIdx) => {
60585
61352
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60586
61353
  };
@@ -60917,18 +61684,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60917
61684
  palette,
60918
61685
  viewState,
60919
61686
  options,
60920
- exportMode
61687
+ exportMode,
61688
+ isDark: theme === "dark"
60921
61689
  };
60922
61690
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
60923
61691
  return (handler ?? exportVisualization)(ctx);
60924
61692
  }
61693
+ function ctxTagOverride(ctx) {
61694
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61695
+ }
60925
61696
  async function exportOrg(ctx) {
60926
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61697
+ const { content, theme, palette, viewState, exportMode } = ctx;
60927
61698
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
60928
61699
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
60929
61700
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
60930
61701
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
60931
- const isDark = theme === "dark";
61702
+ const isDark = ctx.isDark;
60932
61703
  const effectivePalette = await resolveExportPalette(theme, palette);
60933
61704
  const orgParsed = parseOrg2(content, effectivePalette);
60934
61705
  if (orgParsed.error) return "";
@@ -60936,7 +61707,7 @@ async function exportOrg(ctx) {
60936
61707
  const activeTagGroup = resolveActiveTagGroup(
60937
61708
  orgParsed.tagGroups,
60938
61709
  orgParsed.options["active-tag"],
60939
- viewState?.tag ?? options?.tagGroup
61710
+ ctxTagOverride(ctx)
60940
61711
  );
60941
61712
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60942
61713
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60969,12 +61740,12 @@ async function exportOrg(ctx) {
60969
61740
  return finalizeSvgExport(container, theme, effectivePalette);
60970
61741
  }
60971
61742
  async function exportSitemap(ctx) {
60972
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61743
+ const { content, theme, palette, viewState, exportMode } = ctx;
60973
61744
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
60974
61745
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
60975
61746
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
60976
61747
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
60977
- const isDark = theme === "dark";
61748
+ const isDark = ctx.isDark;
60978
61749
  const effectivePalette = await resolveExportPalette(theme, palette);
60979
61750
  const sitemapParsed = parseSitemap2(content, effectivePalette);
60980
61751
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -60982,7 +61753,7 @@ async function exportSitemap(ctx) {
60982
61753
  const activeTagGroup = resolveActiveTagGroup(
60983
61754
  sitemapParsed.tagGroups,
60984
61755
  sitemapParsed.options["active-tag"],
60985
- viewState?.tag ?? options?.tagGroup
61756
+ ctxTagOverride(ctx)
60986
61757
  );
60987
61758
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60988
61759
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61013,7 +61784,7 @@ async function exportSitemap(ctx) {
61013
61784
  return finalizeSvgExport(container, theme, effectivePalette);
61014
61785
  }
61015
61786
  async function exportKanban(ctx) {
61016
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61787
+ const { content, theme, palette, viewState, exportMode } = ctx;
61017
61788
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
61018
61789
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
61019
61790
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61025,11 +61796,11 @@ async function exportKanban(ctx) {
61025
61796
  document.body.appendChild(container);
61026
61797
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
61027
61798
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
61028
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61799
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
61029
61800
  activeTagGroup: resolveActiveTagGroup(
61030
61801
  kanbanParsed.tagGroups,
61031
61802
  kanbanParsed.options["active-tag"],
61032
- viewState?.tag ?? options?.tagGroup
61803
+ ctxTagOverride(ctx)
61033
61804
  ),
61034
61805
  currentSwimlaneGroup: viewState?.swim ?? null,
61035
61806
  ...kanbanCollapsedLanes !== void 0 && {
@@ -61062,7 +61833,7 @@ async function exportClass(ctx) {
61062
61833
  classParsed,
61063
61834
  classLayout,
61064
61835
  effectivePalette,
61065
- theme === "dark",
61836
+ ctx.isDark,
61066
61837
  void 0,
61067
61838
  { width: exportWidth, height: exportHeight },
61068
61839
  void 0,
@@ -61071,7 +61842,7 @@ async function exportClass(ctx) {
61071
61842
  return finalizeSvgExport(container, theme, effectivePalette);
61072
61843
  }
61073
61844
  async function exportEr(ctx) {
61074
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61845
+ const { content, theme, palette, viewState, exportMode } = ctx;
61075
61846
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
61076
61847
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
61077
61848
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -61089,13 +61860,13 @@ async function exportEr(ctx) {
61089
61860
  erParsed,
61090
61861
  erLayout,
61091
61862
  effectivePalette,
61092
- theme === "dark",
61863
+ ctx.isDark,
61093
61864
  void 0,
61094
61865
  { width: exportWidth, height: exportHeight },
61095
61866
  resolveActiveTagGroup(
61096
61867
  erParsed.tagGroups,
61097
61868
  erParsed.options["active-tag"],
61098
- viewState?.tag ?? options?.tagGroup
61869
+ ctxTagOverride(ctx)
61099
61870
  ),
61100
61871
  viewState?.sem,
61101
61872
  exportMode
@@ -61103,7 +61874,7 @@ async function exportEr(ctx) {
61103
61874
  return finalizeSvgExport(container, theme, effectivePalette);
61104
61875
  }
61105
61876
  async function exportBoxesAndLines(ctx) {
61106
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61877
+ const { content, theme, palette, viewState, exportMode } = ctx;
61107
61878
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
61108
61879
  const effectivePalette = await resolveExportPalette(theme, palette);
61109
61880
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -61123,13 +61894,13 @@ async function exportBoxesAndLines(ctx) {
61123
61894
  const exportWidth = blLayout.width + PADDING3 * 2;
61124
61895
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
61125
61896
  const container = createExportContainer(exportWidth, exportHeight);
61126
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61897
+ const blActiveTagGroup = ctxTagOverride(ctx);
61127
61898
  renderBoxesAndLinesForExport2(
61128
61899
  container,
61129
61900
  blParsed,
61130
61901
  blLayout,
61131
61902
  effectivePalette,
61132
- theme === "dark",
61903
+ ctx.isDark,
61133
61904
  {
61134
61905
  exportDims: { width: exportWidth, height: exportHeight },
61135
61906
  ...blActiveTagGroup !== void 0 && {
@@ -61144,12 +61915,12 @@ async function exportBoxesAndLines(ctx) {
61144
61915
  return finalizeSvgExport(container, theme, effectivePalette);
61145
61916
  }
61146
61917
  async function exportMindmap(ctx) {
61147
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61918
+ const { content, theme, palette, viewState, exportMode } = ctx;
61148
61919
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
61149
61920
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
61150
61921
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
61151
61922
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
61152
- const isDark = theme === "dark";
61923
+ const isDark = ctx.isDark;
61153
61924
  const effectivePalette = await resolveExportPalette(theme, palette);
61154
61925
  const mmParsed = parseMindmap2(content, effectivePalette);
61155
61926
  if (mmParsed.error) return "";
@@ -61157,7 +61928,7 @@ async function exportMindmap(ctx) {
61157
61928
  const activeTagGroup = resolveActiveTagGroup(
61158
61929
  mmParsed.tagGroups,
61159
61930
  mmParsed.options["active-tag"],
61160
- viewState?.tag ?? options?.tagGroup
61931
+ ctxTagOverride(ctx)
61161
61932
  );
61162
61933
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
61163
61934
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61207,7 +61978,7 @@ async function exportWireframe(ctx) {
61207
61978
  wireframeParsed,
61208
61979
  wireframeLayout,
61209
61980
  effectivePalette,
61210
- theme === "dark",
61981
+ ctx.isDark,
61211
61982
  void 0,
61212
61983
  { width: exportWidth, height: exportHeight },
61213
61984
  theme
@@ -61215,7 +61986,7 @@ async function exportWireframe(ctx) {
61215
61986
  return finalizeSvgExport(container, theme, effectivePalette);
61216
61987
  }
61217
61988
  async function exportC4(ctx) {
61218
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61989
+ const { content, theme, palette, viewState, exportMode } = ctx;
61219
61990
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61220
61991
  const {
61221
61992
  layoutC4Context: layoutC4Context2,
@@ -61227,9 +61998,9 @@ async function exportC4(ctx) {
61227
61998
  const effectivePalette = await resolveExportPalette(theme, palette);
61228
61999
  const c4Parsed = parseC42(content, effectivePalette);
61229
62000
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61230
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61231
- const c4System = options?.c4System ?? viewState?.c4s;
61232
- const c4Container = options?.c4Container ?? viewState?.c4c;
62001
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62002
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62003
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61233
62004
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61234
62005
  if (c4Layout.nodes.length === 0) return "";
61235
62006
  const PADDING3 = 20;
@@ -61243,13 +62014,13 @@ async function exportC4(ctx) {
61243
62014
  c4Parsed,
61244
62015
  c4Layout,
61245
62016
  effectivePalette,
61246
- theme === "dark",
62017
+ ctx.isDark,
61247
62018
  void 0,
61248
62019
  { width: exportWidth, height: exportHeight },
61249
62020
  resolveActiveTagGroup(
61250
62021
  c4Parsed.tagGroups,
61251
62022
  c4Parsed.options["active-tag"],
61252
- viewState?.tag ?? options?.tagGroup
62023
+ ctxTagOverride(ctx)
61253
62024
  ),
61254
62025
  exportMode
61255
62026
  );
@@ -61270,14 +62041,14 @@ async function exportFlowchart(ctx) {
61270
62041
  fcParsed,
61271
62042
  layout,
61272
62043
  effectivePalette,
61273
- theme === "dark",
62044
+ ctx.isDark,
61274
62045
  void 0,
61275
62046
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61276
62047
  );
61277
62048
  return finalizeSvgExport(container, theme, effectivePalette);
61278
62049
  }
61279
62050
  async function exportInfra(ctx) {
61280
- const { content, theme, palette, viewState, options } = ctx;
62051
+ const { content, theme, palette, viewState } = ctx;
61281
62052
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61282
62053
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61283
62054
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61290,7 +62061,7 @@ async function exportInfra(ctx) {
61290
62061
  const activeTagGroup = resolveActiveTagGroup(
61291
62062
  infraParsed.tagGroups,
61292
62063
  infraParsed.options["active-tag"],
61293
- viewState?.tag ?? options?.tagGroup
62064
+ ctxTagOverride(ctx)
61294
62065
  );
61295
62066
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61296
62067
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61308,7 +62079,7 @@ async function exportInfra(ctx) {
61308
62079
  container,
61309
62080
  infraLayout,
61310
62081
  effectivePalette,
61311
- theme === "dark",
62082
+ ctx.isDark,
61312
62083
  showInfraTitle ? infraParsed.title : null,
61313
62084
  showInfraTitle ? infraParsed.titleLineNumber : null,
61314
62085
  infraTagGroups,
@@ -61355,7 +62126,7 @@ async function exportPert(ctx) {
61355
62126
  pertResolved,
61356
62127
  pertLayout,
61357
62128
  effectivePalette,
61358
- theme === "dark",
62129
+ ctx.isDark,
61359
62130
  {
61360
62131
  title: pertParsed.title,
61361
62132
  exportDims: { width: exportW, height: exportH },
@@ -61368,7 +62139,7 @@ async function exportPert(ctx) {
61368
62139
  return finalizeSvgExport(container, theme, effectivePalette);
61369
62140
  }
61370
62141
  async function exportGantt(ctx) {
61371
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62142
+ const { content, theme, palette, viewState, exportMode } = ctx;
61372
62143
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61373
62144
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61374
62145
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61386,7 +62157,7 @@ async function exportGantt(ctx) {
61386
62157
  container,
61387
62158
  resolved,
61388
62159
  effectivePalette,
61389
- theme === "dark",
62160
+ ctx.isDark,
61390
62161
  {
61391
62162
  ...ganttCollapsedGroups !== void 0 && {
61392
62163
  collapsedGroups: ganttCollapsedGroups
@@ -61400,7 +62171,7 @@ async function exportGantt(ctx) {
61400
62171
  currentActiveGroup: resolveActiveTagGroup(
61401
62172
  resolved.tagGroups,
61402
62173
  resolved.options.activeTag ?? void 0,
61403
- viewState?.tag ?? options?.tagGroup
62174
+ ctxTagOverride(ctx)
61404
62175
  ),
61405
62176
  exportMode
61406
62177
  },
@@ -61423,7 +62194,7 @@ async function exportState(ctx) {
61423
62194
  stateParsed,
61424
62195
  layout,
61425
62196
  effectivePalette,
61426
- theme === "dark",
62197
+ ctx.isDark,
61427
62198
  void 0,
61428
62199
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61429
62200
  );
@@ -61443,7 +62214,7 @@ async function exportTechRadar(ctx) {
61443
62214
  container,
61444
62215
  radarParsed,
61445
62216
  effectivePalette,
61446
- theme === "dark",
62217
+ ctx.isDark,
61447
62218
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61448
62219
  viewState,
61449
62220
  exportMode
@@ -61460,13 +62231,13 @@ async function exportJourneyMap(ctx) {
61460
62231
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61461
62232
  return "";
61462
62233
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61463
- isDark: theme === "dark"
62234
+ isDark: ctx.isDark
61464
62235
  });
61465
62236
  const container = createExportContainer(
61466
62237
  jmLayout.totalWidth,
61467
62238
  jmLayout.totalHeight
61468
62239
  );
61469
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62240
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61470
62241
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61471
62242
  exportMode
61472
62243
  });
@@ -61484,7 +62255,7 @@ async function exportCycle(ctx) {
61484
62255
  container,
61485
62256
  cycleParsed,
61486
62257
  effectivePalette,
61487
- theme === "dark",
62258
+ ctx.isDark,
61488
62259
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61489
62260
  viewState,
61490
62261
  exportMode
@@ -61521,7 +62292,7 @@ async function exportMap(ctx) {
61521
62292
  mapResolved,
61522
62293
  mapData,
61523
62294
  effectivePalette,
61524
- theme === "dark",
62295
+ ctx.isDark,
61525
62296
  dims
61526
62297
  );
61527
62298
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61538,7 +62309,7 @@ async function exportPyramid(ctx) {
61538
62309
  container,
61539
62310
  pyramidParsed,
61540
62311
  effectivePalette,
61541
- theme === "dark",
62312
+ ctx.isDark,
61542
62313
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61543
62314
  );
61544
62315
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61555,7 +62326,7 @@ async function exportRing(ctx) {
61555
62326
  container,
61556
62327
  ringParsed,
61557
62328
  effectivePalette,
61558
- theme === "dark",
62329
+ ctx.isDark,
61559
62330
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61560
62331
  );
61561
62332
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61572,7 +62343,7 @@ async function exportRaci(ctx) {
61572
62343
  container,
61573
62344
  raciParsed,
61574
62345
  effectivePalette,
61575
- theme === "dark",
62346
+ ctx.isDark,
61576
62347
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61577
62348
  );
61578
62349
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61595,7 +62366,7 @@ async function exportSlope(ctx) {
61595
62366
  container,
61596
62367
  parsed,
61597
62368
  effectivePalette,
61598
- theme === "dark",
62369
+ ctx.isDark,
61599
62370
  void 0,
61600
62371
  dims
61601
62372
  );
@@ -61611,14 +62382,14 @@ async function exportArc(ctx) {
61611
62382
  container,
61612
62383
  parsed,
61613
62384
  effectivePalette,
61614
- theme === "dark",
62385
+ ctx.isDark,
61615
62386
  void 0,
61616
62387
  dims
61617
62388
  );
61618
62389
  return finalizeSvgExport(container, theme, effectivePalette);
61619
62390
  }
61620
62391
  async function exportTimeline(ctx) {
61621
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62392
+ const { content, theme, palette, viewState, exportMode } = ctx;
61622
62393
  const parsed = parseTimeline(content, palette);
61623
62394
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61624
62395
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61627,13 +62398,13 @@ async function exportTimeline(ctx) {
61627
62398
  container,
61628
62399
  parsed,
61629
62400
  effectivePalette,
61630
- theme === "dark",
62401
+ ctx.isDark,
61631
62402
  void 0,
61632
62403
  dims,
61633
62404
  resolveActiveTagGroup(
61634
62405
  parsed.timelineTagGroups,
61635
62406
  parsed.timelineActiveTag,
61636
- viewState?.tag ?? options?.tagGroup
62407
+ ctxTagOverride(ctx)
61637
62408
  ),
61638
62409
  viewState?.swim,
61639
62410
  void 0,
@@ -61652,7 +62423,7 @@ async function exportWordcloud(ctx) {
61652
62423
  container,
61653
62424
  parsed,
61654
62425
  effectivePalette,
61655
- theme === "dark",
62426
+ ctx.isDark,
61656
62427
  dims
61657
62428
  );
61658
62429
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61667,7 +62438,7 @@ async function exportVenn(ctx) {
61667
62438
  container,
61668
62439
  parsed,
61669
62440
  effectivePalette,
61670
- theme === "dark",
62441
+ ctx.isDark,
61671
62442
  void 0,
61672
62443
  dims
61673
62444
  );
@@ -61683,14 +62454,14 @@ async function exportQuadrant(ctx) {
61683
62454
  container,
61684
62455
  parsed,
61685
62456
  effectivePalette,
61686
- theme === "dark",
62457
+ ctx.isDark,
61687
62458
  void 0,
61688
62459
  dims
61689
62460
  );
61690
62461
  return finalizeSvgExport(container, theme, effectivePalette);
61691
62462
  }
61692
62463
  async function exportVisualization(ctx) {
61693
- const { content, theme, palette, viewState, options } = ctx;
62464
+ const { content, theme, palette, viewState } = ctx;
61694
62465
  const parsed = parseVisualization(content, palette);
61695
62466
  if (parsed.type !== "sequence") {
61696
62467
  if (parsed.error) {
@@ -61701,7 +62472,7 @@ async function exportVisualization(ctx) {
61701
62472
  }
61702
62473
  }
61703
62474
  const effectivePalette = await resolveExportPalette(theme, palette);
61704
- const isDark = theme === "dark";
62475
+ const isDark = ctx.isDark;
61705
62476
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61706
62477
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61707
62478
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61709,7 +62480,7 @@ async function exportVisualization(ctx) {
61709
62480
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61710
62481
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61711
62482
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61712
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62483
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61713
62484
  renderSequenceDiagram2(
61714
62485
  container,
61715
62486
  seqParsed,
@@ -62483,7 +63254,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
62483
63254
 
62484
63255
  // src/auto/index.ts
62485
63256
  init_safe_href();
62486
- var VERSION = "0.31.0";
63257
+ var VERSION = "0.32.0";
62487
63258
  var DEFAULTS = {
62488
63259
  theme: "auto",
62489
63260
  palette: "slate",