@diagrammo/dgmo 0.31.0 → 0.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.cursorrules +4 -1
  2. package/.github/copilot-instructions.md +4 -1
  3. package/.windsurfrules +4 -1
  4. package/SKILL.md +4 -1
  5. package/dist/advanced.cjs +1297 -358
  6. package/dist/advanced.d.cts +117 -15
  7. package/dist/advanced.d.ts +117 -15
  8. package/dist/advanced.js +1291 -358
  9. package/dist/auto.cjs +1087 -316
  10. package/dist/auto.js +98 -98
  11. package/dist/auto.mjs +1087 -316
  12. package/dist/cli.cjs +140 -140
  13. package/dist/index.cjs +1090 -397
  14. package/dist/index.js +1090 -397
  15. package/docs/ai-integration.md +4 -1
  16. package/docs/language-reference.md +282 -27
  17. package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
  18. package/gallery/fixtures/c4-full.dgmo +4 -5
  19. package/gallery/fixtures/c4.dgmo +2 -3
  20. package/package.json +7 -1
  21. package/src/advanced.ts +7 -0
  22. package/src/boxes-and-lines/focus.ts +257 -0
  23. package/src/boxes-and-lines/layout-search.ts +131 -65
  24. package/src/boxes-and-lines/layout.ts +7 -1
  25. package/src/boxes-and-lines/parser.ts +19 -4
  26. package/src/boxes-and-lines/renderer.ts +54 -3
  27. package/src/c4/parser.ts +8 -7
  28. package/src/chart-type-registry.ts +129 -4
  29. package/src/chart-types.ts +4 -4
  30. package/src/chart.ts +18 -1
  31. package/src/colors.ts +225 -2
  32. package/src/cycle/parser.ts +2 -7
  33. package/src/d3.ts +67 -54
  34. package/src/diagnostics.ts +17 -0
  35. package/src/dimensions.ts +9 -13
  36. package/src/echarts.ts +42 -14
  37. package/src/er/parser.ts +6 -1
  38. package/src/gantt/parser.ts +44 -7
  39. package/src/graph/flowchart-parser.ts +77 -3
  40. package/src/graph/state-renderer.ts +2 -2
  41. package/src/infra/parser.ts +80 -0
  42. package/src/journey-map/parser.ts +8 -7
  43. package/src/kanban/parser.ts +8 -7
  44. package/src/map/context-labels.ts +134 -27
  45. package/src/map/geo.ts +10 -2
  46. package/src/map/layout.ts +259 -4
  47. package/src/map/parser.ts +2 -0
  48. package/src/map/renderer.ts +22 -11
  49. package/src/map/resolver.ts +68 -19
  50. package/src/mindmap/parser.ts +15 -7
  51. package/src/mindmap/renderer.ts +50 -12
  52. package/src/org/parser.ts +8 -7
  53. package/src/org/renderer.ts +22 -7
  54. package/src/palettes/color-utils.ts +12 -2
  55. package/src/palettes/index.ts +1 -0
  56. package/src/pert/renderer.ts +2 -2
  57. package/src/pyramid/parser.ts +2 -7
  58. package/src/quadrant/renderer.ts +2 -2
  59. package/src/raci/parser.ts +2 -7
  60. package/src/raci/renderer.ts +4 -4
  61. package/src/ring/parser.ts +2 -7
  62. package/src/sequence/parser.ts +18 -7
  63. package/src/sequence/renderer.ts +4 -4
  64. package/src/sitemap/parser.ts +8 -7
  65. package/src/sitemap/renderer.ts +2 -2
  66. package/src/tech-radar/parser.ts +2 -7
  67. package/src/timeline/renderer.ts +15 -5
  68. package/src/utils/parsing.ts +13 -1
  69. package/src/utils/scaling.ts +38 -81
  70. package/src/utils/tag-groups.ts +38 -0
  71. package/src/visualizations/parse.ts +6 -1
  72. package/src/wireframe/parser.ts +6 -1
package/dist/index.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
@@ -4082,81 +4286,7 @@ var init_text_measure = __esm({
4082
4286
  });
4083
4287
 
4084
4288
  // src/utils/scaling.ts
4085
- function computeMinDimensions(chartType, counts) {
4086
- switch (chartType) {
4087
- case "sequence":
4088
- return {
4089
- width: Math.max((counts.participants ?? 2) * 80, 320),
4090
- height: Math.max((counts.messages ?? 1) * 20 + 120, 200)
4091
- };
4092
- case "raci":
4093
- return {
4094
- width: Math.max((counts.roles ?? 2) * 50 + 180, 300),
4095
- height: Math.max((counts.tasks ?? 1) * 28 + 80, 200)
4096
- };
4097
- case "mindmap":
4098
- return {
4099
- width: Math.max((counts.nodes ?? 3) * 30, 300),
4100
- height: Math.max((counts.depth ?? 2) * 60, 200)
4101
- };
4102
- case "tech-radar":
4103
- return { width: 360, height: 400 };
4104
- case "heatmap":
4105
- return {
4106
- width: Math.max((counts.columns ?? 3) * 40, 300),
4107
- height: Math.max((counts.rows ?? 3) * 30 + 60, 200)
4108
- };
4109
- case "arc":
4110
- return {
4111
- width: 300,
4112
- height: Math.max((counts.nodes ?? 3) * 20 + 120, 200)
4113
- };
4114
- case "org":
4115
- return {
4116
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4117
- height: Math.max((counts.depth ?? 2) * 80, 200)
4118
- };
4119
- case "gantt":
4120
- return {
4121
- width: 400,
4122
- height: Math.max((counts.tasks ?? 3) * 24 + 80, 200)
4123
- };
4124
- case "kanban":
4125
- return {
4126
- width: Math.max((counts.columns ?? 3) * 120, 360),
4127
- height: 300
4128
- };
4129
- case "er":
4130
- return {
4131
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4132
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4133
- };
4134
- case "class":
4135
- return {
4136
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4137
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4138
- };
4139
- case "flowchart":
4140
- case "state":
4141
- return {
4142
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4143
- height: Math.max((counts.nodes ?? 3) * 50, 200)
4144
- };
4145
- case "pert":
4146
- return {
4147
- width: Math.max((counts.tasks ?? 3) * 80, 340),
4148
- height: Math.max((counts.tasks ?? 3) * 40 + 80, 200)
4149
- };
4150
- case "infra":
4151
- return {
4152
- width: Math.max((counts.nodes ?? 3) * 80, 300),
4153
- height: Math.max((counts.nodes ?? 3) * 60, 200)
4154
- };
4155
- default:
4156
- return { ...DEFAULT_MIN };
4157
- }
4158
- }
4159
- var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext, DEFAULT_MIN;
4289
+ var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext;
4160
4290
  var init_scaling = __esm({
4161
4291
  "src/utils/scaling.ts"() {
4162
4292
  "use strict";
@@ -4175,6 +4305,30 @@ var init_scaling = __esm({
4175
4305
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4176
4306
  return new _ScaleContext(clamped, minScaleFactor);
4177
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
+ }
4178
4332
  static identity() {
4179
4333
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4180
4334
  }
@@ -4191,7 +4345,6 @@ var init_scaling = __esm({
4191
4345
  return Math.max(fontSize * this.factor, floor);
4192
4346
  }
4193
4347
  };
4194
- DEFAULT_MIN = { width: 300, height: 200 };
4195
4348
  }
4196
4349
  });
4197
4350
 
@@ -5876,12 +6029,7 @@ function parseSequenceDgmo(content, palette) {
5876
6029
  const trimmed = token.trim();
5877
6030
  return nameAliasMap.get(trimmed) ?? trimmed;
5878
6031
  };
5879
- const fail = (line11, message) => {
5880
- const diag = makeDgmoError(line11, message);
5881
- result.diagnostics.push(diag);
5882
- result.error = formatDgmoError(diag);
5883
- return result;
5884
- };
6032
+ const fail = makeFail(result);
5885
6033
  const pushError = (line11, message) => {
5886
6034
  const diag = makeDgmoError(line11, message);
5887
6035
  result.diagnostics.push(diag);
@@ -5896,6 +6044,7 @@ function parseSequenceDgmo(content, palette) {
5896
6044
  const lines = content.split("\n");
5897
6045
  let hasExplicitChart = false;
5898
6046
  let contentStarted = false;
6047
+ let bodyStarted = false;
5899
6048
  let firstLineIndex = -1;
5900
6049
  for (let fi = 0; fi < lines.length; fi++) {
5901
6050
  const fl = lines[fi].trim();
@@ -6207,6 +6356,7 @@ function parseSequenceDgmo(content, palette) {
6207
6356
  );
6208
6357
  }
6209
6358
  contentStarted = true;
6359
+ bodyStarted = true;
6210
6360
  const section = {
6211
6361
  kind: "section",
6212
6362
  // Capture group 1 guaranteed present after successful match.
@@ -6422,7 +6572,7 @@ function parseSequenceDgmo(content, palette) {
6422
6572
  alias: bareAlias
6423
6573
  } = splitPipe(trimmed, lineNumber);
6424
6574
  const inGroup = activeGroup && measureIndent(raw) > 0;
6425
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6575
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6426
6576
  contentStarted = true;
6427
6577
  const id = bareCore;
6428
6578
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6469,6 +6619,7 @@ function parseSequenceDgmo(content, palette) {
6469
6619
  }
6470
6620
  if (labeledArrow) {
6471
6621
  contentStarted = true;
6622
+ bodyStarted = true;
6472
6623
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6473
6624
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6474
6625
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6533,6 +6684,7 @@ function parseSequenceDgmo(content, palette) {
6533
6684
  const bareCall = bareCallSync || bareCallAsync;
6534
6685
  if (bareCall) {
6535
6686
  contentStarted = true;
6687
+ bodyStarted = true;
6536
6688
  const from = bareCall[1];
6537
6689
  const to = bareCall[2];
6538
6690
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6554,6 +6706,7 @@ function parseSequenceDgmo(content, palette) {
6554
6706
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6555
6707
  if (ifMatch) {
6556
6708
  contentStarted = true;
6709
+ bodyStarted = true;
6557
6710
  const block = {
6558
6711
  kind: "block",
6559
6712
  type: "if",
@@ -6570,6 +6723,7 @@ function parseSequenceDgmo(content, palette) {
6570
6723
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6571
6724
  if (loopMatch) {
6572
6725
  contentStarted = true;
6726
+ bodyStarted = true;
6573
6727
  const block = {
6574
6728
  kind: "block",
6575
6729
  type: "loop",
@@ -6586,6 +6740,7 @@ function parseSequenceDgmo(content, palette) {
6586
6740
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6587
6741
  if (parallelMatch) {
6588
6742
  contentStarted = true;
6743
+ bodyStarted = true;
6589
6744
  const block = {
6590
6745
  kind: "block",
6591
6746
  type: "parallel",
@@ -6682,6 +6837,7 @@ function parseSequenceDgmo(content, palette) {
6682
6837
  lineNumber,
6683
6838
  endLineNumber: lineNumber
6684
6839
  };
6840
+ bodyStarted = true;
6685
6841
  currentContainer().push(note);
6686
6842
  continue;
6687
6843
  }
@@ -6706,6 +6862,7 @@ function parseSequenceDgmo(content, palette) {
6706
6862
  endLineNumber: i + 1
6707
6863
  // i has advanced past the body lines (1-based)
6708
6864
  };
6865
+ bodyStarted = true;
6709
6866
  currentContainer().push(note);
6710
6867
  continue;
6711
6868
  }
@@ -7550,6 +7707,15 @@ function parseNodeRef(text) {
7550
7707
  }
7551
7708
  return null;
7552
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
+ }
7553
7719
  function splitArrows(line11) {
7554
7720
  const segments = [];
7555
7721
  const arrowPositions = [];
@@ -7645,6 +7811,7 @@ function parseFlowchart(content, palette) {
7645
7811
  const notes = [];
7646
7812
  let contentStarted = false;
7647
7813
  let firstLineParsed = false;
7814
+ let prevLineLastNodeId = null;
7648
7815
  const nameAliasMap = /* @__PURE__ */ new Map();
7649
7816
  function peelAlias2(seg) {
7650
7817
  const trimmed = seg.trim();
@@ -7652,6 +7819,19 @@ function parseFlowchart(content, palette) {
7652
7819
  if (!m) return { seg: trimmed };
7653
7820
  return { seg: m[1].trim(), alias: m[2] };
7654
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
+ }
7655
7835
  function getOrCreateNode(ref, lineNumber) {
7656
7836
  const key = ref.id;
7657
7837
  const existing = nodeMap.get(key);
@@ -7710,6 +7890,8 @@ function parseFlowchart(content, palette) {
7710
7890
  indentStack[indentStack.length - 1].nodeId
7711
7891
  ) : null;
7712
7892
  const segments = splitArrows(trimmed);
7893
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7894
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7713
7895
  if (segments.length === 1) {
7714
7896
  const peeled = peelAlias2(segments[0]);
7715
7897
  const ref = parseNodeRef(peeled.seg);
@@ -7719,6 +7901,13 @@ function parseFlowchart(content, palette) {
7719
7901
  indentStack.push({ nodeId: node.id, indent });
7720
7902
  return node.id;
7721
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
+ }
7722
7911
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7723
7912
  if (aliasResolved !== void 0) {
7724
7913
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7754,16 +7943,23 @@ function parseFlowchart(content, palette) {
7754
7943
  }
7755
7944
  }
7756
7945
  }
7946
+ if (!ref) {
7947
+ const loose = parseNodeRefLoose(peeled.seg);
7948
+ if (loose) {
7949
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7950
+ ref = loose.ref;
7951
+ }
7952
+ }
7757
7953
  if (!ref) continue;
7758
7954
  const node = getOrCreateNode(ref, lineNumber);
7759
7955
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7760
7956
  if (pendingArrow !== null) {
7761
- const sourceId = lastNodeId ?? implicitSourceId;
7957
+ const sourceId = lastNodeId ?? effectiveSource;
7762
7958
  if (sourceId) {
7763
7959
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7764
7960
  }
7765
7961
  pendingArrow = null;
7766
- } else if (lastNodeId === null && implicitSourceId === null) {
7962
+ } else if (lastNodeId === null && effectiveSource === null) {
7767
7963
  }
7768
7964
  lastNodeId = node.id;
7769
7965
  }
@@ -7845,7 +8041,8 @@ function parseFlowchart(content, palette) {
7845
8041
  continue;
7846
8042
  }
7847
8043
  }
7848
- processContentLine(trimmed, lineNumber, indent);
8044
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8045
+ if (lastId) prevLineLastNodeId = lastId;
7849
8046
  }
7850
8047
  if (result.nodes.length === 0 && !result.error) {
7851
8048
  const diag = makeDgmoError(
@@ -8928,7 +9125,12 @@ function parseERDiagram(content, palette) {
8928
9125
  }
8929
9126
  if (currentTagGroup && !contentStarted && indent > 0) {
8930
9127
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8931
- const { label, color } = extractColor(cleanEntry, palette);
9128
+ const { label, color } = extractColor(
9129
+ cleanEntry,
9130
+ palette,
9131
+ result.diagnostics,
9132
+ lineNumber
9133
+ );
8932
9134
  if (isDefault) {
8933
9135
  currentTagGroup.defaultValue = label;
8934
9136
  } else if (currentTagGroup.entries.length === 0) {
@@ -9246,12 +9448,7 @@ function parseOrg(content, palette) {
9246
9448
  diagnostics: [],
9247
9449
  error: null
9248
9450
  };
9249
- const fail = (line11, message) => {
9250
- const diag = makeDgmoError(line11, message);
9251
- result.diagnostics.push(diag);
9252
- result.error = formatDgmoError(diag);
9253
- return result;
9254
- };
9451
+ const fail = makeFail(result);
9255
9452
  const pushError = (line11, message) => {
9256
9453
  const diag = makeDgmoError(line11, message);
9257
9454
  result.diagnostics.push(diag);
@@ -9360,7 +9557,12 @@ function parseOrg(content, palette) {
9360
9557
  const indent2 = measureIndent(line11);
9361
9558
  if (indent2 > 0) {
9362
9559
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9363
- const { label, color } = extractColor(cleanEntry, palette);
9560
+ const { label, color } = extractColor(
9561
+ cleanEntry,
9562
+ palette,
9563
+ result.diagnostics,
9564
+ lineNumber
9565
+ );
9364
9566
  if (isDefault) {
9365
9567
  currentTagGroup.defaultValue = label;
9366
9568
  } else if (currentTagGroup.entries.length === 0) {
@@ -9646,12 +9848,7 @@ function parseSitemap(content, palette) {
9646
9848
  diagnostics: [],
9647
9849
  error: null
9648
9850
  };
9649
- const fail = (line11, message) => {
9650
- const diag = makeDgmoError(line11, message);
9651
- result.diagnostics.push(diag);
9652
- result.error = formatDgmoError(diag);
9653
- return result;
9654
- };
9851
+ const fail = makeFail(result);
9655
9852
  const pushError = (line11, message) => {
9656
9853
  const diag = makeDgmoError(line11, message);
9657
9854
  result.diagnostics.push(diag);
@@ -9752,7 +9949,12 @@ function parseSitemap(content, palette) {
9752
9949
  const indent2 = measureIndent(line11);
9753
9950
  if (indent2 > 0) {
9754
9951
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9755
- const { label, color } = extractColor(cleanEntry, palette);
9952
+ const { label, color } = extractColor(
9953
+ cleanEntry,
9954
+ palette,
9955
+ result.diagnostics,
9956
+ lineNumber
9957
+ );
9756
9958
  currentTagGroup.entries.push({
9757
9959
  value: label,
9758
9960
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11467,7 +11669,7 @@ var init_chart_types = __esm({
11467
11669
  },
11468
11670
  {
11469
11671
  id: "sequence",
11470
- description: "Message / interaction flows",
11672
+ description: "Message request and response interaction flows",
11471
11673
  fallback: true
11472
11674
  },
11473
11675
  {
@@ -11541,7 +11743,7 @@ var init_chart_types = __esm({
11541
11743
  },
11542
11744
  {
11543
11745
  id: "map",
11544
- 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"
11545
11747
  },
11546
11748
  // ── Tier 3 — Specialized analytical charts ────────────────
11547
11749
  {
@@ -11558,7 +11760,7 @@ var init_chart_types = __esm({
11558
11760
  },
11559
11761
  {
11560
11762
  id: "slope",
11561
- description: "Change for multiple things between exactly two periods"
11763
+ description: "Change between 2 time periods"
11562
11764
  },
11563
11765
  {
11564
11766
  id: "sankey",
@@ -11587,7 +11789,7 @@ var init_chart_types = __esm({
11587
11789
  // ── Tier 4 — General-purpose data charts ──────────────────
11588
11790
  {
11589
11791
  id: "bar",
11590
- description: "Categorical comparisons",
11792
+ description: "Categorical comparisons for 3 - 5 figures",
11591
11793
  fallback: true
11592
11794
  },
11593
11795
  {
@@ -11849,7 +12051,9 @@ function parseChart(content, palette) {
11849
12051
  if (dataValues) {
11850
12052
  const { label: rawLabel, color: pointColor } = extractColor(
11851
12053
  dataValues.label,
11852
- palette
12054
+ palette,
12055
+ result.diagnostics,
12056
+ lineNumber
11853
12057
  );
11854
12058
  const [first, ...rest] = dataValues.values;
11855
12059
  result.data.push({
@@ -11914,6 +12118,12 @@ function parseChart(content, palette) {
11914
12118
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11915
12119
  );
11916
12120
  }
12121
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12122
+ warn(
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
+ }
11917
12127
  if (!result.error && result.seriesNames) {
11918
12128
  const expectedCount = result.seriesNames.length;
11919
12129
  for (const dp of result.data) {
@@ -12162,7 +12372,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12162
12372
  if (!dataRow || dataRow.values.length < 2) return null;
12163
12373
  const { label: rawLabel, color: pointColor } = extractColor(
12164
12374
  dataRow.label,
12165
- palette
12375
+ palette,
12376
+ diagnostics,
12377
+ lineNumber
12166
12378
  );
12167
12379
  return {
12168
12380
  name: rawLabel,
@@ -12300,11 +12512,15 @@ function parseExtendedChartFull(content, palette) {
12300
12512
  const targetResolved = resolveSlot(rawTarget);
12301
12513
  const { label: source, color: sourceColor } = extractColor(
12302
12514
  sourceResolved,
12303
- palette
12515
+ palette,
12516
+ result.diagnostics,
12517
+ lineNumber
12304
12518
  );
12305
12519
  const { label: target, color: targetColor } = extractColor(
12306
12520
  targetResolved,
12307
- palette
12521
+ palette,
12522
+ result.diagnostics,
12523
+ lineNumber
12308
12524
  );
12309
12525
  if (sourceColor || targetColor) {
12310
12526
  if (!result.nodeColors) result.nodeColors = {};
@@ -12356,7 +12572,9 @@ function parseExtendedChartFull(content, palette) {
12356
12572
  const targetResolved = resolveSlot(dataRow2.label);
12357
12573
  const { label: target, color: targetColor } = extractColor(
12358
12574
  targetResolved,
12359
- palette
12575
+ palette,
12576
+ result.diagnostics,
12577
+ lineNumber
12360
12578
  );
12361
12579
  if (targetColor) {
12362
12580
  if (!result.nodeColors) result.nodeColors = {};
@@ -12389,7 +12607,9 @@ function parseExtendedChartFull(content, palette) {
12389
12607
  const trimmedResolved = resolveSlot(trimmed);
12390
12608
  const { label: nodeName, color: nodeColor2 } = extractColor(
12391
12609
  trimmedResolved,
12392
- palette
12610
+ palette,
12611
+ result.diagnostics,
12612
+ lineNumber
12393
12613
  );
12394
12614
  if (nodeColor2) {
12395
12615
  if (!result.nodeColors) result.nodeColors = {};
@@ -12479,8 +12699,11 @@ function parseExtendedChartFull(content, palette) {
12479
12699
  min: parseFloat(rangeMatch[1]),
12480
12700
  max: parseFloat(rangeMatch[2])
12481
12701
  };
12702
+ continue;
12703
+ }
12704
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12705
+ continue;
12482
12706
  }
12483
- continue;
12484
12707
  }
12485
12708
  }
12486
12709
  if (firstToken === "no-name") {
@@ -12540,7 +12763,9 @@ function parseExtendedChartFull(content, palette) {
12540
12763
  if (colonIndex >= 0) {
12541
12764
  const { label: fnName, color: fnColor } = extractColor(
12542
12765
  trimmed.substring(0, colonIndex).trim(),
12543
- palette
12766
+ palette,
12767
+ result.diagnostics,
12768
+ lineNumber
12544
12769
  );
12545
12770
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12546
12771
  if (!result.functions) result.functions = [];
@@ -12588,7 +12813,9 @@ function parseExtendedChartFull(content, palette) {
12588
12813
  if (dataRow?.values.length === 1) {
12589
12814
  const { label: rawLabel, color: pointColor } = extractColor(
12590
12815
  dataRow.label,
12591
- palette
12816
+ palette,
12817
+ result.diagnostics,
12818
+ lineNumber
12592
12819
  );
12593
12820
  result.data.push({
12594
12821
  label: rawLabel,
@@ -12687,11 +12914,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12687
12914
  const sc = ctx ?? ScaleContext.identity();
12688
12915
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12689
12916
  if (parsed.type === "sankey") {
12690
- const bg = isDark ? palette.surface : palette.bg;
12917
+ const bg = themeBaseBg(palette, isDark);
12691
12918
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12692
12919
  }
12693
12920
  if (parsed.type === "chord") {
12694
- const bg = isDark ? palette.surface : palette.bg;
12921
+ const bg = themeBaseBg(palette, isDark);
12695
12922
  return buildChordOption(
12696
12923
  parsed,
12697
12924
  palette,
@@ -12732,7 +12959,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12732
12959
  );
12733
12960
  }
12734
12961
  if (parsed.type === "funnel") {
12735
- const bg = isDark ? palette.surface : palette.bg;
12962
+ const bg = themeBaseBg(palette, isDark);
12736
12963
  return buildFunnelOption(
12737
12964
  parsed,
12738
12965
  palette,
@@ -13328,7 +13555,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13328
13555
  const gridLeft = parsed.ylabel ? 12 : 3;
13329
13556
  const gridRight = 4;
13330
13557
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13331
- 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;
13332
13560
  let graphic;
13333
13561
  if (showLabels && points.length > 0) {
13334
13562
  const labelPoints = [];
@@ -13468,7 +13696,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13468
13696
  }
13469
13697
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13470
13698
  const sc = ctx ?? ScaleContext.identity();
13471
- const bg = isDark ? palette.surface : palette.bg;
13699
+ const bg = themeBaseBg(palette, isDark);
13472
13700
  const heatmapRows = parsed.heatmapRows ?? [];
13473
13701
  const columns = parsed.columns ?? [];
13474
13702
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13787,7 +14015,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13787
14015
  colors,
13788
14016
  titleConfig
13789
14017
  } = buildChartCommons(parsed, palette, isDark, sc);
13790
- const bg = isDark ? palette.surface : palette.bg;
14018
+ const bg = themeBaseBg(palette, isDark);
13791
14019
  switch (parsed.type) {
13792
14020
  case "bar":
13793
14021
  return buildBarOption(
@@ -14739,12 +14967,7 @@ function parseKanban(content, palette) {
14739
14967
  diagnostics: [],
14740
14968
  error: null
14741
14969
  };
14742
- const fail = (line11, message) => {
14743
- const diag = makeDgmoError(line11, message);
14744
- result.diagnostics.push(diag);
14745
- result.error = formatDgmoError(diag);
14746
- return result;
14747
- };
14970
+ const fail = makeFail(result);
14748
14971
  const warn = (line11, message) => {
14749
14972
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14750
14973
  };
@@ -14847,7 +15070,12 @@ function parseKanban(content, palette) {
14847
15070
  const indent2 = measureIndent(line11);
14848
15071
  if (indent2 > 0) {
14849
15072
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14850
- const { label, color } = extractColor(cleanEntry, palette);
15073
+ const { label, color } = extractColor(
15074
+ cleanEntry,
15075
+ palette,
15076
+ result.diagnostics,
15077
+ lineNumber
15078
+ );
14851
15079
  if (isDefault) {
14852
15080
  currentTagGroup.defaultValue = label;
14853
15081
  } else if (currentTagGroup.entries.length === 0) {
@@ -15191,12 +15419,7 @@ function parseC4(content, palette) {
15191
15419
  if (!result.error && severity === "error")
15192
15420
  result.error = formatDgmoError(diag);
15193
15421
  };
15194
- const fail = (line11, message) => {
15195
- const diag = makeDgmoError(line11, message);
15196
- result.diagnostics.push(diag);
15197
- result.error = formatDgmoError(diag);
15198
- return result;
15199
- };
15422
+ const fail = makeFail(result);
15200
15423
  if (!content?.trim()) {
15201
15424
  return fail(0, "No content provided");
15202
15425
  }
@@ -15297,7 +15520,12 @@ function parseC4(content, palette) {
15297
15520
  const indent2 = measureIndent(line11);
15298
15521
  if (indent2 > 0) {
15299
15522
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15300
- const { label, color } = extractColor(cleanEntry, palette);
15523
+ const { label, color } = extractColor(
15524
+ cleanEntry,
15525
+ palette,
15526
+ result.diagnostics,
15527
+ lineNumber
15528
+ );
15301
15529
  if (isDefault) {
15302
15530
  currentTagGroup.defaultValue = label;
15303
15531
  } else if (currentTagGroup.entries.length === 0) {
@@ -16962,8 +17190,66 @@ function parseInfra(content) {
16962
17190
  }
16963
17191
  }
16964
17192
  validateTagGroupNames(result.tagGroups, warn, setError);
17193
+ checkReachability(result);
16965
17194
  return result;
16966
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
+ }
16967
17253
  function stripNodeDecorations(name) {
16968
17254
  let s = name.trim();
16969
17255
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17253,7 +17539,12 @@ function parseGantt(content, palette) {
17253
17539
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17254
17540
  if (eraEntryMatch) {
17255
17541
  const eraLabelRaw = eraEntryMatch[3].trim();
17256
- const eraExtracted = extractColor(eraLabelRaw, palette);
17542
+ const eraExtracted = extractColor(
17543
+ eraLabelRaw,
17544
+ palette,
17545
+ diagnostics,
17546
+ lineNumber
17547
+ );
17257
17548
  result.eras.push({
17258
17549
  startDate: eraEntryMatch[1],
17259
17550
  endDate: eraEntryMatch[2],
@@ -17275,7 +17566,12 @@ function parseGantt(content, palette) {
17275
17566
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17276
17567
  if (markerEntryMatch) {
17277
17568
  const markerLabelRaw = markerEntryMatch[2].trim();
17278
- const markerExtracted = extractColor(markerLabelRaw, palette);
17569
+ const markerExtracted = extractColor(
17570
+ markerLabelRaw,
17571
+ palette,
17572
+ diagnostics,
17573
+ lineNumber
17574
+ );
17279
17575
  result.markers.push({
17280
17576
  date: markerEntryMatch[1],
17281
17577
  label: markerExtracted.label,
@@ -17296,7 +17592,12 @@ function parseGantt(content, palette) {
17296
17592
  } else {
17297
17593
  if (COMMENT_RE.test(line11)) continue;
17298
17594
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17299
- const extracted = extractColor(cleanEntry, palette);
17595
+ const extracted = extractColor(
17596
+ cleanEntry,
17597
+ palette,
17598
+ diagnostics,
17599
+ lineNumber
17600
+ );
17300
17601
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17301
17602
  const isFirstEntry = currentTagGroup.entries.length === 0;
17302
17603
  currentTagGroup.entries.push({
@@ -17491,7 +17792,12 @@ function parseGantt(content, palette) {
17491
17792
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17492
17793
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17493
17794
  const eraLabelRaw = eraOffsetMatch[3].trim();
17494
- const eraExtracted = extractColor(eraLabelRaw, palette);
17795
+ const eraExtracted = extractColor(
17796
+ eraLabelRaw,
17797
+ palette,
17798
+ diagnostics,
17799
+ lineNumber
17800
+ );
17495
17801
  result.eras.push({
17496
17802
  startDate: "",
17497
17803
  endDate: "",
@@ -17507,7 +17813,12 @@ function parseGantt(content, palette) {
17507
17813
  if (markerOffsetMatch) {
17508
17814
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17509
17815
  const markerLabelRaw = markerOffsetMatch[2].trim();
17510
- const markerExtracted = extractColor(markerLabelRaw, palette);
17816
+ const markerExtracted = extractColor(
17817
+ markerLabelRaw,
17818
+ palette,
17819
+ diagnostics,
17820
+ lineNumber
17821
+ );
17511
17822
  result.markers.push({
17512
17823
  date: "",
17513
17824
  label: markerExtracted.label,
@@ -17581,7 +17892,12 @@ function parseGantt(content, palette) {
17581
17892
  const eraMatch = line11.match(ERA_RE);
17582
17893
  if (eraMatch) {
17583
17894
  const eraLabelRaw = eraMatch[3].trim();
17584
- const eraExtracted = extractColor(eraLabelRaw, palette);
17895
+ const eraExtracted = extractColor(
17896
+ eraLabelRaw,
17897
+ palette,
17898
+ diagnostics,
17899
+ lineNumber
17900
+ );
17585
17901
  result.eras.push({
17586
17902
  startDate: eraMatch[1],
17587
17903
  endDate: eraMatch[2],
@@ -17599,7 +17915,12 @@ function parseGantt(content, palette) {
17599
17915
  const markerMatch = line11.match(MARKER_RE);
17600
17916
  if (markerMatch) {
17601
17917
  const markerLabelRaw = markerMatch[2].trim();
17602
- const markerExtracted = extractColor(markerLabelRaw, palette);
17918
+ const markerExtracted = extractColor(
17919
+ markerLabelRaw,
17920
+ palette,
17921
+ diagnostics,
17922
+ lineNumber
17923
+ );
17603
17924
  result.markers.push({
17604
17925
  date: markerMatch[1],
17605
17926
  label: markerExtracted.label,
@@ -19071,7 +19392,7 @@ function parseBoxesAndLines(content, palette) {
19071
19392
  const trimmed = raw.trim();
19072
19393
  const indent = measureIndent2(raw);
19073
19394
  if (!trimmed || trimmed.startsWith("//")) continue;
19074
- 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)) {
19075
19396
  result.diagnostics.push(
19076
19397
  makeDgmoError(
19077
19398
  lineNum,
@@ -19200,7 +19521,12 @@ function parseBoxesAndLines(content, palette) {
19200
19521
  if (tagBlockMatch.inlineValues) {
19201
19522
  for (const rawVal of tagBlockMatch.inlineValues) {
19202
19523
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19203
- const { label, color } = extractColor(cleanVal);
19524
+ const { label, color } = extractColor(
19525
+ cleanVal,
19526
+ palette,
19527
+ result.diagnostics,
19528
+ lineNum
19529
+ );
19204
19530
  newTagGroup.entries.push({
19205
19531
  value: label,
19206
19532
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19217,7 +19543,12 @@ function parseBoxesAndLines(content, palette) {
19217
19543
  }
19218
19544
  if (currentTagGroup && !contentStarted && indent > 0) {
19219
19545
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19220
- const { label, color } = extractColor(cleanEntry);
19546
+ const { label, color } = extractColor(
19547
+ cleanEntry,
19548
+ palette,
19549
+ result.diagnostics,
19550
+ lineNum
19551
+ );
19221
19552
  currentTagGroup.entries.push({
19222
19553
  value: label,
19223
19554
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19815,7 +20146,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19815
20146
  metadata
19816
20147
  };
19817
20148
  }
19818
- var MAX_GROUP_DEPTH;
20149
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19819
20150
  var init_parser18 = __esm({
19820
20151
  "src/boxes-and-lines/parser.ts"() {
19821
20152
  "use strict";
@@ -19827,6 +20158,8 @@ var init_parser18 = __esm({
19827
20158
  init_reserved_key_registry();
19828
20159
  init_notes();
19829
20160
  MAX_GROUP_DEPTH = 2;
20161
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20162
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19830
20163
  }
19831
20164
  });
19832
20165
 
@@ -19846,12 +20179,7 @@ function parseMindmap(content, palette) {
19846
20179
  diagnostics: [],
19847
20180
  error: null
19848
20181
  };
19849
- const fail = (line11, message) => {
19850
- const diag = makeDgmoError(line11, message);
19851
- result.diagnostics.push(diag);
19852
- result.error = formatDgmoError(diag);
19853
- return result;
19854
- };
20182
+ const fail = makeFail(result);
19855
20183
  const pushError = (line11, message) => {
19856
20184
  const diag = makeDgmoError(line11, message);
19857
20185
  result.diagnostics.push(diag);
@@ -19964,7 +20292,12 @@ function parseMindmap(content, palette) {
19964
20292
  const indent2 = measureIndent(line11);
19965
20293
  if (indent2 > 0) {
19966
20294
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19967
- const { label, color } = extractColor(cleanEntry, palette);
20295
+ const { label, color } = extractColor(
20296
+ cleanEntry,
20297
+ palette,
20298
+ result.diagnostics,
20299
+ lineNumber
20300
+ );
19968
20301
  if (isDefault) {
19969
20302
  currentTagGroup.defaultValue = label;
19970
20303
  } else if (currentTagGroup.entries.length === 0) {
@@ -20041,6 +20374,7 @@ function parseMindmap(content, palette) {
20041
20374
  collectAll(result.roots);
20042
20375
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
20043
20376
  validateTagGroupNames(result.tagGroups, pushWarning);
20377
+ cascadeTagMetadata(result.roots, result.tagGroups);
20044
20378
  }
20045
20379
  if (result.roots.length === 0 && !result.error) {
20046
20380
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20647,7 +20981,12 @@ function parseWireframe(content) {
20647
20981
  }
20648
20982
  if (indent > 0 && currentTagGroup) {
20649
20983
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20650
- const { label, color } = extractColor(cleanEntry);
20984
+ const { label, color } = extractColor(
20985
+ cleanEntry,
20986
+ void 0,
20987
+ diagnostics,
20988
+ lineNumber
20989
+ );
20651
20990
  currentTagGroup.entries.push({
20652
20991
  value: label,
20653
20992
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20895,12 +21234,7 @@ function parseTechRadar(content) {
20895
21234
  diagnostics: [],
20896
21235
  error: null
20897
21236
  };
20898
- const fail = (line11, message) => {
20899
- const diag = makeDgmoError(line11, message);
20900
- result.diagnostics.push(diag);
20901
- result.error = formatDgmoError(diag);
20902
- return result;
20903
- };
21237
+ const fail = makeFail(result);
20904
21238
  const warn = (line11, message) => {
20905
21239
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20906
21240
  };
@@ -21268,12 +21602,7 @@ function parseCycle(content) {
21268
21602
  let state = "top";
21269
21603
  let currentNode = null;
21270
21604
  let currentEdge = null;
21271
- const fail = (line11, message) => {
21272
- const diag = makeDgmoError(line11, message);
21273
- result.diagnostics.push(diag);
21274
- result.error = formatDgmoError(diag);
21275
- return result;
21276
- };
21605
+ const fail = makeFail(result);
21277
21606
  const warn = (line11, message, severity = "warning") => {
21278
21607
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21279
21608
  };
@@ -21550,12 +21879,7 @@ function parseJourneyMap(content, palette) {
21550
21879
  diagnostics: [],
21551
21880
  error: null
21552
21881
  };
21553
- const fail = (line11, message) => {
21554
- const diag = makeDgmoError(line11, message);
21555
- result.diagnostics.push(diag);
21556
- result.error = formatDgmoError(diag);
21557
- return result;
21558
- };
21882
+ const fail = makeFail(result);
21559
21883
  const warn = (line11, message) => {
21560
21884
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21561
21885
  };
@@ -21704,7 +22028,12 @@ function parseJourneyMap(content, palette) {
21704
22028
  if (currentTagGroup && !contentStarted) {
21705
22029
  if (indent > 0) {
21706
22030
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21707
- const { label, color } = extractColor(cleanEntry, palette);
22031
+ const { label, color } = extractColor(
22032
+ cleanEntry,
22033
+ palette,
22034
+ result.diagnostics,
22035
+ lineNumber
22036
+ );
21708
22037
  if (isDefault) {
21709
22038
  currentTagGroup.defaultValue = label;
21710
22039
  } else if (currentTagGroup.entries.length === 0) {
@@ -22076,12 +22405,7 @@ function parsePyramid(content) {
22076
22405
  const lines = content.split("\n");
22077
22406
  let headerParsed = false;
22078
22407
  let currentLayer = null;
22079
- const fail = (line11, message) => {
22080
- const diag = makeDgmoError(line11, message);
22081
- result.diagnostics.push(diag);
22082
- result.error = formatDgmoError(diag);
22083
- return result;
22084
- };
22408
+ const fail = makeFail(result);
22085
22409
  const warn = (line11, message, severity = "warning") => {
22086
22410
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22087
22411
  };
@@ -22228,12 +22552,7 @@ function parseRing(content) {
22228
22552
  const lines = content.split("\n");
22229
22553
  let headerParsed = false;
22230
22554
  let currentLayer = null;
22231
- const fail = (line11, message) => {
22232
- const diag = makeDgmoError(line11, message);
22233
- result.diagnostics.push(diag);
22234
- result.error = formatDgmoError(diag);
22235
- return result;
22236
- };
22555
+ const fail = makeFail(result);
22237
22556
  const warn = (line11, message, severity = "warning") => {
22238
22557
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22239
22558
  };
@@ -22697,12 +23016,7 @@ function parseRaci(content, palette) {
22697
23016
  diagnostics: [],
22698
23017
  error: null
22699
23018
  };
22700
- const fail = (line11, message) => {
22701
- const diag = makeDgmoError(line11, message);
22702
- result.diagnostics.push(diag);
22703
- result.error = formatDgmoError(diag);
22704
- return result;
22705
- };
23019
+ const fail = makeFail(result);
22706
23020
  const warn = (line11, message, code) => {
22707
23021
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22708
23022
  };
@@ -23305,6 +23619,81 @@ function measureInfra(content) {
23305
23619
  const parsed = parseInfra(content);
23306
23620
  return { nodes: parsed.nodes.length };
23307
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
+ }
23308
23697
  function isExtendedChartParser(parse) {
23309
23698
  return EXTENDED_CHART_DOORS.has(parse);
23310
23699
  }
@@ -23348,33 +23737,50 @@ var init_chart_type_registry = __esm({
23348
23737
  id: "sequence",
23349
23738
  category: "diagram",
23350
23739
  parse: parseSequenceDgmo,
23351
- measure: measureSequence
23740
+ measure: measureSequence,
23741
+ minDims: minDimsSequence
23352
23742
  },
23353
23743
  {
23354
23744
  id: "flowchart",
23355
23745
  category: "diagram",
23356
23746
  parse: parseFlowchart,
23357
- measure: measureFlowchart
23747
+ measure: measureFlowchart,
23748
+ minDims: minDimsGraph
23358
23749
  },
23359
23750
  {
23360
23751
  id: "class",
23361
23752
  category: "diagram",
23362
23753
  parse: parseClassDiagram,
23363
- 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
23364
23763
  },
23365
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23366
23764
  {
23367
23765
  id: "state",
23368
23766
  category: "diagram",
23369
23767
  parse: parseState,
23370
- 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
23371
23777
  },
23372
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23373
23778
  {
23374
23779
  id: "kanban",
23375
23780
  category: "diagram",
23376
23781
  parse: parseKanban,
23377
- measure: measureKanban
23782
+ measure: measureKanban,
23783
+ minDims: minDimsKanban
23378
23784
  },
23379
23785
  { id: "c4", category: "diagram", parse: parseC4 },
23380
23786
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23382,25 +23788,40 @@ var init_chart_type_registry = __esm({
23382
23788
  id: "infra",
23383
23789
  category: "diagram",
23384
23790
  parse: parseInfra,
23385
- measure: measureInfra
23791
+ measure: measureInfra,
23792
+ minDims: minDimsInfra
23386
23793
  },
23387
23794
  {
23388
23795
  id: "gantt",
23389
23796
  category: "diagram",
23390
23797
  parse: parseGantt,
23391
- 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
23392
23807
  },
23393
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23394
23808
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23395
23809
  {
23396
23810
  id: "mindmap",
23397
23811
  category: "diagram",
23398
23812
  parse: parseMindmap,
23399
- measure: measureMindmap
23813
+ measure: measureMindmap,
23814
+ minDims: minDimsMindmap
23400
23815
  },
23401
23816
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23402
23817
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23403
- { 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
+ },
23404
23825
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23405
23826
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23406
23827
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23422,7 +23843,8 @@ var init_chart_type_registry = __esm({
23422
23843
  id: "heatmap",
23423
23844
  category: "data-chart",
23424
23845
  parse: parseHeatmap,
23425
- measure: measureHeatmap
23846
+ measure: measureHeatmap,
23847
+ minDims: minDimsHeatmap
23426
23848
  },
23427
23849
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23428
23850
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23432,7 +23854,8 @@ var init_chart_type_registry = __esm({
23432
23854
  id: "arc",
23433
23855
  category: "visualization",
23434
23856
  parse: parseArc,
23435
- measure: measureArc
23857
+ measure: measureArc,
23858
+ minDims: minDimsArc
23436
23859
  },
23437
23860
  { id: "timeline", category: "visualization", parse: parseTimeline },
23438
23861
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23442,7 +23865,8 @@ var init_chart_type_registry = __esm({
23442
23865
  id: "tech-radar",
23443
23866
  category: "visualization",
23444
23867
  parse: parseTechRadar,
23445
- measure: measureTechRadar
23868
+ measure: measureTechRadar,
23869
+ minDims: minDimsTechRadar
23446
23870
  },
23447
23871
  { id: "cycle", category: "visualization", parse: parseCycle },
23448
23872
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24663,7 +25087,7 @@ function nodeStroke(palette, nodeColor2) {
24663
25087
  }
24664
25088
  function containerFill(palette, isDark, nodeColor2) {
24665
25089
  if (nodeColor2) {
24666
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25090
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24667
25091
  }
24668
25092
  return mix(palette.surface, palette.bg, 40);
24669
25093
  }
@@ -24821,10 +25245,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24821
25245
  const iconY = iconPad;
24822
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})`);
24823
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
+ );
24824
25253
  const cx = iconSize / 2;
24825
25254
  const cy = iconSize / 2;
24826
- 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);
24827
- 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);
24828
25257
  }
24829
25258
  }
24830
25259
  for (const edge of layout.edges) {
@@ -24913,10 +25342,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24913
25342
  const iconY = iconPad;
24914
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})`);
24915
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;
24916
25346
  const cx = iconSize / 2;
24917
25347
  const cy = iconSize / 2;
24918
- 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);
24919
- 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);
24920
25350
  }
24921
25351
  }
24922
25352
  if (hasAncestorTrail) {
@@ -25837,7 +26267,7 @@ function nodeStroke2(_palette, nodeColor2) {
25837
26267
  }
25838
26268
  function containerFill2(palette, isDark, nodeColor2) {
25839
26269
  if (nodeColor2) {
25840
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26270
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25841
26271
  }
25842
26272
  return mix(palette.surface, palette.bg, 40);
25843
26273
  }
@@ -28644,7 +29074,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28644
29074
  onToggleDescriptions,
28645
29075
  onToggleControlsExpand,
28646
29076
  exportMode = false,
28647
- controlsHost
29077
+ controlsHost,
29078
+ rampDomain
28648
29079
  } = options ?? {};
28649
29080
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28650
29081
  const width = exportDims?.width ?? container.clientWidth;
@@ -28664,8 +29095,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28664
29095
  const sTitleY = sctx.structural(TITLE_Y);
28665
29096
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28666
29097
  const hasRamp = nodeValues.length > 0;
28667
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28668
- const rampMax = Math.max(...nodeValues);
29098
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29099
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28669
29100
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28670
29101
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28671
29102
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28817,7 +29248,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28817
29248
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28818
29249
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28819
29250
  if (group.collapsed) {
28820
- const fillColor = isDark ? palette.surface : palette.bg;
29251
+ const fillColor = themeBaseBg(palette, isDark);
28821
29252
  const strokeColor = palette.border;
28822
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);
28823
29254
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -29173,6 +29604,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29173
29604
  });
29174
29605
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29175
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
+ }
29176
29616
  }
29177
29617
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29178
29618
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29769,7 +30209,7 @@ function shuffle(a, r) {
29769
30209
  return x;
29770
30210
  }
29771
30211
  function flatten(d) {
29772
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30212
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29773
30213
  const pts = [];
29774
30214
  let i = 0, cx = 0, cy = 0, cmd = "";
29775
30215
  const num = () => parseFloat(toks[i++]);
@@ -29813,17 +30253,10 @@ function flatten(d) {
29813
30253
  }
29814
30254
  return pts;
29815
30255
  }
29816
- function segPoint(p1, p2, p3, p4) {
29817
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29818
- if (Math.abs(den) < 1e-9) return null;
29819
- 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;
29820
- 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;
29821
- }
29822
- function countSplineCrossings(layout) {
29823
- const center = /* @__PURE__ */ new Map();
29824
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29825
- for (const g of layout.groups)
29826
- 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;
29827
30260
  const polys = layout.edges.map((e) => {
29828
30261
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29829
30262
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29833,8 +30266,24 @@ function countSplineCrossings(layout) {
29833
30266
  if (p.y < y0) y0 = p.y;
29834
30267
  if (p.y > y1) y1 = p.y;
29835
30268
  }
29836
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30269
+ return { pts, x0, y0, x1, y1 };
29837
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;
29838
30287
  const R = 34;
29839
30288
  let total = 0;
29840
30289
  for (let a = 0; a < polys.length; a++)
@@ -29842,23 +30291,33 @@ function countSplineCrossings(layout) {
29842
30291
  const A = polys[a], B = polys[b];
29843
30292
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29844
30293
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29845
- 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);
29846
30300
  const hits = [];
29847
- for (let i = 1; i < A.pts.length; i++)
29848
- for (let j = 1; j < B.pts.length; j++) {
29849
- const p = segPoint(
29850
- A.pts[i - 1],
29851
- A.pts[i],
29852
- B.pts[j - 1],
29853
- B.pts[j]
29854
- );
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);
29855
30312
  if (!p) continue;
29856
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29857
- 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;
29858
30315
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29859
30316
  hits.push(p);
29860
30317
  }
30318
+ }
29861
30319
  total += hits.length;
30320
+ if (total > floor) return total;
29862
30321
  }
29863
30322
  return total;
29864
30323
  }
@@ -29896,17 +30355,8 @@ function detectEdgeOverlaps(layout, opts) {
29896
30355
  w: g.width,
29897
30356
  h: g.height
29898
30357
  });
29899
- const polys = layout.edges.map((e) => {
29900
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29901
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29902
- for (const p of pts) {
29903
- if (p.x < x0) x0 = p.x;
29904
- if (p.x > x1) x1 = p.x;
29905
- if (p.y < y0) y0 = p.y;
29906
- if (p.y > y1) y1 = p.y;
29907
- }
29908
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29909
- });
30358
+ const polys = flatPolys(layout);
30359
+ const edges = layout.edges;
29910
30360
  const runs = [];
29911
30361
  for (let a = 0; a < polys.length; a++)
29912
30362
  for (let b = a + 1; b < polys.length; b++) {
@@ -29914,7 +30364,12 @@ function detectEdgeOverlaps(layout, opts) {
29914
30364
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29915
30365
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29916
30366
  continue;
29917
- 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);
29918
30373
  let run = [];
29919
30374
  let runLen = 0;
29920
30375
  const flush = () => {
@@ -29928,7 +30383,7 @@ function detectEdgeOverlaps(layout, opts) {
29928
30383
  runLen = 0;
29929
30384
  };
29930
30385
  for (const p of A.pts) {
29931
- 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;
29932
30387
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29933
30388
  if (covered) {
29934
30389
  if (run.length)
@@ -29963,9 +30418,10 @@ function detectEdgeNodePierces(layout, opts) {
29963
30418
  });
29964
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;
29965
30420
  const out = [];
30421
+ const polys = flatPolys(layout);
29966
30422
  layout.edges.forEach((e, idx) => {
29967
30423
  if (e.points.length < 2) return;
29968
- const poly = flatten(splineGen(e.points) ?? "");
30424
+ const poly = polys[idx].pts;
29969
30425
  for (const r of rects) {
29970
30426
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29971
30427
  continue;
@@ -30312,8 +30768,10 @@ function edgeLength(layout) {
30312
30768
  );
30313
30769
  return total;
30314
30770
  }
30315
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30771
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30316
30772
  const hideDescriptions = opts?.hideDescriptions ?? false;
30773
+ const onProgress = opts?.onProgress;
30774
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30317
30775
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30318
30776
  if (collapseInfo) {
30319
30777
  const missing = /* @__PURE__ */ new Set();
@@ -30727,17 +31185,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30727
31185
  seed: s
30728
31186
  });
30729
31187
  const badness = (lay, floor) => {
30730
- const x = countSplineCrossings(lay);
31188
+ const x = countSplineCrossings(lay, floor);
30731
31189
  if (x > floor) return Infinity;
30732
31190
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30733
31191
  };
30734
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
+ };
30735
31200
  const pool = [];
30736
31201
  for (const cfg of configs) {
30737
31202
  try {
30738
31203
  pool.push(place(cfg));
30739
31204
  } catch {
30740
31205
  }
31206
+ await step("Optimizing layout");
30741
31207
  }
30742
31208
  if (!pool.length)
30743
31209
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30746,9 +31212,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30746
31212
  layered = layeredCandidates(parsed, sizes);
30747
31213
  } catch {
30748
31214
  }
30749
- pool.sort(
30750
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30751
- );
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));
30752
31218
  const refineK = Math.min(REFINE_K, pool.length);
30753
31219
  let best = pool[0];
30754
31220
  let bestObj = Infinity;
@@ -30763,7 +31229,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30763
31229
  best = lay;
30764
31230
  }
30765
31231
  };
30766
- 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
+ }
30767
31236
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30768
31237
  const extra = [];
30769
31238
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30779,9 +31248,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30779
31248
  } catch {
30780
31249
  }
30781
31250
  }
30782
- extra.sort(
30783
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30784
- );
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));
30785
31255
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30786
31256
  }
30787
31257
  for (const lay of layered) {
@@ -30808,7 +31278,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30808
31278
  }
30809
31279
  return best;
30810
31280
  }
30811
- 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;
30812
31282
  var init_layout_search = __esm({
30813
31283
  "src/boxes-and-lines/layout-search.ts"() {
30814
31284
  "use strict";
@@ -30822,6 +31292,8 @@ var init_layout_search = __esm({
30822
31292
  ESCALATE_SEEDS = 18;
30823
31293
  ESCALATE_REFINE = 10;
30824
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();
30825
31297
  GROUP_LABEL_ZONE2 = 32;
30826
31298
  }
30827
31299
  });
@@ -30906,12 +31378,15 @@ function computeNodeSize(node, reserveValueRow) {
30906
31378
  }
30907
31379
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30908
31380
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30909
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31381
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30910
31382
  ...layoutOptions?.hideDescriptions !== void 0 && {
30911
31383
  hideDescriptions: layoutOptions.hideDescriptions
30912
31384
  },
30913
31385
  ...layoutOptions?.previousPositions !== void 0 && {
30914
31386
  previousPositions: layoutOptions.previousPositions
31387
+ },
31388
+ ...layoutOptions?.onProgress !== void 0 && {
31389
+ onProgress: layoutOptions.onProgress
30915
31390
  }
30916
31391
  });
30917
31392
  return attachNotes(
@@ -31695,7 +32170,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31695
32170
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31696
32171
  const availWidth = containerWidth;
31697
32172
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31698
- 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
+ );
31699
32179
  let renderLayout = layout;
31700
32180
  if (ctx.factor < 1) {
31701
32181
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31704,17 +32184,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31704
32184
  hiddenCounts.set(n.id, n.hiddenCount);
31705
32185
  }
31706
32186
  }
31707
- renderLayout = layoutMindmap(parsed, palette, {
32187
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31708
32188
  interactive: !isExport,
31709
32189
  ...hiddenCounts.size > 0 && { hiddenCounts },
31710
32190
  activeTagGroup: activeTagGroup ?? null,
31711
32191
  ...hideDescriptions !== void 0 && { hideDescriptions },
31712
- ctx
32192
+ ctx: c
31713
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
+ }
31714
32204
  }
31715
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31716
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31717
- 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
+ );
31718
32218
  if (ctx.isBelowFloor) {
31719
32219
  svg.attr("width", "100%");
31720
32220
  }
@@ -41057,7 +41557,7 @@ __export(renderer_exports11, {
41057
41557
  renderPertForExport: () => renderPertForExport
41058
41558
  });
41059
41559
  function analysisBlockChrome(palette, isDark) {
41060
- const surfaceBg = isDark ? palette.surface : palette.bg;
41560
+ const surfaceBg = themeBaseBg(palette, isDark);
41061
41561
  return {
41062
41562
  fill: mix(palette.surface, palette.bg, 40),
41063
41563
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -45769,7 +46269,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45769
46269
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45770
46270
  const gw = group.width + sGroupExtraPadding * 2;
45771
46271
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45772
- 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);
45773
46273
  const strokeColor = group.color ?? palette.textMuted;
45774
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");
45775
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");
@@ -49742,6 +50242,16 @@ __export(resolver_exports, {
49742
50242
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49743
50243
  resolveMap: () => resolveMap
49744
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
+ }
49745
50255
  function bboxArea(b) {
49746
50256
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49747
50257
  }
@@ -50238,7 +50748,7 @@ function resolveMap(parsed, data) {
50238
50748
  const containerUnion = unionExtent(containerBoxes, points);
50239
50749
  if (containerUnion)
50240
50750
  extent3 = pad(
50241
- clampContainerToCluster(containerUnion, points),
50751
+ clampContainerToCluster(containerUnion, points, usOriented),
50242
50752
  PAD_FRACTION
50243
50753
  );
50244
50754
  }
@@ -50322,17 +50832,21 @@ function mostCommonCountry(regions, poiCountries) {
50322
50832
  }
50323
50833
  return best;
50324
50834
  }
50325
- function clampContainerToCluster(container, points) {
50835
+ function clampContainerToCluster(container, points, usOriented) {
50326
50836
  const poi = unionExtent([], points);
50327
50837
  if (!poi) return container;
50328
50838
  let [[west, south], [east, north]] = container;
50329
50839
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50330
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50331
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50332
- if (east <= 180 && pEast <= 180) {
50333
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50334
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50335
- }
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;
50336
50850
  return [
50337
50851
  [west, south],
50338
50852
  [east, north]
@@ -50350,7 +50864,7 @@ function firstError(diags) {
50350
50864
  const e = diags.find((d) => d.severity === "error");
50351
50865
  return e ? formatDgmoError(e) : null;
50352
50866
  }
50353
- 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;
50354
50868
  var init_resolver2 = __esm({
50355
50869
  "src/map/resolver.ts"() {
50356
50870
  "use strict";
@@ -50363,7 +50877,9 @@ var init_resolver2 = __esm({
50363
50877
  WORLD_LAT_SOUTH = -58;
50364
50878
  WORLD_LAT_NORTH = 78;
50365
50879
  POI_ZOOM_FLOOR_DEG = 7;
50366
- CONTAINER_OVERSHOOT_DEG = 8;
50880
+ CONTAINER_OVERSHOOT_MAX = 8;
50881
+ CONTAINER_OVERSHOOT_MIN = 3;
50882
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50367
50883
  US_NATIONAL_LON_SPAN = 48;
50368
50884
  CONUS_BBOX = [
50369
50885
  [-125, 25],
@@ -50529,12 +51045,12 @@ function tierBand(maxSpanDeg) {
50529
51045
  }
50530
51046
  function labelBudget(width, height, band) {
50531
51047
  const bandCap = {
50532
- world: 7,
50533
- continental: 6,
50534
- regional: 5,
50535
- local: 4
51048
+ world: 10,
51049
+ continental: 9,
51050
+ regional: 7,
51051
+ local: 6
50536
51052
  };
50537
- 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);
50538
51054
  return Math.max(0, Math.min(area2, bandCap[band]));
50539
51055
  }
50540
51056
  function waterEligible(tier, kind, band) {
@@ -50588,6 +51104,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50588
51104
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50589
51105
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50590
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
+ }
50591
51112
  function rectFits(r, width, height) {
50592
51113
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50593
51114
  }
@@ -50606,6 +51127,7 @@ function placeContextLabels(args) {
50606
51127
  palette,
50607
51128
  project,
50608
51129
  collides,
51130
+ contentPoints,
50609
51131
  overLand
50610
51132
  } = args;
50611
51133
  void projection;
@@ -50676,8 +51198,17 @@ function placeContextLabels(args) {
50676
51198
  const [x0, y0, x1, y1] = c.bbox;
50677
51199
  const w = x1 - x0;
50678
51200
  const h = y1 - y0;
50679
- return { c, w, h, area: w * h };
50680
- }).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
+ );
50681
51212
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50682
51213
  let ci = 0;
50683
51214
  for (const r of ranked) {
@@ -50694,7 +51225,7 @@ function placeContextLabels(args) {
50694
51225
  );
50695
51226
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50696
51227
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50697
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51228
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50698
51229
  const text = c.name;
50699
51230
  const tw = labelWidth(text, 0, fontSize);
50700
51231
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50707,6 +51238,9 @@ function placeContextLabels(args) {
50707
51238
  letterSpacing: 0,
50708
51239
  color,
50709
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 } : {},
50710
51244
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50711
51245
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50712
51246
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50721,39 +51255,53 @@ function placeContextLabels(args) {
50721
51255
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50722
51256
  const waterCap = budget - Math.min(2, countryCount);
50723
51257
  let waterPlaced = 0;
50724
- for (const cand of candidates) {
50725
- if (placed.length >= budget) break;
50726
- if (cand.italic && waterPlaced >= waterCap) continue;
51258
+ const gateAt = (cx, cy, cand) => {
50727
51259
  const rect = rectAround(
50728
- cand.cx,
50729
- cand.cy,
51260
+ cx,
51261
+ cy,
50730
51262
  cand.lines,
50731
51263
  cand.letterSpacing,
50732
51264
  cand.fontSize
50733
51265
  );
50734
- if (!rectFits(rect, width, height)) continue;
51266
+ if (!rectFits(rect, width, height)) return null;
50735
51267
  if (cand.italic && overLand) {
50736
51268
  const inset = 2;
50737
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51269
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50738
51270
  const touchesLand = cand.lines.some((line11, li) => {
50739
51271
  const lw = labelWidth(line11, cand.letterSpacing);
50740
- const x0 = cand.cx - lw / 2 + inset;
50741
- const x1 = cand.cx + lw / 2 - inset;
50742
- 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];
50743
51275
  const base = top + li * LINE_HEIGHT;
50744
51276
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50745
51277
  (y) => xs.some((x) => overLand(x, y))
50746
51278
  );
50747
51279
  });
50748
- if (touchesLand) continue;
51280
+ if (touchesLand) return null;
51281
+ }
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
+ }
50749
51298
  }
50750
- if (collides(rect)) continue;
50751
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50752
- placedRects.push(rect);
51299
+ if (!chosen) continue;
51300
+ placedRects.push(chosen.rect);
50753
51301
  if (cand.italic) waterPlaced++;
50754
51302
  placed.push({
50755
- x: cand.cx,
50756
- y: cand.cy,
51303
+ x: chosen.x,
51304
+ y: chosen.y,
50757
51305
  text: cand.text,
50758
51306
  anchor: "middle",
50759
51307
  color: cand.color,
@@ -50771,7 +51319,7 @@ function placeContextLabels(args) {
50771
51319
  }
50772
51320
  return placed;
50773
51321
  }
50774
- 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;
50775
51323
  var init_context_labels = __esm({
50776
51324
  "src/map/context-labels.ts"() {
50777
51325
  "use strict";
@@ -50789,6 +51337,9 @@ var init_context_labels = __esm({
50789
51337
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50790
51338
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50791
51339
  COUNTRY_FADE_MAX = 45;
51340
+ MAX_COUNTRY_POSITIONS = 4;
51341
+ COUNTRY_POS_GRID = 5;
51342
+ COUNTRY_POS_TOPN_MARGIN = 3;
50792
51343
  KIND_ORDER = {
50793
51344
  ocean: 0,
50794
51345
  sea: 1,
@@ -50871,6 +51422,84 @@ function decodeLayer(topo) {
50871
51422
  decodeCache.set(topo, out);
50872
51423
  return out;
50873
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
+ }
50874
51503
  function projectionFor(family, extent3) {
50875
51504
  switch (family) {
50876
51505
  case "albers-usa":
@@ -51698,6 +52327,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51698
52327
  if (p[1] < minY) minY = p[1];
51699
52328
  if (p[1] > maxY) maxY = p[1];
51700
52329
  }
52330
+ r.bbox = [minX, minY, maxX, maxY];
52331
+ r.rings = rings;
51701
52332
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51702
52333
  });
51703
52334
  const fillAt = (x, y) => {
@@ -52888,6 +53519,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52888
53519
  for (const box of insets)
52889
53520
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52890
53521
  const countryCandidates = [];
53522
+ const rawCountries = [];
52891
53523
  for (const f of worldLayer.values()) {
52892
53524
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52893
53525
  if (!iso || regionById.has(iso)) continue;
@@ -52904,11 +53536,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52904
53536
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52905
53537
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52906
53538
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52907
- countryCandidates.push({
53539
+ rawCountries.push({
53540
+ f,
53541
+ iso,
52908
53542
  name: f.properties?.name ?? iso,
52909
53543
  bbox: [x0, y0, x1, y1],
52910
53544
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52911
- 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 } : {}
52912
53592
  });
52913
53593
  }
52914
53594
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52942,6 +53622,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52942
53622
  palette,
52943
53623
  project,
52944
53624
  collides,
53625
+ contentPoints,
52945
53626
  // Water labels must stay over open water — `fillAt` returns the ocean
52946
53627
  // backdrop colour off-land and a region fill on-land (lakes/states count
52947
53628
  // as land here, which is the safe side for an ocean name).
@@ -53195,7 +53876,7 @@ function ringToCoastPaths(ring, frame) {
53195
53876
  function coastlineOuterRings(regions, minExtent, frame) {
53196
53877
  const paths = [];
53197
53878
  for (const r of regions) {
53198
- const rings = parsePathRings(r.d);
53879
+ const rings = r.rings ?? parsePathRings(r.d);
53199
53880
  for (let i = 0; i < rings.length; i++) {
53200
53881
  const ring = rings[i];
53201
53882
  if (ring.length < 3) continue;
@@ -53390,14 +54071,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53390
54071
  if (decoCluster !== void 0)
53391
54072
  gPatch.attr("data-cluster-deco", decoCluster);
53392
54073
  for (const r of layout.regions) {
53393
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53394
- for (const ring of parsePathRings(r.d))
53395
- for (const [px, py] of ring) {
53396
- if (px < minX) minX = px;
53397
- if (px > maxX) maxX = px;
53398
- if (py < minY) minY = py;
53399
- if (py > maxY) maxY = py;
53400
- }
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
+ }
53401
54090
  const hit = blobRects.some(
53402
54091
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53403
54092
  );
@@ -54625,7 +55314,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54625
55314
  const tasksAll = [...allTasks(parsed)];
54626
55315
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54627
55316
  const solid = parsed.options["solid-fill"] === "on";
54628
- const surfaceBg = isDark ? palette.surface : palette.bg;
55317
+ const surfaceBg = themeBaseBg(palette, isDark);
54629
55318
  const roleCount = Math.max(1, parsed.roles.length);
54630
55319
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54631
55320
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54803,10 +55492,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54803
55492
  const cx = roleX(i) + sColumnInset;
54804
55493
  const cw = roleColW - 2 * sColumnInset;
54805
55494
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54806
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55495
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54807
55496
  const headerFill = mix(
54808
55497
  roleColor,
54809
- isDark ? palette.surface : palette.bg,
55498
+ themeBaseBg(palette, isDark),
54810
55499
  30
54811
55500
  );
54812
55501
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -56533,7 +57222,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56533
57222
  const groupTagColor = getTagColor(groupTagValue || void 0);
56534
57223
  const fillColor = groupTagColor ? mix(
56535
57224
  groupTagColor,
56536
- isDark ? palette.surface : palette.bg,
57225
+ themeBaseBg(palette, isDark),
56537
57226
  isDark ? 15 : 20
56538
57227
  ) : isDark ? palette.surface : palette.bg;
56539
57228
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56587,7 +57276,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56587
57276
  participantG.append("title").text("Click to expand");
56588
57277
  const pFill = effectiveTagColor ? mix(
56589
57278
  effectiveTagColor,
56590
- isDark ? palette.surface : palette.bg,
57279
+ themeBaseBg(palette, isDark),
56591
57280
  isDark ? 30 : 40
56592
57281
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56593
57282
  const pStroke = effectiveTagColor || palette.border;
@@ -56794,7 +57483,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56794
57483
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56795
57484
  const actTagColor = getTagColor(actTagValue);
56796
57485
  const actBaseColor = actTagColor || palette.primary;
56797
- 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));
56798
57487
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56799
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");
56800
57489
  if (tagKey && actTagValue) {
@@ -58517,7 +59206,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58517
59206
  const textColor = palette.text;
58518
59207
  const mutedColor = palette.border;
58519
59208
  const bgColor = palette.bg;
58520
- const bg = isDark ? palette.surface : palette.bg;
59209
+ const bg = themeBaseBg(palette, isDark);
58521
59210
  const colors = getSeriesColors(palette);
58522
59211
  const groupColorMap = /* @__PURE__ */ new Map();
58523
59212
  timelineGroups.forEach((grp, i) => {
@@ -58932,9 +59621,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58932
59621
  drawLegend2();
58933
59622
  }
58934
59623
  }
58935
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59624
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58936
59625
  const {
58937
59626
  width,
59627
+ height,
58938
59628
  tooltip,
58939
59629
  solid,
58940
59630
  textColor,
@@ -58986,9 +59676,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58986
59676
  const rowH = ctx.structural(28);
58987
59677
  const innerHeight = rowH * sorted.length;
58988
59678
  const usedHeight = margin.top + innerHeight + margin.bottom;
59679
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59680
+ const svgHeight = fitToContainer ? height : usedHeight;
58989
59681
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58990
- 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);
58991
- if (ctx.isBelowFloor) {
59682
+ 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);
59683
+ if (ctx.isBelowFloor && !fitToContainer) {
58992
59684
  svg.attr("width", "100%");
58993
59685
  }
58994
59686
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60662,7 +61354,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60662
61354
  onClickItem
60663
61355
  );
60664
61356
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60665
- const bg = isDark ? palette.surface : palette.bg;
61357
+ const bg = themeBaseBg(palette, isDark);
60666
61358
  const getQuadrantColor = (label, defaultIdx) => {
60667
61359
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60668
61360
  };
@@ -60999,18 +61691,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60999
61691
  palette,
61000
61692
  viewState,
61001
61693
  options,
61002
- exportMode
61694
+ exportMode,
61695
+ isDark: theme === "dark"
61003
61696
  };
61004
61697
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
61005
61698
  return (handler ?? exportVisualization)(ctx);
61006
61699
  }
61700
+ function ctxTagOverride(ctx) {
61701
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61702
+ }
61007
61703
  async function exportOrg(ctx) {
61008
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61704
+ const { content, theme, palette, viewState, exportMode } = ctx;
61009
61705
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
61010
61706
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
61011
61707
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
61012
61708
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
61013
- const isDark = theme === "dark";
61709
+ const isDark = ctx.isDark;
61014
61710
  const effectivePalette = await resolveExportPalette(theme, palette);
61015
61711
  const orgParsed = parseOrg2(content, effectivePalette);
61016
61712
  if (orgParsed.error) return "";
@@ -61018,7 +61714,7 @@ async function exportOrg(ctx) {
61018
61714
  const activeTagGroup = resolveActiveTagGroup(
61019
61715
  orgParsed.tagGroups,
61020
61716
  orgParsed.options["active-tag"],
61021
- viewState?.tag ?? options?.tagGroup
61717
+ ctxTagOverride(ctx)
61022
61718
  );
61023
61719
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
61024
61720
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61051,12 +61747,12 @@ async function exportOrg(ctx) {
61051
61747
  return finalizeSvgExport(container, theme, effectivePalette);
61052
61748
  }
61053
61749
  async function exportSitemap(ctx) {
61054
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61750
+ const { content, theme, palette, viewState, exportMode } = ctx;
61055
61751
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
61056
61752
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
61057
61753
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
61058
61754
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
61059
- const isDark = theme === "dark";
61755
+ const isDark = ctx.isDark;
61060
61756
  const effectivePalette = await resolveExportPalette(theme, palette);
61061
61757
  const sitemapParsed = parseSitemap2(content, effectivePalette);
61062
61758
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -61064,7 +61760,7 @@ async function exportSitemap(ctx) {
61064
61760
  const activeTagGroup = resolveActiveTagGroup(
61065
61761
  sitemapParsed.tagGroups,
61066
61762
  sitemapParsed.options["active-tag"],
61067
- viewState?.tag ?? options?.tagGroup
61763
+ ctxTagOverride(ctx)
61068
61764
  );
61069
61765
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
61070
61766
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61095,7 +61791,7 @@ async function exportSitemap(ctx) {
61095
61791
  return finalizeSvgExport(container, theme, effectivePalette);
61096
61792
  }
61097
61793
  async function exportKanban(ctx) {
61098
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61794
+ const { content, theme, palette, viewState, exportMode } = ctx;
61099
61795
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
61100
61796
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
61101
61797
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61107,11 +61803,11 @@ async function exportKanban(ctx) {
61107
61803
  document.body.appendChild(container);
61108
61804
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
61109
61805
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
61110
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61806
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
61111
61807
  activeTagGroup: resolveActiveTagGroup(
61112
61808
  kanbanParsed.tagGroups,
61113
61809
  kanbanParsed.options["active-tag"],
61114
- viewState?.tag ?? options?.tagGroup
61810
+ ctxTagOverride(ctx)
61115
61811
  ),
61116
61812
  currentSwimlaneGroup: viewState?.swim ?? null,
61117
61813
  ...kanbanCollapsedLanes !== void 0 && {
@@ -61144,7 +61840,7 @@ async function exportClass(ctx) {
61144
61840
  classParsed,
61145
61841
  classLayout,
61146
61842
  effectivePalette,
61147
- theme === "dark",
61843
+ ctx.isDark,
61148
61844
  void 0,
61149
61845
  { width: exportWidth, height: exportHeight },
61150
61846
  void 0,
@@ -61153,7 +61849,7 @@ async function exportClass(ctx) {
61153
61849
  return finalizeSvgExport(container, theme, effectivePalette);
61154
61850
  }
61155
61851
  async function exportEr(ctx) {
61156
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61852
+ const { content, theme, palette, viewState, exportMode } = ctx;
61157
61853
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
61158
61854
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
61159
61855
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -61171,13 +61867,13 @@ async function exportEr(ctx) {
61171
61867
  erParsed,
61172
61868
  erLayout,
61173
61869
  effectivePalette,
61174
- theme === "dark",
61870
+ ctx.isDark,
61175
61871
  void 0,
61176
61872
  { width: exportWidth, height: exportHeight },
61177
61873
  resolveActiveTagGroup(
61178
61874
  erParsed.tagGroups,
61179
61875
  erParsed.options["active-tag"],
61180
- viewState?.tag ?? options?.tagGroup
61876
+ ctxTagOverride(ctx)
61181
61877
  ),
61182
61878
  viewState?.sem,
61183
61879
  exportMode
@@ -61185,7 +61881,7 @@ async function exportEr(ctx) {
61185
61881
  return finalizeSvgExport(container, theme, effectivePalette);
61186
61882
  }
61187
61883
  async function exportBoxesAndLines(ctx) {
61188
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61884
+ const { content, theme, palette, viewState, exportMode } = ctx;
61189
61885
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
61190
61886
  const effectivePalette = await resolveExportPalette(theme, palette);
61191
61887
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -61205,13 +61901,13 @@ async function exportBoxesAndLines(ctx) {
61205
61901
  const exportWidth = blLayout.width + PADDING3 * 2;
61206
61902
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
61207
61903
  const container = createExportContainer(exportWidth, exportHeight);
61208
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61904
+ const blActiveTagGroup = ctxTagOverride(ctx);
61209
61905
  renderBoxesAndLinesForExport2(
61210
61906
  container,
61211
61907
  blParsed,
61212
61908
  blLayout,
61213
61909
  effectivePalette,
61214
- theme === "dark",
61910
+ ctx.isDark,
61215
61911
  {
61216
61912
  exportDims: { width: exportWidth, height: exportHeight },
61217
61913
  ...blActiveTagGroup !== void 0 && {
@@ -61226,12 +61922,12 @@ async function exportBoxesAndLines(ctx) {
61226
61922
  return finalizeSvgExport(container, theme, effectivePalette);
61227
61923
  }
61228
61924
  async function exportMindmap(ctx) {
61229
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61925
+ const { content, theme, palette, viewState, exportMode } = ctx;
61230
61926
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
61231
61927
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
61232
61928
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
61233
61929
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
61234
- const isDark = theme === "dark";
61930
+ const isDark = ctx.isDark;
61235
61931
  const effectivePalette = await resolveExportPalette(theme, palette);
61236
61932
  const mmParsed = parseMindmap2(content, effectivePalette);
61237
61933
  if (mmParsed.error) return "";
@@ -61239,7 +61935,7 @@ async function exportMindmap(ctx) {
61239
61935
  const activeTagGroup = resolveActiveTagGroup(
61240
61936
  mmParsed.tagGroups,
61241
61937
  mmParsed.options["active-tag"],
61242
- viewState?.tag ?? options?.tagGroup
61938
+ ctxTagOverride(ctx)
61243
61939
  );
61244
61940
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
61245
61941
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61289,7 +61985,7 @@ async function exportWireframe(ctx) {
61289
61985
  wireframeParsed,
61290
61986
  wireframeLayout,
61291
61987
  effectivePalette,
61292
- theme === "dark",
61988
+ ctx.isDark,
61293
61989
  void 0,
61294
61990
  { width: exportWidth, height: exportHeight },
61295
61991
  theme
@@ -61297,7 +61993,7 @@ async function exportWireframe(ctx) {
61297
61993
  return finalizeSvgExport(container, theme, effectivePalette);
61298
61994
  }
61299
61995
  async function exportC4(ctx) {
61300
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61996
+ const { content, theme, palette, viewState, exportMode } = ctx;
61301
61997
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61302
61998
  const {
61303
61999
  layoutC4Context: layoutC4Context2,
@@ -61309,9 +62005,9 @@ async function exportC4(ctx) {
61309
62005
  const effectivePalette = await resolveExportPalette(theme, palette);
61310
62006
  const c4Parsed = parseC42(content, effectivePalette);
61311
62007
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61312
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61313
- const c4System = options?.c4System ?? viewState?.c4s;
61314
- const c4Container = options?.c4Container ?? viewState?.c4c;
62008
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62009
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62010
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61315
62011
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61316
62012
  if (c4Layout.nodes.length === 0) return "";
61317
62013
  const PADDING3 = 20;
@@ -61325,13 +62021,13 @@ async function exportC4(ctx) {
61325
62021
  c4Parsed,
61326
62022
  c4Layout,
61327
62023
  effectivePalette,
61328
- theme === "dark",
62024
+ ctx.isDark,
61329
62025
  void 0,
61330
62026
  { width: exportWidth, height: exportHeight },
61331
62027
  resolveActiveTagGroup(
61332
62028
  c4Parsed.tagGroups,
61333
62029
  c4Parsed.options["active-tag"],
61334
- viewState?.tag ?? options?.tagGroup
62030
+ ctxTagOverride(ctx)
61335
62031
  ),
61336
62032
  exportMode
61337
62033
  );
@@ -61352,14 +62048,14 @@ async function exportFlowchart(ctx) {
61352
62048
  fcParsed,
61353
62049
  layout,
61354
62050
  effectivePalette,
61355
- theme === "dark",
62051
+ ctx.isDark,
61356
62052
  void 0,
61357
62053
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61358
62054
  );
61359
62055
  return finalizeSvgExport(container, theme, effectivePalette);
61360
62056
  }
61361
62057
  async function exportInfra(ctx) {
61362
- const { content, theme, palette, viewState, options } = ctx;
62058
+ const { content, theme, palette, viewState } = ctx;
61363
62059
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61364
62060
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61365
62061
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61372,7 +62068,7 @@ async function exportInfra(ctx) {
61372
62068
  const activeTagGroup = resolveActiveTagGroup(
61373
62069
  infraParsed.tagGroups,
61374
62070
  infraParsed.options["active-tag"],
61375
- viewState?.tag ?? options?.tagGroup
62071
+ ctxTagOverride(ctx)
61376
62072
  );
61377
62073
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61378
62074
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61390,7 +62086,7 @@ async function exportInfra(ctx) {
61390
62086
  container,
61391
62087
  infraLayout,
61392
62088
  effectivePalette,
61393
- theme === "dark",
62089
+ ctx.isDark,
61394
62090
  showInfraTitle ? infraParsed.title : null,
61395
62091
  showInfraTitle ? infraParsed.titleLineNumber : null,
61396
62092
  infraTagGroups,
@@ -61437,7 +62133,7 @@ async function exportPert(ctx) {
61437
62133
  pertResolved,
61438
62134
  pertLayout,
61439
62135
  effectivePalette,
61440
- theme === "dark",
62136
+ ctx.isDark,
61441
62137
  {
61442
62138
  title: pertParsed.title,
61443
62139
  exportDims: { width: exportW, height: exportH },
@@ -61450,7 +62146,7 @@ async function exportPert(ctx) {
61450
62146
  return finalizeSvgExport(container, theme, effectivePalette);
61451
62147
  }
61452
62148
  async function exportGantt(ctx) {
61453
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62149
+ const { content, theme, palette, viewState, exportMode } = ctx;
61454
62150
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61455
62151
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61456
62152
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61468,7 +62164,7 @@ async function exportGantt(ctx) {
61468
62164
  container,
61469
62165
  resolved,
61470
62166
  effectivePalette,
61471
- theme === "dark",
62167
+ ctx.isDark,
61472
62168
  {
61473
62169
  ...ganttCollapsedGroups !== void 0 && {
61474
62170
  collapsedGroups: ganttCollapsedGroups
@@ -61482,7 +62178,7 @@ async function exportGantt(ctx) {
61482
62178
  currentActiveGroup: resolveActiveTagGroup(
61483
62179
  resolved.tagGroups,
61484
62180
  resolved.options.activeTag ?? void 0,
61485
- viewState?.tag ?? options?.tagGroup
62181
+ ctxTagOverride(ctx)
61486
62182
  ),
61487
62183
  exportMode
61488
62184
  },
@@ -61505,7 +62201,7 @@ async function exportState(ctx) {
61505
62201
  stateParsed,
61506
62202
  layout,
61507
62203
  effectivePalette,
61508
- theme === "dark",
62204
+ ctx.isDark,
61509
62205
  void 0,
61510
62206
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61511
62207
  );
@@ -61525,7 +62221,7 @@ async function exportTechRadar(ctx) {
61525
62221
  container,
61526
62222
  radarParsed,
61527
62223
  effectivePalette,
61528
- theme === "dark",
62224
+ ctx.isDark,
61529
62225
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61530
62226
  viewState,
61531
62227
  exportMode
@@ -61542,13 +62238,13 @@ async function exportJourneyMap(ctx) {
61542
62238
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61543
62239
  return "";
61544
62240
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61545
- isDark: theme === "dark"
62241
+ isDark: ctx.isDark
61546
62242
  });
61547
62243
  const container = createExportContainer(
61548
62244
  jmLayout.totalWidth,
61549
62245
  jmLayout.totalHeight
61550
62246
  );
61551
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62247
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61552
62248
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61553
62249
  exportMode
61554
62250
  });
@@ -61566,7 +62262,7 @@ async function exportCycle(ctx) {
61566
62262
  container,
61567
62263
  cycleParsed,
61568
62264
  effectivePalette,
61569
- theme === "dark",
62265
+ ctx.isDark,
61570
62266
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61571
62267
  viewState,
61572
62268
  exportMode
@@ -61603,7 +62299,7 @@ async function exportMap(ctx) {
61603
62299
  mapResolved,
61604
62300
  mapData,
61605
62301
  effectivePalette,
61606
- theme === "dark",
62302
+ ctx.isDark,
61607
62303
  dims
61608
62304
  );
61609
62305
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61620,7 +62316,7 @@ async function exportPyramid(ctx) {
61620
62316
  container,
61621
62317
  pyramidParsed,
61622
62318
  effectivePalette,
61623
- theme === "dark",
62319
+ ctx.isDark,
61624
62320
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61625
62321
  );
61626
62322
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61637,7 +62333,7 @@ async function exportRing(ctx) {
61637
62333
  container,
61638
62334
  ringParsed,
61639
62335
  effectivePalette,
61640
- theme === "dark",
62336
+ ctx.isDark,
61641
62337
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61642
62338
  );
61643
62339
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61654,7 +62350,7 @@ async function exportRaci(ctx) {
61654
62350
  container,
61655
62351
  raciParsed,
61656
62352
  effectivePalette,
61657
- theme === "dark",
62353
+ ctx.isDark,
61658
62354
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61659
62355
  );
61660
62356
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61677,7 +62373,7 @@ async function exportSlope(ctx) {
61677
62373
  container,
61678
62374
  parsed,
61679
62375
  effectivePalette,
61680
- theme === "dark",
62376
+ ctx.isDark,
61681
62377
  void 0,
61682
62378
  dims
61683
62379
  );
@@ -61693,14 +62389,14 @@ async function exportArc(ctx) {
61693
62389
  container,
61694
62390
  parsed,
61695
62391
  effectivePalette,
61696
- theme === "dark",
62392
+ ctx.isDark,
61697
62393
  void 0,
61698
62394
  dims
61699
62395
  );
61700
62396
  return finalizeSvgExport(container, theme, effectivePalette);
61701
62397
  }
61702
62398
  async function exportTimeline(ctx) {
61703
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62399
+ const { content, theme, palette, viewState, exportMode } = ctx;
61704
62400
  const parsed = parseTimeline(content, palette);
61705
62401
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61706
62402
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61709,13 +62405,13 @@ async function exportTimeline(ctx) {
61709
62405
  container,
61710
62406
  parsed,
61711
62407
  effectivePalette,
61712
- theme === "dark",
62408
+ ctx.isDark,
61713
62409
  void 0,
61714
62410
  dims,
61715
62411
  resolveActiveTagGroup(
61716
62412
  parsed.timelineTagGroups,
61717
62413
  parsed.timelineActiveTag,
61718
- viewState?.tag ?? options?.tagGroup
62414
+ ctxTagOverride(ctx)
61719
62415
  ),
61720
62416
  viewState?.swim,
61721
62417
  void 0,
@@ -61734,7 +62430,7 @@ async function exportWordcloud(ctx) {
61734
62430
  container,
61735
62431
  parsed,
61736
62432
  effectivePalette,
61737
- theme === "dark",
62433
+ ctx.isDark,
61738
62434
  dims
61739
62435
  );
61740
62436
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61749,7 +62445,7 @@ async function exportVenn(ctx) {
61749
62445
  container,
61750
62446
  parsed,
61751
62447
  effectivePalette,
61752
- theme === "dark",
62448
+ ctx.isDark,
61753
62449
  void 0,
61754
62450
  dims
61755
62451
  );
@@ -61765,14 +62461,14 @@ async function exportQuadrant(ctx) {
61765
62461
  container,
61766
62462
  parsed,
61767
62463
  effectivePalette,
61768
- theme === "dark",
62464
+ ctx.isDark,
61769
62465
  void 0,
61770
62466
  dims
61771
62467
  );
61772
62468
  return finalizeSvgExport(container, theme, effectivePalette);
61773
62469
  }
61774
62470
  async function exportVisualization(ctx) {
61775
- const { content, theme, palette, viewState, options } = ctx;
62471
+ const { content, theme, palette, viewState } = ctx;
61776
62472
  const parsed = parseVisualization(content, palette);
61777
62473
  if (parsed.type !== "sequence") {
61778
62474
  if (parsed.error) {
@@ -61783,7 +62479,7 @@ async function exportVisualization(ctx) {
61783
62479
  }
61784
62480
  }
61785
62481
  const effectivePalette = await resolveExportPalette(theme, palette);
61786
- const isDark = theme === "dark";
62482
+ const isDark = ctx.isDark;
61787
62483
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61788
62484
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61789
62485
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61791,7 +62487,7 @@ async function exportVisualization(ctx) {
61791
62487
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61792
62488
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61793
62489
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61794
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62490
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61795
62491
  renderSequenceDiagram2(
61796
62492
  container,
61797
62493
  seqParsed,
@@ -62037,16 +62733,13 @@ var themes = {
62037
62733
 
62038
62734
  // src/dimensions.ts
62039
62735
  init_dgmo_router();
62040
- init_scaling();
62041
62736
  init_chart_type_registry();
62042
62737
  function getMinDimensions(content) {
62043
62738
  const { chartType } = parseDgmo(content);
62044
62739
  if (!chartType) return { width: 300, height: 200 };
62045
- const counts = extractContentCounts(content, chartType);
62046
- return computeMinDimensions(chartType, counts);
62047
- }
62048
- function extractContentCounts(content, chartType) {
62049
- return REGISTRY_BY_ID.get(chartType)?.measure?.(content) ?? {};
62740
+ const descriptor = REGISTRY_BY_ID.get(chartType);
62741
+ const counts = descriptor?.measure?.(content) ?? {};
62742
+ return descriptor?.minDims?.(counts) ?? { width: 300, height: 200 };
62050
62743
  }
62051
62744
 
62052
62745
  // src/utils/svg-embed.ts