@diagrammo/dgmo 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.cursorrules +4 -1
  2. package/.github/copilot-instructions.md +4 -1
  3. package/.windsurfrules +4 -1
  4. package/SKILL.md +4 -1
  5. package/dist/advanced.cjs +1297 -358
  6. package/dist/advanced.d.cts +117 -15
  7. package/dist/advanced.d.ts +117 -15
  8. package/dist/advanced.js +1291 -358
  9. package/dist/auto.cjs +1087 -316
  10. package/dist/auto.js +98 -98
  11. package/dist/auto.mjs +1087 -316
  12. package/dist/cli.cjs +140 -140
  13. package/dist/index.cjs +1090 -397
  14. package/dist/index.js +1090 -397
  15. package/docs/ai-integration.md +4 -1
  16. package/docs/language-reference.md +282 -27
  17. package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
  18. package/gallery/fixtures/c4-full.dgmo +4 -5
  19. package/gallery/fixtures/c4.dgmo +2 -3
  20. package/package.json +7 -1
  21. package/src/advanced.ts +7 -0
  22. package/src/boxes-and-lines/focus.ts +257 -0
  23. package/src/boxes-and-lines/layout-search.ts +131 -65
  24. package/src/boxes-and-lines/layout.ts +7 -1
  25. package/src/boxes-and-lines/parser.ts +19 -4
  26. package/src/boxes-and-lines/renderer.ts +54 -3
  27. package/src/c4/parser.ts +8 -7
  28. package/src/chart-type-registry.ts +129 -4
  29. package/src/chart-types.ts +4 -4
  30. package/src/chart.ts +18 -1
  31. package/src/colors.ts +225 -2
  32. package/src/cycle/parser.ts +2 -7
  33. package/src/d3.ts +67 -54
  34. package/src/diagnostics.ts +17 -0
  35. package/src/dimensions.ts +9 -13
  36. package/src/echarts.ts +42 -14
  37. package/src/er/parser.ts +6 -1
  38. package/src/gantt/parser.ts +44 -7
  39. package/src/graph/flowchart-parser.ts +77 -3
  40. package/src/graph/state-renderer.ts +2 -2
  41. package/src/infra/parser.ts +80 -0
  42. package/src/journey-map/parser.ts +8 -7
  43. package/src/kanban/parser.ts +8 -7
  44. package/src/map/context-labels.ts +134 -27
  45. package/src/map/geo.ts +10 -2
  46. package/src/map/layout.ts +259 -4
  47. package/src/map/parser.ts +2 -0
  48. package/src/map/renderer.ts +22 -11
  49. package/src/map/resolver.ts +68 -19
  50. package/src/mindmap/parser.ts +15 -7
  51. package/src/mindmap/renderer.ts +50 -12
  52. package/src/org/parser.ts +8 -7
  53. package/src/org/renderer.ts +22 -7
  54. package/src/palettes/color-utils.ts +12 -2
  55. package/src/palettes/index.ts +1 -0
  56. package/src/pert/renderer.ts +2 -2
  57. package/src/pyramid/parser.ts +2 -7
  58. package/src/quadrant/renderer.ts +2 -2
  59. package/src/raci/parser.ts +2 -7
  60. package/src/raci/renderer.ts +4 -4
  61. package/src/ring/parser.ts +2 -7
  62. package/src/sequence/parser.ts +18 -7
  63. package/src/sequence/renderer.ts +4 -4
  64. package/src/sitemap/parser.ts +8 -7
  65. package/src/sitemap/renderer.ts +2 -2
  66. package/src/tech-radar/parser.ts +2 -7
  67. package/src/timeline/renderer.ts +15 -5
  68. package/src/utils/parsing.ts +13 -1
  69. package/src/utils/scaling.ts +38 -81
  70. package/src/utils/tag-groups.ts +38 -0
  71. package/src/visualizations/parse.ts +6 -1
  72. package/src/wireframe/parser.ts +6 -1
package/dist/index.js CHANGED
@@ -38,6 +38,14 @@ function makeDgmoError(line11, message, severity = "error", code) {
38
38
  function formatDgmoError(err) {
39
39
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
40
40
  }
41
+ function makeFail(result) {
42
+ return (line11, message) => {
43
+ const diag = makeDgmoError(line11, message);
44
+ result.diagnostics.push(diag);
45
+ result.error = formatDgmoError(diag);
46
+ return result;
47
+ };
48
+ }
41
49
  function levenshtein(a, b) {
42
50
  const m = a.length;
43
51
  const n = b.length;
@@ -304,21 +312,99 @@ function resolveColor(color, palette) {
304
312
  }
305
313
  return colorNames[lower] ?? null;
306
314
  }
315
+ function nearestNamedColor(input) {
316
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
317
+ if (cssHex) input = cssHex;
318
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
319
+ if (!m) return null;
320
+ let h = m[1].toLowerCase();
321
+ if (h.length === 3)
322
+ h = h.split("").map((c) => c + c).join("");
323
+ const r = parseInt(h.slice(0, 2), 16) / 255;
324
+ const g = parseInt(h.slice(2, 4), 16) / 255;
325
+ const b = parseInt(h.slice(4, 6), 16) / 255;
326
+ const max = Math.max(r, g, b);
327
+ const min = Math.min(r, g, b);
328
+ const delta = max - min;
329
+ const l = (max + min) / 2;
330
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
331
+ if (s < 0.15) {
332
+ if (l < 0.2) return "black";
333
+ if (l > 0.85) return "white";
334
+ return "gray";
335
+ }
336
+ let hue;
337
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
338
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
339
+ else hue = 60 * ((r - g) / delta + 4);
340
+ if (hue < 0) hue += 360;
341
+ const anchors = [
342
+ ["red", 0],
343
+ ["orange", 30],
344
+ ["yellow", 55],
345
+ ["green", 120],
346
+ ["teal", 170],
347
+ ["cyan", 190],
348
+ ["blue", 225],
349
+ ["purple", 285],
350
+ ["red", 360]
351
+ ];
352
+ let best = "red";
353
+ let bestD = Infinity;
354
+ for (const [name, deg] of anchors) {
355
+ const d = Math.abs(hue - deg);
356
+ if (d < bestD) {
357
+ bestD = d;
358
+ best = name;
359
+ }
360
+ }
361
+ return best;
362
+ }
363
+ function isInvalidColorToken(token) {
364
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
365
+ const lower = token.toLowerCase();
366
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
367
+ }
368
+ function invalidColorDiagnostic(token, line11) {
369
+ if (!isInvalidColorToken(token)) return null;
370
+ const nearest = nearestNamedColor(token);
371
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
372
+ return makeDgmoError(
373
+ line11,
374
+ `Color "${token}" is not a valid DGMO color \u2014 DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${near}`,
375
+ "warning",
376
+ INVALID_COLOR_CODE
377
+ );
378
+ }
307
379
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
308
380
  const resolved = resolveColor(color, palette);
309
381
  if (resolved !== null) return resolved;
382
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
383
+ const nearest = nearestNamedColor(color);
384
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
385
+ diagnostics.push(
386
+ makeDgmoError(
387
+ line11,
388
+ `Color "${color}" is not supported \u2014 DGMO does not accept hex or CSS color values. Use a named palette color: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${near}`,
389
+ "error",
390
+ INVALID_COLOR_CODE
391
+ )
392
+ );
393
+ return void 0;
394
+ }
310
395
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
311
396
  const suggestion = hint ? ` ${hint}` : "";
312
397
  diagnostics.push(
313
398
  makeDgmoError(
314
399
  line11,
315
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
316
- "warning"
400
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
401
+ "warning",
402
+ INVALID_COLOR_CODE
317
403
  )
318
404
  );
319
405
  return void 0;
320
406
  }
321
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, seriesColors;
407
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
322
408
  var init_colors = __esm({
323
409
  "src/colors.ts"() {
324
410
  "use strict";
@@ -386,6 +472,92 @@ var init_colors = __esm({
386
472
  "orange",
387
473
  "cyan"
388
474
  ]);
475
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
476
+ INVALID_CSS_COLOR_HEX = Object.freeze({
477
+ pink: "#ffc0cb",
478
+ hotpink: "#ff69b4",
479
+ deeppink: "#ff1493",
480
+ lightpink: "#ffb6c1",
481
+ palevioletred: "#db7093",
482
+ crimson: "#dc143c",
483
+ scarlet: "#ff2400",
484
+ firebrick: "#b22222",
485
+ darkred: "#8b0000",
486
+ maroon: "#800000",
487
+ salmon: "#fa8072",
488
+ lightsalmon: "#ffa07a",
489
+ darksalmon: "#e9967a",
490
+ coral: "#ff7f50",
491
+ lightcoral: "#f08080",
492
+ tomato: "#ff6347",
493
+ orangered: "#ff4500",
494
+ darkorange: "#ff8c00",
495
+ gold: "#ffd700",
496
+ goldenrod: "#daa520",
497
+ darkgoldenrod: "#b8860b",
498
+ khaki: "#f0e68c",
499
+ darkkhaki: "#bdb76b",
500
+ amber: "#ffbf00",
501
+ lavender: "#e6e6fa",
502
+ violet: "#ee82ee",
503
+ magenta: "#ff00ff",
504
+ fuchsia: "#ff00ff",
505
+ orchid: "#da70d6",
506
+ plum: "#dda0dd",
507
+ indigo: "#4b0082",
508
+ navy: "#000080",
509
+ midnightblue: "#191970",
510
+ darkblue: "#00008b",
511
+ mediumblue: "#0000cd",
512
+ royalblue: "#4169e1",
513
+ cornflowerblue: "#6495ed",
514
+ dodgerblue: "#1e90ff",
515
+ deepskyblue: "#00bfff",
516
+ skyblue: "#87ceeb",
517
+ lightskyblue: "#87cefa",
518
+ lightblue: "#add8e6",
519
+ powderblue: "#b0e0e6",
520
+ steelblue: "#4682b4",
521
+ slateblue: "#6a5acd",
522
+ cadetblue: "#5f9ea0",
523
+ turquoise: "#40e0d0",
524
+ aqua: "#00ffff",
525
+ aquamarine: "#7fffd4",
526
+ lime: "#00ff00",
527
+ limegreen: "#32cd32",
528
+ lightgreen: "#90ee90",
529
+ palegreen: "#98fb98",
530
+ seagreen: "#2e8b57",
531
+ mediumseagreen: "#3cb371",
532
+ forestgreen: "#228b22",
533
+ darkgreen: "#006400",
534
+ olive: "#808000",
535
+ olivedrab: "#6b8e23",
536
+ darkolivegreen: "#556b2f",
537
+ chartreuse: "#7fff00",
538
+ lawngreen: "#7cfc00",
539
+ springgreen: "#00ff7f",
540
+ greenyellow: "#adff2f",
541
+ brown: "#a52a2a",
542
+ sienna: "#a0522d",
543
+ chocolate: "#d2691e",
544
+ peru: "#cd853f",
545
+ tan: "#d2b48c",
546
+ beige: "#f5f5dc",
547
+ wheat: "#f5deb3",
548
+ ivory: "#fffff0",
549
+ silver: "#c0c0c0",
550
+ lightgray: "#d3d3d3",
551
+ lightgrey: "#d3d3d3",
552
+ darkgray: "#a9a9a9",
553
+ darkgrey: "#a9a9a9",
554
+ dimgray: "#696969",
555
+ dimgrey: "#696969",
556
+ slategray: "#708090",
557
+ slategrey: "#708090",
558
+ gainsboro: "#dcdcdc",
559
+ grey: "#808080"
560
+ });
389
561
  seriesColors = [
390
562
  nord.nord10,
391
563
  // blue
@@ -560,7 +732,13 @@ function extractColor(label, palette, diagnostics, line11) {
560
732
  );
561
733
  if (lastSpaceIdx < 0) return { label };
562
734
  const trailing = label.substring(lastSpaceIdx + 1);
563
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
735
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
736
+ if (diagnostics && line11 !== void 0) {
737
+ const diag = invalidColorDiagnostic(trailing, line11);
738
+ if (diag) diagnostics.push(diag);
739
+ }
740
+ return { label };
741
+ }
564
742
  let color;
565
743
  if (diagnostics && line11 !== void 0) {
566
744
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1184,6 +1362,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1184
1362
  }
1185
1363
  }
1186
1364
  }
1365
+ function cascadeTagMetadata(roots, tagGroups) {
1366
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1367
+ if (keys.length === 0) return;
1368
+ const walk = (node, inherited) => {
1369
+ const childInherited = { ...inherited };
1370
+ for (const key of keys) {
1371
+ const own = node.metadata[key];
1372
+ if (own) {
1373
+ childInherited[key] = own;
1374
+ } else if (inherited[key]) {
1375
+ node.metadata[key] = inherited[key];
1376
+ }
1377
+ }
1378
+ for (const child of node.children) walk(child, childInherited);
1379
+ };
1380
+ for (const root of roots) walk(root, {});
1381
+ }
1187
1382
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1188
1383
  const defaults = [];
1189
1384
  for (const group of tagGroups) {
@@ -1722,7 +1917,12 @@ function parseVisualizationFull(content, palette) {
1722
1917
  }
1723
1918
  if (currentTimelineTagGroup && indent > 0) {
1724
1919
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1725
- const { label, color } = extractColor(entryText, palette);
1920
+ const { label, color } = extractColor(
1921
+ entryText,
1922
+ palette,
1923
+ result.diagnostics,
1924
+ lineNumber
1925
+ );
1726
1926
  if (color) {
1727
1927
  if (isDefault) {
1728
1928
  currentTimelineTagGroup.defaultValue = label;
@@ -3023,9 +3223,12 @@ function contrastText(bg, lightText, darkText) {
3023
3223
  }
3024
3224
  return lightText;
3025
3225
  }
3226
+ function themeBaseBg(palette, isDark) {
3227
+ return isDark ? palette.surface : palette.bg;
3228
+ }
3026
3229
  function shapeFill(palette, intent, isDark, opts) {
3027
3230
  if (opts?.solid) return intent;
3028
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3231
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3029
3232
  }
3030
3233
  function getSeriesColors(palette) {
3031
3234
  const c = palette.colors;
@@ -3056,7 +3259,7 @@ function getSegmentColors(palette, count) {
3056
3259
  }
3057
3260
  function politicalTints(palette, count, isDark) {
3058
3261
  if (count <= 0) return [];
3059
- const base = isDark ? palette.surface : palette.bg;
3262
+ const base = themeBaseBg(palette, isDark);
3060
3263
  const c = palette.colors;
3061
3264
  const swatches = [
3062
3265
  .../* @__PURE__ */ new Set([
@@ -3866,6 +4069,7 @@ __export(palettes_exports, {
3866
4069
  shade: () => shade,
3867
4070
  shapeFill: () => shapeFill,
3868
4071
  slatePalette: () => slatePalette,
4072
+ themeBaseBg: () => themeBaseBg,
3869
4073
  tidewaterPalette: () => tidewaterPalette,
3870
4074
  tint: () => tint,
3871
4075
  tokyoNightPalette: () => tokyoNightPalette
@@ -4080,81 +4284,7 @@ var init_text_measure = __esm({
4080
4284
  });
4081
4285
 
4082
4286
  // src/utils/scaling.ts
4083
- function computeMinDimensions(chartType, counts) {
4084
- switch (chartType) {
4085
- case "sequence":
4086
- return {
4087
- width: Math.max((counts.participants ?? 2) * 80, 320),
4088
- height: Math.max((counts.messages ?? 1) * 20 + 120, 200)
4089
- };
4090
- case "raci":
4091
- return {
4092
- width: Math.max((counts.roles ?? 2) * 50 + 180, 300),
4093
- height: Math.max((counts.tasks ?? 1) * 28 + 80, 200)
4094
- };
4095
- case "mindmap":
4096
- return {
4097
- width: Math.max((counts.nodes ?? 3) * 30, 300),
4098
- height: Math.max((counts.depth ?? 2) * 60, 200)
4099
- };
4100
- case "tech-radar":
4101
- return { width: 360, height: 400 };
4102
- case "heatmap":
4103
- return {
4104
- width: Math.max((counts.columns ?? 3) * 40, 300),
4105
- height: Math.max((counts.rows ?? 3) * 30 + 60, 200)
4106
- };
4107
- case "arc":
4108
- return {
4109
- width: 300,
4110
- height: Math.max((counts.nodes ?? 3) * 20 + 120, 200)
4111
- };
4112
- case "org":
4113
- return {
4114
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4115
- height: Math.max((counts.depth ?? 2) * 80, 200)
4116
- };
4117
- case "gantt":
4118
- return {
4119
- width: 400,
4120
- height: Math.max((counts.tasks ?? 3) * 24 + 80, 200)
4121
- };
4122
- case "kanban":
4123
- return {
4124
- width: Math.max((counts.columns ?? 3) * 120, 360),
4125
- height: 300
4126
- };
4127
- case "er":
4128
- return {
4129
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4130
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4131
- };
4132
- case "class":
4133
- return {
4134
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4135
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4136
- };
4137
- case "flowchart":
4138
- case "state":
4139
- return {
4140
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4141
- height: Math.max((counts.nodes ?? 3) * 50, 200)
4142
- };
4143
- case "pert":
4144
- return {
4145
- width: Math.max((counts.tasks ?? 3) * 80, 340),
4146
- height: Math.max((counts.tasks ?? 3) * 40 + 80, 200)
4147
- };
4148
- case "infra":
4149
- return {
4150
- width: Math.max((counts.nodes ?? 3) * 80, 300),
4151
- height: Math.max((counts.nodes ?? 3) * 60, 200)
4152
- };
4153
- default:
4154
- return { ...DEFAULT_MIN };
4155
- }
4156
- }
4157
- var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext, DEFAULT_MIN;
4287
+ var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext;
4158
4288
  var init_scaling = __esm({
4159
4289
  "src/utils/scaling.ts"() {
4160
4290
  "use strict";
@@ -4173,6 +4303,30 @@ var init_scaling = __esm({
4173
4303
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4174
4304
  return new _ScaleContext(clamped, minScaleFactor);
4175
4305
  }
4306
+ /**
4307
+ * Fit content into a bounding box, scaling by whichever dimension is more
4308
+ * constraining (the smaller of the width- and height-fit ratios) so the
4309
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4310
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4311
+ * never shrunk past the readability floor).
4312
+ */
4313
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4314
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4315
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4316
+ const raw = Math.min(wRaw, hRaw);
4317
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4318
+ return new _ScaleContext(clamped, minScaleFactor);
4319
+ }
4320
+ /**
4321
+ * Build a context from an explicit raw factor (clamped to
4322
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4323
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4324
+ * can still overflow — re-measure the laid-out result and tighten.
4325
+ */
4326
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4327
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4328
+ return new _ScaleContext(clamped, minScaleFactor);
4329
+ }
4176
4330
  static identity() {
4177
4331
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4178
4332
  }
@@ -4189,7 +4343,6 @@ var init_scaling = __esm({
4189
4343
  return Math.max(fontSize * this.factor, floor);
4190
4344
  }
4191
4345
  };
4192
- DEFAULT_MIN = { width: 300, height: 200 };
4193
4346
  }
4194
4347
  });
4195
4348
 
@@ -5874,12 +6027,7 @@ function parseSequenceDgmo(content, palette) {
5874
6027
  const trimmed = token.trim();
5875
6028
  return nameAliasMap.get(trimmed) ?? trimmed;
5876
6029
  };
5877
- const fail = (line11, message) => {
5878
- const diag = makeDgmoError(line11, message);
5879
- result.diagnostics.push(diag);
5880
- result.error = formatDgmoError(diag);
5881
- return result;
5882
- };
6030
+ const fail = makeFail(result);
5883
6031
  const pushError = (line11, message) => {
5884
6032
  const diag = makeDgmoError(line11, message);
5885
6033
  result.diagnostics.push(diag);
@@ -5894,6 +6042,7 @@ function parseSequenceDgmo(content, palette) {
5894
6042
  const lines = content.split("\n");
5895
6043
  let hasExplicitChart = false;
5896
6044
  let contentStarted = false;
6045
+ let bodyStarted = false;
5897
6046
  let firstLineIndex = -1;
5898
6047
  for (let fi = 0; fi < lines.length; fi++) {
5899
6048
  const fl = lines[fi].trim();
@@ -6205,6 +6354,7 @@ function parseSequenceDgmo(content, palette) {
6205
6354
  );
6206
6355
  }
6207
6356
  contentStarted = true;
6357
+ bodyStarted = true;
6208
6358
  const section = {
6209
6359
  kind: "section",
6210
6360
  // Capture group 1 guaranteed present after successful match.
@@ -6420,7 +6570,7 @@ function parseSequenceDgmo(content, palette) {
6420
6570
  alias: bareAlias
6421
6571
  } = splitPipe(trimmed, lineNumber);
6422
6572
  const inGroup = activeGroup && measureIndent(raw) > 0;
6423
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6573
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6424
6574
  contentStarted = true;
6425
6575
  const id = bareCore;
6426
6576
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6467,6 +6617,7 @@ function parseSequenceDgmo(content, palette) {
6467
6617
  }
6468
6618
  if (labeledArrow) {
6469
6619
  contentStarted = true;
6620
+ bodyStarted = true;
6470
6621
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6471
6622
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6472
6623
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6531,6 +6682,7 @@ function parseSequenceDgmo(content, palette) {
6531
6682
  const bareCall = bareCallSync || bareCallAsync;
6532
6683
  if (bareCall) {
6533
6684
  contentStarted = true;
6685
+ bodyStarted = true;
6534
6686
  const from = bareCall[1];
6535
6687
  const to = bareCall[2];
6536
6688
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6552,6 +6704,7 @@ function parseSequenceDgmo(content, palette) {
6552
6704
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6553
6705
  if (ifMatch) {
6554
6706
  contentStarted = true;
6707
+ bodyStarted = true;
6555
6708
  const block = {
6556
6709
  kind: "block",
6557
6710
  type: "if",
@@ -6568,6 +6721,7 @@ function parseSequenceDgmo(content, palette) {
6568
6721
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6569
6722
  if (loopMatch) {
6570
6723
  contentStarted = true;
6724
+ bodyStarted = true;
6571
6725
  const block = {
6572
6726
  kind: "block",
6573
6727
  type: "loop",
@@ -6584,6 +6738,7 @@ function parseSequenceDgmo(content, palette) {
6584
6738
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6585
6739
  if (parallelMatch) {
6586
6740
  contentStarted = true;
6741
+ bodyStarted = true;
6587
6742
  const block = {
6588
6743
  kind: "block",
6589
6744
  type: "parallel",
@@ -6680,6 +6835,7 @@ function parseSequenceDgmo(content, palette) {
6680
6835
  lineNumber,
6681
6836
  endLineNumber: lineNumber
6682
6837
  };
6838
+ bodyStarted = true;
6683
6839
  currentContainer().push(note);
6684
6840
  continue;
6685
6841
  }
@@ -6704,6 +6860,7 @@ function parseSequenceDgmo(content, palette) {
6704
6860
  endLineNumber: i + 1
6705
6861
  // i has advanced past the body lines (1-based)
6706
6862
  };
6863
+ bodyStarted = true;
6707
6864
  currentContainer().push(note);
6708
6865
  continue;
6709
6866
  }
@@ -7548,6 +7705,15 @@ function parseNodeRef(text) {
7548
7705
  }
7549
7706
  return null;
7550
7707
  }
7708
+ function parseNodeRefLoose(text) {
7709
+ const t = text.trim();
7710
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7711
+ const m = t.match(shapeRe);
7712
+ if (!m) return null;
7713
+ const ref = parseNodeRef(m[1]);
7714
+ if (!ref) return null;
7715
+ return { ref, trailing: m[2].trim() };
7716
+ }
7551
7717
  function splitArrows(line11) {
7552
7718
  const segments = [];
7553
7719
  const arrowPositions = [];
@@ -7643,6 +7809,7 @@ function parseFlowchart(content, palette) {
7643
7809
  const notes = [];
7644
7810
  let contentStarted = false;
7645
7811
  let firstLineParsed = false;
7812
+ let prevLineLastNodeId = null;
7646
7813
  const nameAliasMap = /* @__PURE__ */ new Map();
7647
7814
  function peelAlias2(seg) {
7648
7815
  const trimmed = seg.trim();
@@ -7650,6 +7817,19 @@ function parseFlowchart(content, palette) {
7650
7817
  if (!m) return { seg: trimmed };
7651
7818
  return { seg: m[1].trim(), alias: m[2] };
7652
7819
  }
7820
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7821
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7822
+ if (suffixWarnedLines.has(lineNumber)) return;
7823
+ suffixWarnedLines.add(lineNumber);
7824
+ result.diagnostics.push(
7825
+ makeDgmoError(
7826
+ lineNumber,
7827
+ `Ignored unsupported text after a node shape: "${trailing}". Flowcharts have no tag groups or node metadata; node colors are assigned automatically by shape (e.g. terminals green/red, decisions yellow). Remove the suffix.`,
7828
+ "warning",
7829
+ "W_FLOWCHART_NODE_SUFFIX"
7830
+ )
7831
+ );
7832
+ }
7653
7833
  function getOrCreateNode(ref, lineNumber) {
7654
7834
  const key = ref.id;
7655
7835
  const existing = nodeMap.get(key);
@@ -7708,6 +7888,8 @@ function parseFlowchart(content, palette) {
7708
7888
  indentStack[indentStack.length - 1].nodeId
7709
7889
  ) : null;
7710
7890
  const segments = splitArrows(trimmed);
7891
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7892
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7711
7893
  if (segments.length === 1) {
7712
7894
  const peeled = peelAlias2(segments[0]);
7713
7895
  const ref = parseNodeRef(peeled.seg);
@@ -7717,6 +7899,13 @@ function parseFlowchart(content, palette) {
7717
7899
  indentStack.push({ nodeId: node.id, indent });
7718
7900
  return node.id;
7719
7901
  }
7902
+ const loose = parseNodeRefLoose(peeled.seg);
7903
+ if (loose) {
7904
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7905
+ const node = getOrCreateNode(loose.ref, lineNumber);
7906
+ indentStack.push({ nodeId: node.id, indent });
7907
+ return node.id;
7908
+ }
7720
7909
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7721
7910
  if (aliasResolved !== void 0) {
7722
7911
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7752,16 +7941,23 @@ function parseFlowchart(content, palette) {
7752
7941
  }
7753
7942
  }
7754
7943
  }
7944
+ if (!ref) {
7945
+ const loose = parseNodeRefLoose(peeled.seg);
7946
+ if (loose) {
7947
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7948
+ ref = loose.ref;
7949
+ }
7950
+ }
7755
7951
  if (!ref) continue;
7756
7952
  const node = getOrCreateNode(ref, lineNumber);
7757
7953
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7758
7954
  if (pendingArrow !== null) {
7759
- const sourceId = lastNodeId ?? implicitSourceId;
7955
+ const sourceId = lastNodeId ?? effectiveSource;
7760
7956
  if (sourceId) {
7761
7957
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7762
7958
  }
7763
7959
  pendingArrow = null;
7764
- } else if (lastNodeId === null && implicitSourceId === null) {
7960
+ } else if (lastNodeId === null && effectiveSource === null) {
7765
7961
  }
7766
7962
  lastNodeId = node.id;
7767
7963
  }
@@ -7843,7 +8039,8 @@ function parseFlowchart(content, palette) {
7843
8039
  continue;
7844
8040
  }
7845
8041
  }
7846
- processContentLine(trimmed, lineNumber, indent);
8042
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8043
+ if (lastId) prevLineLastNodeId = lastId;
7847
8044
  }
7848
8045
  if (result.nodes.length === 0 && !result.error) {
7849
8046
  const diag = makeDgmoError(
@@ -8926,7 +9123,12 @@ function parseERDiagram(content, palette) {
8926
9123
  }
8927
9124
  if (currentTagGroup && !contentStarted && indent > 0) {
8928
9125
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8929
- const { label, color } = extractColor(cleanEntry, palette);
9126
+ const { label, color } = extractColor(
9127
+ cleanEntry,
9128
+ palette,
9129
+ result.diagnostics,
9130
+ lineNumber
9131
+ );
8930
9132
  if (isDefault) {
8931
9133
  currentTagGroup.defaultValue = label;
8932
9134
  } else if (currentTagGroup.entries.length === 0) {
@@ -9244,12 +9446,7 @@ function parseOrg(content, palette) {
9244
9446
  diagnostics: [],
9245
9447
  error: null
9246
9448
  };
9247
- const fail = (line11, message) => {
9248
- const diag = makeDgmoError(line11, message);
9249
- result.diagnostics.push(diag);
9250
- result.error = formatDgmoError(diag);
9251
- return result;
9252
- };
9449
+ const fail = makeFail(result);
9253
9450
  const pushError = (line11, message) => {
9254
9451
  const diag = makeDgmoError(line11, message);
9255
9452
  result.diagnostics.push(diag);
@@ -9358,7 +9555,12 @@ function parseOrg(content, palette) {
9358
9555
  const indent2 = measureIndent(line11);
9359
9556
  if (indent2 > 0) {
9360
9557
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9361
- const { label, color } = extractColor(cleanEntry, palette);
9558
+ const { label, color } = extractColor(
9559
+ cleanEntry,
9560
+ palette,
9561
+ result.diagnostics,
9562
+ lineNumber
9563
+ );
9362
9564
  if (isDefault) {
9363
9565
  currentTagGroup.defaultValue = label;
9364
9566
  } else if (currentTagGroup.entries.length === 0) {
@@ -9644,12 +9846,7 @@ function parseSitemap(content, palette) {
9644
9846
  diagnostics: [],
9645
9847
  error: null
9646
9848
  };
9647
- const fail = (line11, message) => {
9648
- const diag = makeDgmoError(line11, message);
9649
- result.diagnostics.push(diag);
9650
- result.error = formatDgmoError(diag);
9651
- return result;
9652
- };
9849
+ const fail = makeFail(result);
9653
9850
  const pushError = (line11, message) => {
9654
9851
  const diag = makeDgmoError(line11, message);
9655
9852
  result.diagnostics.push(diag);
@@ -9750,7 +9947,12 @@ function parseSitemap(content, palette) {
9750
9947
  const indent2 = measureIndent(line11);
9751
9948
  if (indent2 > 0) {
9752
9949
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9753
- const { label, color } = extractColor(cleanEntry, palette);
9950
+ const { label, color } = extractColor(
9951
+ cleanEntry,
9952
+ palette,
9953
+ result.diagnostics,
9954
+ lineNumber
9955
+ );
9754
9956
  currentTagGroup.entries.push({
9755
9957
  value: label,
9756
9958
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11465,7 +11667,7 @@ var init_chart_types = __esm({
11465
11667
  },
11466
11668
  {
11467
11669
  id: "sequence",
11468
- description: "Message / interaction flows",
11670
+ description: "Message request and response interaction flows",
11469
11671
  fallback: true
11470
11672
  },
11471
11673
  {
@@ -11539,7 +11741,7 @@ var init_chart_types = __esm({
11539
11741
  },
11540
11742
  {
11541
11743
  id: "map",
11542
- description: "Geographic map: a value or count per country, state, or region (choropleth); points of interest; routes. Use when categories are real-world places."
11744
+ description: "Geographic concept map: highlight/score regions, drop points of interest, connect with routes or edges"
11543
11745
  },
11544
11746
  // ── Tier 3 — Specialized analytical charts ────────────────
11545
11747
  {
@@ -11556,7 +11758,7 @@ var init_chart_types = __esm({
11556
11758
  },
11557
11759
  {
11558
11760
  id: "slope",
11559
- description: "Change for multiple things between exactly two periods"
11761
+ description: "Change between 2 time periods"
11560
11762
  },
11561
11763
  {
11562
11764
  id: "sankey",
@@ -11585,7 +11787,7 @@ var init_chart_types = __esm({
11585
11787
  // ── Tier 4 — General-purpose data charts ──────────────────
11586
11788
  {
11587
11789
  id: "bar",
11588
- description: "Categorical comparisons",
11790
+ description: "Categorical comparisons for 3 - 5 figures",
11589
11791
  fallback: true
11590
11792
  },
11591
11793
  {
@@ -11847,7 +12049,9 @@ function parseChart(content, palette) {
11847
12049
  if (dataValues) {
11848
12050
  const { label: rawLabel, color: pointColor } = extractColor(
11849
12051
  dataValues.label,
11850
- palette
12052
+ palette,
12053
+ result.diagnostics,
12054
+ lineNumber
11851
12055
  );
11852
12056
  const [first, ...rest] = dataValues.values;
11853
12057
  result.data.push({
@@ -11912,6 +12116,12 @@ function parseChart(content, palette) {
11912
12116
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11913
12117
  );
11914
12118
  }
12119
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12120
+ warn(
12121
+ result.seriesLineNumber ?? 1,
12122
+ `Plain "bar" shows only the first series ("${result.seriesNames[0]}"); the other ${result.seriesNames.length - 1} are dropped at render. Use "bar-stacked" for stacked bars or "multi-line" to plot every series.`
12123
+ );
12124
+ }
11915
12125
  if (!result.error && result.seriesNames) {
11916
12126
  const expectedCount = result.seriesNames.length;
11917
12127
  for (const dp of result.data) {
@@ -12182,7 +12392,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12182
12392
  if (!dataRow || dataRow.values.length < 2) return null;
12183
12393
  const { label: rawLabel, color: pointColor } = extractColor(
12184
12394
  dataRow.label,
12185
- palette
12395
+ palette,
12396
+ diagnostics,
12397
+ lineNumber
12186
12398
  );
12187
12399
  return {
12188
12400
  name: rawLabel,
@@ -12320,11 +12532,15 @@ function parseExtendedChartFull(content, palette) {
12320
12532
  const targetResolved = resolveSlot(rawTarget);
12321
12533
  const { label: source, color: sourceColor } = extractColor(
12322
12534
  sourceResolved,
12323
- palette
12535
+ palette,
12536
+ result.diagnostics,
12537
+ lineNumber
12324
12538
  );
12325
12539
  const { label: target, color: targetColor } = extractColor(
12326
12540
  targetResolved,
12327
- palette
12541
+ palette,
12542
+ result.diagnostics,
12543
+ lineNumber
12328
12544
  );
12329
12545
  if (sourceColor || targetColor) {
12330
12546
  if (!result.nodeColors) result.nodeColors = {};
@@ -12376,7 +12592,9 @@ function parseExtendedChartFull(content, palette) {
12376
12592
  const targetResolved = resolveSlot(dataRow2.label);
12377
12593
  const { label: target, color: targetColor } = extractColor(
12378
12594
  targetResolved,
12379
- palette
12595
+ palette,
12596
+ result.diagnostics,
12597
+ lineNumber
12380
12598
  );
12381
12599
  if (targetColor) {
12382
12600
  if (!result.nodeColors) result.nodeColors = {};
@@ -12409,7 +12627,9 @@ function parseExtendedChartFull(content, palette) {
12409
12627
  const trimmedResolved = resolveSlot(trimmed);
12410
12628
  const { label: nodeName, color: nodeColor2 } = extractColor(
12411
12629
  trimmedResolved,
12412
- palette
12630
+ palette,
12631
+ result.diagnostics,
12632
+ lineNumber
12413
12633
  );
12414
12634
  if (nodeColor2) {
12415
12635
  if (!result.nodeColors) result.nodeColors = {};
@@ -12499,8 +12719,11 @@ function parseExtendedChartFull(content, palette) {
12499
12719
  min: parseFloat(rangeMatch[1]),
12500
12720
  max: parseFloat(rangeMatch[2])
12501
12721
  };
12722
+ continue;
12723
+ }
12724
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12725
+ continue;
12502
12726
  }
12503
- continue;
12504
12727
  }
12505
12728
  }
12506
12729
  if (firstToken === "no-name") {
@@ -12560,7 +12783,9 @@ function parseExtendedChartFull(content, palette) {
12560
12783
  if (colonIndex >= 0) {
12561
12784
  const { label: fnName, color: fnColor } = extractColor(
12562
12785
  trimmed.substring(0, colonIndex).trim(),
12563
- palette
12786
+ palette,
12787
+ result.diagnostics,
12788
+ lineNumber
12564
12789
  );
12565
12790
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12566
12791
  if (!result.functions) result.functions = [];
@@ -12608,7 +12833,9 @@ function parseExtendedChartFull(content, palette) {
12608
12833
  if (dataRow?.values.length === 1) {
12609
12834
  const { label: rawLabel, color: pointColor } = extractColor(
12610
12835
  dataRow.label,
12611
- palette
12836
+ palette,
12837
+ result.diagnostics,
12838
+ lineNumber
12612
12839
  );
12613
12840
  result.data.push({
12614
12841
  label: rawLabel,
@@ -12707,11 +12934,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12707
12934
  const sc = ctx ?? ScaleContext.identity();
12708
12935
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12709
12936
  if (parsed.type === "sankey") {
12710
- const bg = isDark ? palette.surface : palette.bg;
12937
+ const bg = themeBaseBg(palette, isDark);
12711
12938
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12712
12939
  }
12713
12940
  if (parsed.type === "chord") {
12714
- const bg = isDark ? palette.surface : palette.bg;
12941
+ const bg = themeBaseBg(palette, isDark);
12715
12942
  return buildChordOption(
12716
12943
  parsed,
12717
12944
  palette,
@@ -12752,7 +12979,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12752
12979
  );
12753
12980
  }
12754
12981
  if (parsed.type === "funnel") {
12755
- const bg = isDark ? palette.surface : palette.bg;
12982
+ const bg = themeBaseBg(palette, isDark);
12756
12983
  return buildFunnelOption(
12757
12984
  parsed,
12758
12985
  palette,
@@ -13348,7 +13575,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13348
13575
  const gridLeft = parsed.ylabel ? 12 : 3;
13349
13576
  const gridRight = 4;
13350
13577
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13351
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13578
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13579
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13352
13580
  let graphic;
13353
13581
  if (showLabels && points.length > 0) {
13354
13582
  const labelPoints = [];
@@ -13488,7 +13716,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13488
13716
  }
13489
13717
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13490
13718
  const sc = ctx ?? ScaleContext.identity();
13491
- const bg = isDark ? palette.surface : palette.bg;
13719
+ const bg = themeBaseBg(palette, isDark);
13492
13720
  const heatmapRows = parsed.heatmapRows ?? [];
13493
13721
  const columns = parsed.columns ?? [];
13494
13722
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13807,7 +14035,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13807
14035
  colors,
13808
14036
  titleConfig
13809
14037
  } = buildChartCommons(parsed, palette, isDark, sc);
13810
- const bg = isDark ? palette.surface : palette.bg;
14038
+ const bg = themeBaseBg(palette, isDark);
13811
14039
  switch (parsed.type) {
13812
14040
  case "bar":
13813
14041
  return buildBarOption(
@@ -14755,12 +14983,7 @@ function parseKanban(content, palette) {
14755
14983
  diagnostics: [],
14756
14984
  error: null
14757
14985
  };
14758
- const fail = (line11, message) => {
14759
- const diag = makeDgmoError(line11, message);
14760
- result.diagnostics.push(diag);
14761
- result.error = formatDgmoError(diag);
14762
- return result;
14763
- };
14986
+ const fail = makeFail(result);
14764
14987
  const warn = (line11, message) => {
14765
14988
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14766
14989
  };
@@ -14863,7 +15086,12 @@ function parseKanban(content, palette) {
14863
15086
  const indent2 = measureIndent(line11);
14864
15087
  if (indent2 > 0) {
14865
15088
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14866
- const { label, color } = extractColor(cleanEntry, palette);
15089
+ const { label, color } = extractColor(
15090
+ cleanEntry,
15091
+ palette,
15092
+ result.diagnostics,
15093
+ lineNumber
15094
+ );
14867
15095
  if (isDefault) {
14868
15096
  currentTagGroup.defaultValue = label;
14869
15097
  } else if (currentTagGroup.entries.length === 0) {
@@ -15207,12 +15435,7 @@ function parseC4(content, palette) {
15207
15435
  if (!result.error && severity === "error")
15208
15436
  result.error = formatDgmoError(diag);
15209
15437
  };
15210
- const fail = (line11, message) => {
15211
- const diag = makeDgmoError(line11, message);
15212
- result.diagnostics.push(diag);
15213
- result.error = formatDgmoError(diag);
15214
- return result;
15215
- };
15438
+ const fail = makeFail(result);
15216
15439
  if (!content?.trim()) {
15217
15440
  return fail(0, "No content provided");
15218
15441
  }
@@ -15313,7 +15536,12 @@ function parseC4(content, palette) {
15313
15536
  const indent2 = measureIndent(line11);
15314
15537
  if (indent2 > 0) {
15315
15538
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15316
- const { label, color } = extractColor(cleanEntry, palette);
15539
+ const { label, color } = extractColor(
15540
+ cleanEntry,
15541
+ palette,
15542
+ result.diagnostics,
15543
+ lineNumber
15544
+ );
15317
15545
  if (isDefault) {
15318
15546
  currentTagGroup.defaultValue = label;
15319
15547
  } else if (currentTagGroup.entries.length === 0) {
@@ -16978,8 +17206,66 @@ function parseInfra(content) {
16978
17206
  }
16979
17207
  }
16980
17208
  validateTagGroupNames(result.tagGroups, warn, setError);
17209
+ checkReachability(result);
16981
17210
  return result;
16982
17211
  }
17212
+ function checkReachability(result) {
17213
+ if (result.nodes.length === 0) return;
17214
+ const entries = result.nodes.filter((n) => n.isEdge);
17215
+ if (entries.length === 0) {
17216
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17217
+ result.diagnostics.push(
17218
+ makeDgmoError(
17219
+ line11,
17220
+ `Infra diagram has no 'internet' or 'edge' entry point \u2014 an infra diagram traces request traffic from an entry inward, so without one nothing carries traffic. Add an 'internet' or 'edge' node and route from it.`,
17221
+ "warning",
17222
+ "W_INFRA_NO_ENTRY"
17223
+ )
17224
+ );
17225
+ return;
17226
+ }
17227
+ const groupChildMap = /* @__PURE__ */ new Map();
17228
+ for (const node of result.nodes) {
17229
+ if (node.groupId) {
17230
+ const list = groupChildMap.get(node.groupId) ?? [];
17231
+ list.push(node.id);
17232
+ groupChildMap.set(node.groupId, list);
17233
+ }
17234
+ }
17235
+ const outbound = /* @__PURE__ */ new Map();
17236
+ for (const edge of result.edges) {
17237
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17238
+ const list = outbound.get(edge.sourceId) ?? [];
17239
+ list.push(...targets);
17240
+ outbound.set(edge.sourceId, list);
17241
+ }
17242
+ const reachable = /* @__PURE__ */ new Set();
17243
+ const queue = [];
17244
+ for (const entry of entries) {
17245
+ reachable.add(entry.id);
17246
+ queue.push(entry.id);
17247
+ }
17248
+ while (queue.length > 0) {
17249
+ const current = queue.shift();
17250
+ for (const next of outbound.get(current) ?? []) {
17251
+ if (!reachable.has(next)) {
17252
+ reachable.add(next);
17253
+ queue.push(next);
17254
+ }
17255
+ }
17256
+ }
17257
+ for (const node of result.nodes) {
17258
+ if (node.isEdge || reachable.has(node.id)) continue;
17259
+ result.diagnostics.push(
17260
+ makeDgmoError(
17261
+ node.lineNumber,
17262
+ `'${node.label}' is unreachable from an 'internet'/'edge' entry \u2014 no request traffic flows to it, so it's dead on an infra diagram. Connect it downstream of an entry, or remove it.`,
17263
+ "warning",
17264
+ "W_INFRA_UNREACHABLE"
17265
+ )
17266
+ );
17267
+ }
17268
+ }
16983
17269
  function stripNodeDecorations(name) {
16984
17270
  let s = name.trim();
16985
17271
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17269,7 +17555,12 @@ function parseGantt(content, palette) {
17269
17555
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17270
17556
  if (eraEntryMatch) {
17271
17557
  const eraLabelRaw = eraEntryMatch[3].trim();
17272
- const eraExtracted = extractColor(eraLabelRaw, palette);
17558
+ const eraExtracted = extractColor(
17559
+ eraLabelRaw,
17560
+ palette,
17561
+ diagnostics,
17562
+ lineNumber
17563
+ );
17273
17564
  result.eras.push({
17274
17565
  startDate: eraEntryMatch[1],
17275
17566
  endDate: eraEntryMatch[2],
@@ -17291,7 +17582,12 @@ function parseGantt(content, palette) {
17291
17582
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17292
17583
  if (markerEntryMatch) {
17293
17584
  const markerLabelRaw = markerEntryMatch[2].trim();
17294
- const markerExtracted = extractColor(markerLabelRaw, palette);
17585
+ const markerExtracted = extractColor(
17586
+ markerLabelRaw,
17587
+ palette,
17588
+ diagnostics,
17589
+ lineNumber
17590
+ );
17295
17591
  result.markers.push({
17296
17592
  date: markerEntryMatch[1],
17297
17593
  label: markerExtracted.label,
@@ -17312,7 +17608,12 @@ function parseGantt(content, palette) {
17312
17608
  } else {
17313
17609
  if (COMMENT_RE.test(line11)) continue;
17314
17610
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17315
- const extracted = extractColor(cleanEntry, palette);
17611
+ const extracted = extractColor(
17612
+ cleanEntry,
17613
+ palette,
17614
+ diagnostics,
17615
+ lineNumber
17616
+ );
17316
17617
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17317
17618
  const isFirstEntry = currentTagGroup.entries.length === 0;
17318
17619
  currentTagGroup.entries.push({
@@ -17507,7 +17808,12 @@ function parseGantt(content, palette) {
17507
17808
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17508
17809
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17509
17810
  const eraLabelRaw = eraOffsetMatch[3].trim();
17510
- const eraExtracted = extractColor(eraLabelRaw, palette);
17811
+ const eraExtracted = extractColor(
17812
+ eraLabelRaw,
17813
+ palette,
17814
+ diagnostics,
17815
+ lineNumber
17816
+ );
17511
17817
  result.eras.push({
17512
17818
  startDate: "",
17513
17819
  endDate: "",
@@ -17523,7 +17829,12 @@ function parseGantt(content, palette) {
17523
17829
  if (markerOffsetMatch) {
17524
17830
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17525
17831
  const markerLabelRaw = markerOffsetMatch[2].trim();
17526
- const markerExtracted = extractColor(markerLabelRaw, palette);
17832
+ const markerExtracted = extractColor(
17833
+ markerLabelRaw,
17834
+ palette,
17835
+ diagnostics,
17836
+ lineNumber
17837
+ );
17527
17838
  result.markers.push({
17528
17839
  date: "",
17529
17840
  label: markerExtracted.label,
@@ -17597,7 +17908,12 @@ function parseGantt(content, palette) {
17597
17908
  const eraMatch = line11.match(ERA_RE);
17598
17909
  if (eraMatch) {
17599
17910
  const eraLabelRaw = eraMatch[3].trim();
17600
- const eraExtracted = extractColor(eraLabelRaw, palette);
17911
+ const eraExtracted = extractColor(
17912
+ eraLabelRaw,
17913
+ palette,
17914
+ diagnostics,
17915
+ lineNumber
17916
+ );
17601
17917
  result.eras.push({
17602
17918
  startDate: eraMatch[1],
17603
17919
  endDate: eraMatch[2],
@@ -17615,7 +17931,12 @@ function parseGantt(content, palette) {
17615
17931
  const markerMatch = line11.match(MARKER_RE);
17616
17932
  if (markerMatch) {
17617
17933
  const markerLabelRaw = markerMatch[2].trim();
17618
- const markerExtracted = extractColor(markerLabelRaw, palette);
17934
+ const markerExtracted = extractColor(
17935
+ markerLabelRaw,
17936
+ palette,
17937
+ diagnostics,
17938
+ lineNumber
17939
+ );
17619
17940
  result.markers.push({
17620
17941
  date: markerMatch[1],
17621
17942
  label: markerExtracted.label,
@@ -19087,7 +19408,7 @@ function parseBoxesAndLines(content, palette) {
19087
19408
  const trimmed = raw.trim();
19088
19409
  const indent = measureIndent2(raw);
19089
19410
  if (!trimmed || trimmed.startsWith("//")) continue;
19090
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19411
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
19091
19412
  result.diagnostics.push(
19092
19413
  makeDgmoError(
19093
19414
  lineNum,
@@ -19216,7 +19537,12 @@ function parseBoxesAndLines(content, palette) {
19216
19537
  if (tagBlockMatch.inlineValues) {
19217
19538
  for (const rawVal of tagBlockMatch.inlineValues) {
19218
19539
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19219
- const { label, color } = extractColor(cleanVal);
19540
+ const { label, color } = extractColor(
19541
+ cleanVal,
19542
+ palette,
19543
+ result.diagnostics,
19544
+ lineNum
19545
+ );
19220
19546
  newTagGroup.entries.push({
19221
19547
  value: label,
19222
19548
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19233,7 +19559,12 @@ function parseBoxesAndLines(content, palette) {
19233
19559
  }
19234
19560
  if (currentTagGroup && !contentStarted && indent > 0) {
19235
19561
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19236
- const { label, color } = extractColor(cleanEntry);
19562
+ const { label, color } = extractColor(
19563
+ cleanEntry,
19564
+ palette,
19565
+ result.diagnostics,
19566
+ lineNum
19567
+ );
19237
19568
  currentTagGroup.entries.push({
19238
19569
  value: label,
19239
19570
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19831,7 +20162,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19831
20162
  metadata
19832
20163
  };
19833
20164
  }
19834
- var MAX_GROUP_DEPTH;
20165
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19835
20166
  var init_parser18 = __esm({
19836
20167
  "src/boxes-and-lines/parser.ts"() {
19837
20168
  "use strict";
@@ -19843,6 +20174,8 @@ var init_parser18 = __esm({
19843
20174
  init_reserved_key_registry();
19844
20175
  init_notes();
19845
20176
  MAX_GROUP_DEPTH = 2;
20177
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20178
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19846
20179
  }
19847
20180
  });
19848
20181
 
@@ -19862,12 +20195,7 @@ function parseMindmap(content, palette) {
19862
20195
  diagnostics: [],
19863
20196
  error: null
19864
20197
  };
19865
- const fail = (line11, message) => {
19866
- const diag = makeDgmoError(line11, message);
19867
- result.diagnostics.push(diag);
19868
- result.error = formatDgmoError(diag);
19869
- return result;
19870
- };
20198
+ const fail = makeFail(result);
19871
20199
  const pushError = (line11, message) => {
19872
20200
  const diag = makeDgmoError(line11, message);
19873
20201
  result.diagnostics.push(diag);
@@ -19980,7 +20308,12 @@ function parseMindmap(content, palette) {
19980
20308
  const indent2 = measureIndent(line11);
19981
20309
  if (indent2 > 0) {
19982
20310
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19983
- const { label, color } = extractColor(cleanEntry, palette);
20311
+ const { label, color } = extractColor(
20312
+ cleanEntry,
20313
+ palette,
20314
+ result.diagnostics,
20315
+ lineNumber
20316
+ );
19984
20317
  if (isDefault) {
19985
20318
  currentTagGroup.defaultValue = label;
19986
20319
  } else if (currentTagGroup.entries.length === 0) {
@@ -20057,6 +20390,7 @@ function parseMindmap(content, palette) {
20057
20390
  collectAll(result.roots);
20058
20391
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
20059
20392
  validateTagGroupNames(result.tagGroups, pushWarning);
20393
+ cascadeTagMetadata(result.roots, result.tagGroups);
20060
20394
  }
20061
20395
  if (result.roots.length === 0 && !result.error) {
20062
20396
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20663,7 +20997,12 @@ function parseWireframe(content) {
20663
20997
  }
20664
20998
  if (indent > 0 && currentTagGroup) {
20665
20999
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20666
- const { label, color } = extractColor(cleanEntry);
21000
+ const { label, color } = extractColor(
21001
+ cleanEntry,
21002
+ void 0,
21003
+ diagnostics,
21004
+ lineNumber
21005
+ );
20667
21006
  currentTagGroup.entries.push({
20668
21007
  value: label,
20669
21008
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20911,12 +21250,7 @@ function parseTechRadar(content) {
20911
21250
  diagnostics: [],
20912
21251
  error: null
20913
21252
  };
20914
- const fail = (line11, message) => {
20915
- const diag = makeDgmoError(line11, message);
20916
- result.diagnostics.push(diag);
20917
- result.error = formatDgmoError(diag);
20918
- return result;
20919
- };
21253
+ const fail = makeFail(result);
20920
21254
  const warn = (line11, message) => {
20921
21255
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20922
21256
  };
@@ -21284,12 +21618,7 @@ function parseCycle(content) {
21284
21618
  let state = "top";
21285
21619
  let currentNode = null;
21286
21620
  let currentEdge = null;
21287
- const fail = (line11, message) => {
21288
- const diag = makeDgmoError(line11, message);
21289
- result.diagnostics.push(diag);
21290
- result.error = formatDgmoError(diag);
21291
- return result;
21292
- };
21621
+ const fail = makeFail(result);
21293
21622
  const warn = (line11, message, severity = "warning") => {
21294
21623
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21295
21624
  };
@@ -21566,12 +21895,7 @@ function parseJourneyMap(content, palette) {
21566
21895
  diagnostics: [],
21567
21896
  error: null
21568
21897
  };
21569
- const fail = (line11, message) => {
21570
- const diag = makeDgmoError(line11, message);
21571
- result.diagnostics.push(diag);
21572
- result.error = formatDgmoError(diag);
21573
- return result;
21574
- };
21898
+ const fail = makeFail(result);
21575
21899
  const warn = (line11, message) => {
21576
21900
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21577
21901
  };
@@ -21720,7 +22044,12 @@ function parseJourneyMap(content, palette) {
21720
22044
  if (currentTagGroup && !contentStarted) {
21721
22045
  if (indent > 0) {
21722
22046
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21723
- const { label, color } = extractColor(cleanEntry, palette);
22047
+ const { label, color } = extractColor(
22048
+ cleanEntry,
22049
+ palette,
22050
+ result.diagnostics,
22051
+ lineNumber
22052
+ );
21724
22053
  if (isDefault) {
21725
22054
  currentTagGroup.defaultValue = label;
21726
22055
  } else if (currentTagGroup.entries.length === 0) {
@@ -22092,12 +22421,7 @@ function parsePyramid(content) {
22092
22421
  const lines = content.split("\n");
22093
22422
  let headerParsed = false;
22094
22423
  let currentLayer = null;
22095
- const fail = (line11, message) => {
22096
- const diag = makeDgmoError(line11, message);
22097
- result.diagnostics.push(diag);
22098
- result.error = formatDgmoError(diag);
22099
- return result;
22100
- };
22424
+ const fail = makeFail(result);
22101
22425
  const warn = (line11, message, severity = "warning") => {
22102
22426
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22103
22427
  };
@@ -22244,12 +22568,7 @@ function parseRing(content) {
22244
22568
  const lines = content.split("\n");
22245
22569
  let headerParsed = false;
22246
22570
  let currentLayer = null;
22247
- const fail = (line11, message) => {
22248
- const diag = makeDgmoError(line11, message);
22249
- result.diagnostics.push(diag);
22250
- result.error = formatDgmoError(diag);
22251
- return result;
22252
- };
22571
+ const fail = makeFail(result);
22253
22572
  const warn = (line11, message, severity = "warning") => {
22254
22573
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22255
22574
  };
@@ -22713,12 +23032,7 @@ function parseRaci(content, palette) {
22713
23032
  diagnostics: [],
22714
23033
  error: null
22715
23034
  };
22716
- const fail = (line11, message) => {
22717
- const diag = makeDgmoError(line11, message);
22718
- result.diagnostics.push(diag);
22719
- result.error = formatDgmoError(diag);
22720
- return result;
22721
- };
23035
+ const fail = makeFail(result);
22722
23036
  const warn = (line11, message, code) => {
22723
23037
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22724
23038
  };
@@ -23321,6 +23635,81 @@ function measureInfra(content) {
23321
23635
  const parsed = parseInfra(content);
23322
23636
  return { nodes: parsed.nodes.length };
23323
23637
  }
23638
+ function minDimsSequence(c) {
23639
+ return {
23640
+ width: Math.max((c.participants ?? 2) * 80, 320),
23641
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23642
+ };
23643
+ }
23644
+ function minDimsRaci(c) {
23645
+ return {
23646
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23647
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23648
+ };
23649
+ }
23650
+ function minDimsMindmap(c) {
23651
+ return {
23652
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23653
+ height: Math.max((c.depth ?? 2) * 60, 200)
23654
+ };
23655
+ }
23656
+ function minDimsTechRadar() {
23657
+ return { width: 360, height: 400 };
23658
+ }
23659
+ function minDimsHeatmap(c) {
23660
+ return {
23661
+ width: Math.max((c.columns ?? 3) * 40, 300),
23662
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23663
+ };
23664
+ }
23665
+ function minDimsArc(c) {
23666
+ return {
23667
+ width: 300,
23668
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23669
+ };
23670
+ }
23671
+ function minDimsOrg(c) {
23672
+ return {
23673
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23674
+ height: Math.max((c.depth ?? 2) * 80, 200)
23675
+ };
23676
+ }
23677
+ function minDimsGantt(c) {
23678
+ return {
23679
+ width: 400,
23680
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23681
+ };
23682
+ }
23683
+ function minDimsKanban(c) {
23684
+ return {
23685
+ width: Math.max((c.columns ?? 3) * 120, 360),
23686
+ height: 300
23687
+ };
23688
+ }
23689
+ function minDimsEntities(c) {
23690
+ return {
23691
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23692
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23693
+ };
23694
+ }
23695
+ function minDimsGraph(c) {
23696
+ return {
23697
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23698
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23699
+ };
23700
+ }
23701
+ function minDimsPert(c) {
23702
+ return {
23703
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23704
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23705
+ };
23706
+ }
23707
+ function minDimsInfra(c) {
23708
+ return {
23709
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23710
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23711
+ };
23712
+ }
23324
23713
  function isExtendedChartParser(parse) {
23325
23714
  return EXTENDED_CHART_DOORS.has(parse);
23326
23715
  }
@@ -23364,33 +23753,50 @@ var init_chart_type_registry = __esm({
23364
23753
  id: "sequence",
23365
23754
  category: "diagram",
23366
23755
  parse: parseSequenceDgmo,
23367
- measure: measureSequence
23756
+ measure: measureSequence,
23757
+ minDims: minDimsSequence
23368
23758
  },
23369
23759
  {
23370
23760
  id: "flowchart",
23371
23761
  category: "diagram",
23372
23762
  parse: parseFlowchart,
23373
- measure: measureFlowchart
23763
+ measure: measureFlowchart,
23764
+ minDims: minDimsGraph
23374
23765
  },
23375
23766
  {
23376
23767
  id: "class",
23377
23768
  category: "diagram",
23378
23769
  parse: parseClassDiagram,
23379
- measure: measureClass
23770
+ measure: measureClass,
23771
+ minDims: minDimsEntities
23772
+ },
23773
+ {
23774
+ id: "er",
23775
+ category: "diagram",
23776
+ parse: parseERDiagram,
23777
+ measure: measureER,
23778
+ minDims: minDimsEntities
23380
23779
  },
23381
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23382
23780
  {
23383
23781
  id: "state",
23384
23782
  category: "diagram",
23385
23783
  parse: parseState,
23386
- measure: measureStateGraph
23784
+ measure: measureStateGraph,
23785
+ minDims: minDimsGraph
23786
+ },
23787
+ {
23788
+ id: "org",
23789
+ category: "diagram",
23790
+ parse: parseOrg,
23791
+ measure: measureOrg,
23792
+ minDims: minDimsOrg
23387
23793
  },
23388
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23389
23794
  {
23390
23795
  id: "kanban",
23391
23796
  category: "diagram",
23392
23797
  parse: parseKanban,
23393
- measure: measureKanban
23798
+ measure: measureKanban,
23799
+ minDims: minDimsKanban
23394
23800
  },
23395
23801
  { id: "c4", category: "diagram", parse: parseC4 },
23396
23802
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23398,25 +23804,40 @@ var init_chart_type_registry = __esm({
23398
23804
  id: "infra",
23399
23805
  category: "diagram",
23400
23806
  parse: parseInfra,
23401
- measure: measureInfra
23807
+ measure: measureInfra,
23808
+ minDims: minDimsInfra
23402
23809
  },
23403
23810
  {
23404
23811
  id: "gantt",
23405
23812
  category: "diagram",
23406
23813
  parse: parseGantt,
23407
- measure: measureGantt
23814
+ measure: measureGantt,
23815
+ minDims: minDimsGantt
23816
+ },
23817
+ {
23818
+ id: "pert",
23819
+ category: "diagram",
23820
+ parse: parsePert,
23821
+ measure: measurePert,
23822
+ minDims: minDimsPert
23408
23823
  },
23409
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23410
23824
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23411
23825
  {
23412
23826
  id: "mindmap",
23413
23827
  category: "diagram",
23414
23828
  parse: parseMindmap,
23415
- measure: measureMindmap
23829
+ measure: measureMindmap,
23830
+ minDims: minDimsMindmap
23416
23831
  },
23417
23832
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23418
23833
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23419
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23834
+ {
23835
+ id: "raci",
23836
+ category: "diagram",
23837
+ parse: parseRaci,
23838
+ measure: measureRaci,
23839
+ minDims: minDimsRaci
23840
+ },
23420
23841
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23421
23842
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23422
23843
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23438,7 +23859,8 @@ var init_chart_type_registry = __esm({
23438
23859
  id: "heatmap",
23439
23860
  category: "data-chart",
23440
23861
  parse: parseHeatmap,
23441
- measure: measureHeatmap
23862
+ measure: measureHeatmap,
23863
+ minDims: minDimsHeatmap
23442
23864
  },
23443
23865
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23444
23866
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23448,7 +23870,8 @@ var init_chart_type_registry = __esm({
23448
23870
  id: "arc",
23449
23871
  category: "visualization",
23450
23872
  parse: parseArc,
23451
- measure: measureArc
23873
+ measure: measureArc,
23874
+ minDims: minDimsArc
23452
23875
  },
23453
23876
  { id: "timeline", category: "visualization", parse: parseTimeline },
23454
23877
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23458,7 +23881,8 @@ var init_chart_type_registry = __esm({
23458
23881
  id: "tech-radar",
23459
23882
  category: "visualization",
23460
23883
  parse: parseTechRadar,
23461
- measure: measureTechRadar
23884
+ measure: measureTechRadar,
23885
+ minDims: minDimsTechRadar
23462
23886
  },
23463
23887
  { id: "cycle", category: "visualization", parse: parseCycle },
23464
23888
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24680,7 +25104,7 @@ function nodeStroke(palette, nodeColor2) {
24680
25104
  }
24681
25105
  function containerFill(palette, isDark, nodeColor2) {
24682
25106
  if (nodeColor2) {
24683
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25107
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24684
25108
  }
24685
25109
  return mix(palette.surface, palette.bg, 40);
24686
25110
  }
@@ -24838,10 +25262,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24838
25262
  const iconY = iconPad;
24839
25263
  const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24840
25264
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25265
+ const iconColor = contrastText(
25266
+ fill2,
25267
+ palette.textOnFillLight,
25268
+ palette.textOnFillDark
25269
+ );
24841
25270
  const cx = iconSize / 2;
24842
25271
  const cy = iconSize / 2;
24843
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
24844
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25272
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25273
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24845
25274
  }
24846
25275
  }
24847
25276
  for (const edge of layout.edges) {
@@ -24930,10 +25359,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24930
25359
  const iconY = iconPad;
24931
25360
  const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24932
25361
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25362
+ const iconColor = labelColor;
24933
25363
  const cx = iconSize / 2;
24934
25364
  const cy = iconSize / 2;
24935
- 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);
24936
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25365
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25366
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24937
25367
  }
24938
25368
  }
24939
25369
  if (hasAncestorTrail) {
@@ -25855,7 +26285,7 @@ function nodeStroke2(_palette, nodeColor2) {
25855
26285
  }
25856
26286
  function containerFill2(palette, isDark, nodeColor2) {
25857
26287
  if (nodeColor2) {
25858
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26288
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25859
26289
  }
25860
26290
  return mix(palette.surface, palette.bg, 40);
25861
26291
  }
@@ -28662,7 +29092,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28662
29092
  onToggleDescriptions,
28663
29093
  onToggleControlsExpand,
28664
29094
  exportMode = false,
28665
- controlsHost
29095
+ controlsHost,
29096
+ rampDomain
28666
29097
  } = options ?? {};
28667
29098
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28668
29099
  const width = exportDims?.width ?? container.clientWidth;
@@ -28682,8 +29113,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28682
29113
  const sTitleY = sctx.structural(TITLE_Y);
28683
29114
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28684
29115
  const hasRamp = nodeValues.length > 0;
28685
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28686
- const rampMax = Math.max(...nodeValues);
29116
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29117
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28687
29118
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28688
29119
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28689
29120
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28835,7 +29266,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28835
29266
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28836
29267
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28837
29268
  if (group.collapsed) {
28838
- const fillColor = isDark ? palette.surface : palette.bg;
29269
+ const fillColor = themeBaseBg(palette, isDark);
28839
29270
  const strokeColor = palette.border;
28840
29271
  groupG.append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", group.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", sNodeStrokeWidth);
28841
29272
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -29191,6 +29622,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29191
29622
  });
29192
29623
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29193
29624
  }
29625
+ if (!exportDims && !exportMode) {
29626
+ const iconSize = 14;
29627
+ const focusG = svg.append("g").attr("class", "bl-focus-icon").attr("data-export-ignore", "true").style("display", "none").style("pointer-events", "auto").style("cursor", "pointer");
29628
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29629
+ const cx = iconSize / 2;
29630
+ const cy = iconSize / 2;
29631
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", palette.bg).attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
29632
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29633
+ }
29194
29634
  }
29195
29635
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29196
29636
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29787,7 +30227,7 @@ function shuffle(a, r) {
29787
30227
  return x;
29788
30228
  }
29789
30229
  function flatten(d) {
29790
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30230
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29791
30231
  const pts = [];
29792
30232
  let i = 0, cx = 0, cy = 0, cmd = "";
29793
30233
  const num = () => parseFloat(toks[i++]);
@@ -29831,17 +30271,10 @@ function flatten(d) {
29831
30271
  }
29832
30272
  return pts;
29833
30273
  }
29834
- function segPoint(p1, p2, p3, p4) {
29835
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29836
- if (Math.abs(den) < 1e-9) return null;
29837
- 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;
29838
- 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;
29839
- }
29840
- function countSplineCrossings(layout) {
29841
- const center = /* @__PURE__ */ new Map();
29842
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29843
- for (const g of layout.groups)
29844
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30274
+ function flatPolys(layout) {
30275
+ const key = layout.edges;
30276
+ const hit = FLAT_CACHE.get(key);
30277
+ if (hit) return hit;
29845
30278
  const polys = layout.edges.map((e) => {
29846
30279
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29847
30280
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29851,8 +30284,24 @@ function countSplineCrossings(layout) {
29851
30284
  if (p.y < y0) y0 = p.y;
29852
30285
  if (p.y > y1) y1 = p.y;
29853
30286
  }
29854
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30287
+ return { pts, x0, y0, x1, y1 };
29855
30288
  });
30289
+ FLAT_CACHE.set(key, polys);
30290
+ return polys;
30291
+ }
30292
+ function segPoint(p1, p2, p3, p4) {
30293
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30294
+ if (Math.abs(den) < 1e-9) return null;
30295
+ const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
30296
+ return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
30297
+ }
30298
+ function countSplineCrossings(layout, floor = Infinity) {
30299
+ const center = /* @__PURE__ */ new Map();
30300
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30301
+ for (const g of layout.groups)
30302
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30303
+ const polys = flatPolys(layout);
30304
+ const edges = layout.edges;
29856
30305
  const R = 34;
29857
30306
  let total = 0;
29858
30307
  for (let a = 0; a < polys.length; a++)
@@ -29860,23 +30309,33 @@ function countSplineCrossings(layout) {
29860
30309
  const A = polys[a], B = polys[b];
29861
30310
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29862
30311
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29863
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30312
+ const ea = edges[a], eb = edges[b];
30313
+ let sh0, sh1;
30314
+ if (ea.source === eb.source || ea.source === eb.target)
30315
+ sh0 = center.get(ea.source);
30316
+ if (ea.target === eb.source || ea.target === eb.target)
30317
+ sh1 = center.get(ea.target);
29864
30318
  const hits = [];
29865
- for (let i = 1; i < A.pts.length; i++)
29866
- for (let j = 1; j < B.pts.length; j++) {
29867
- const p = segPoint(
29868
- A.pts[i - 1],
29869
- A.pts[i],
29870
- B.pts[j - 1],
29871
- B.pts[j]
29872
- );
30319
+ const ap = A.pts, bp = B.pts;
30320
+ for (let i = 1; i < ap.length; i++) {
30321
+ const a0 = ap[i - 1], a1 = ap[i];
30322
+ const axMin = a0.x < a1.x ? a0.x : a1.x, axMax = a0.x > a1.x ? a0.x : a1.x, ayMin = a0.y < a1.y ? a0.y : a1.y, ayMax = a0.y > a1.y ? a0.y : a1.y;
30323
+ for (let j = 1; j < bp.length; j++) {
30324
+ const b0 = bp[j - 1], b1 = bp[j];
30325
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30326
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30327
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30328
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30329
+ const p = segPoint(a0, a1, b0, b1);
29873
30330
  if (!p) continue;
29874
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29875
- continue;
30331
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30332
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29876
30333
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29877
30334
  hits.push(p);
29878
30335
  }
30336
+ }
29879
30337
  total += hits.length;
30338
+ if (total > floor) return total;
29880
30339
  }
29881
30340
  return total;
29882
30341
  }
@@ -29914,17 +30373,8 @@ function detectEdgeOverlaps(layout, opts) {
29914
30373
  w: g.width,
29915
30374
  h: g.height
29916
30375
  });
29917
- const polys = layout.edges.map((e) => {
29918
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29919
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29920
- for (const p of pts) {
29921
- if (p.x < x0) x0 = p.x;
29922
- if (p.x > x1) x1 = p.x;
29923
- if (p.y < y0) y0 = p.y;
29924
- if (p.y > y1) y1 = p.y;
29925
- }
29926
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29927
- });
30376
+ const polys = flatPolys(layout);
30377
+ const edges = layout.edges;
29928
30378
  const runs = [];
29929
30379
  for (let a = 0; a < polys.length; a++)
29930
30380
  for (let b = a + 1; b < polys.length; b++) {
@@ -29932,7 +30382,12 @@ function detectEdgeOverlaps(layout, opts) {
29932
30382
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29933
30383
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29934
30384
  continue;
29935
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30385
+ const ea = edges[a], eb = edges[b];
30386
+ let shr0, shr1;
30387
+ if (ea.source === eb.source || ea.source === eb.target)
30388
+ shr0 = rect.get(ea.source);
30389
+ if (ea.target === eb.source || ea.target === eb.target)
30390
+ shr1 = rect.get(ea.target);
29936
30391
  let run = [];
29937
30392
  let runLen = 0;
29938
30393
  const flush = () => {
@@ -29946,7 +30401,7 @@ function detectEdgeOverlaps(layout, opts) {
29946
30401
  runLen = 0;
29947
30402
  };
29948
30403
  for (const p of A.pts) {
29949
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30404
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
29950
30405
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29951
30406
  if (covered) {
29952
30407
  if (run.length)
@@ -29981,9 +30436,10 @@ function detectEdgeNodePierces(layout, opts) {
29981
30436
  });
29982
30437
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
29983
30438
  const out = [];
30439
+ const polys = flatPolys(layout);
29984
30440
  layout.edges.forEach((e, idx) => {
29985
30441
  if (e.points.length < 2) return;
29986
- const poly = flatten(splineGen(e.points) ?? "");
30442
+ const poly = polys[idx].pts;
29987
30443
  for (const r of rects) {
29988
30444
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29989
30445
  continue;
@@ -30330,8 +30786,10 @@ function edgeLength(layout) {
30330
30786
  );
30331
30787
  return total;
30332
30788
  }
30333
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30789
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30334
30790
  const hideDescriptions = opts?.hideDescriptions ?? false;
30791
+ const onProgress = opts?.onProgress;
30792
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30335
30793
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30336
30794
  if (collapseInfo) {
30337
30795
  const missing = /* @__PURE__ */ new Set();
@@ -30745,17 +31203,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30745
31203
  seed: s
30746
31204
  });
30747
31205
  const badness = (lay, floor) => {
30748
- const x = countSplineCrossings(lay);
31206
+ const x = countSplineCrossings(lay, floor);
30749
31207
  if (x > floor) return Infinity;
30750
31208
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30751
31209
  };
30752
31210
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31211
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31212
+ let progressDone = 0;
31213
+ const step = async (phase) => {
31214
+ if (!onProgress) return;
31215
+ onProgress(++progressDone, progressTotal, phase);
31216
+ await tick();
31217
+ };
30753
31218
  const pool = [];
30754
31219
  for (const cfg of configs) {
30755
31220
  try {
30756
31221
  pool.push(place(cfg));
30757
31222
  } catch {
30758
31223
  }
31224
+ await step("Optimizing layout");
30759
31225
  }
30760
31226
  if (!pool.length)
30761
31227
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30764,9 +31230,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30764
31230
  layered = layeredCandidates(parsed, sizes);
30765
31231
  } catch {
30766
31232
  }
30767
- pool.sort(
30768
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30769
- );
31233
+ const fastKey = /* @__PURE__ */ new Map();
31234
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31235
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30770
31236
  const refineK = Math.min(REFINE_K, pool.length);
30771
31237
  let best = pool[0];
30772
31238
  let bestObj = Infinity;
@@ -30781,7 +31247,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30781
31247
  best = lay;
30782
31248
  }
30783
31249
  };
30784
- for (const lay of pool.slice(0, refineK)) consider(lay);
31250
+ for (const lay of pool.slice(0, refineK)) {
31251
+ consider(lay);
31252
+ await step("Refining layout");
31253
+ }
30785
31254
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30786
31255
  const extra = [];
30787
31256
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30797,9 +31266,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30797
31266
  } catch {
30798
31267
  }
30799
31268
  }
30800
- extra.sort(
30801
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30802
- );
31269
+ const extraKey = /* @__PURE__ */ new Map();
31270
+ for (const lay of extra)
31271
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31272
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30803
31273
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30804
31274
  }
30805
31275
  for (const lay of layered) {
@@ -30826,7 +31296,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30826
31296
  }
30827
31297
  return best;
30828
31298
  }
30829
- var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31299
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30830
31300
  var init_layout_search = __esm({
30831
31301
  "src/boxes-and-lines/layout-search.ts"() {
30832
31302
  "use strict";
@@ -30838,6 +31308,8 @@ var init_layout_search = __esm({
30838
31308
  ESCALATE_SEEDS = 18;
30839
31309
  ESCALATE_REFINE = 10;
30840
31310
  splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
31311
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31312
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30841
31313
  GROUP_LABEL_ZONE2 = 32;
30842
31314
  }
30843
31315
  });
@@ -30922,12 +31394,15 @@ function computeNodeSize(node, reserveValueRow) {
30922
31394
  }
30923
31395
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30924
31396
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30925
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31397
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30926
31398
  ...layoutOptions?.hideDescriptions !== void 0 && {
30927
31399
  hideDescriptions: layoutOptions.hideDescriptions
30928
31400
  },
30929
31401
  ...layoutOptions?.previousPositions !== void 0 && {
30930
31402
  previousPositions: layoutOptions.previousPositions
31403
+ },
31404
+ ...layoutOptions?.onProgress !== void 0 && {
31405
+ onProgress: layoutOptions.onProgress
30931
31406
  }
30932
31407
  });
30933
31408
  return attachNotes(
@@ -31712,7 +32187,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31712
32187
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31713
32188
  const availWidth = containerWidth;
31714
32189
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31715
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32190
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32191
+ availWidth,
32192
+ layout.width,
32193
+ availHeight,
32194
+ layout.height
32195
+ );
31716
32196
  let renderLayout = layout;
31717
32197
  if (ctx.factor < 1) {
31718
32198
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31721,17 +32201,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31721
32201
  hiddenCounts.set(n.id, n.hiddenCount);
31722
32202
  }
31723
32203
  }
31724
- renderLayout = layoutMindmap(parsed, palette, {
32204
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31725
32205
  interactive: !isExport,
31726
32206
  ...hiddenCounts.size > 0 && { hiddenCounts },
31727
32207
  activeTagGroup: activeTagGroup ?? null,
31728
32208
  ...hideDescriptions !== void 0 && { hideDescriptions },
31729
- ctx
32209
+ ctx: c
31730
32210
  });
32211
+ renderLayout = relayout(ctx);
32212
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32213
+ const refit = Math.min(
32214
+ availWidth / renderLayout.width,
32215
+ availHeight / renderLayout.height
32216
+ );
32217
+ if (refit >= 0.999) break;
32218
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32219
+ renderLayout = relayout(ctx);
32220
+ }
31731
32221
  }
31732
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31733
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31734
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32222
+ const fitScale = isExport ? 1 : Math.min(
32223
+ 1,
32224
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32225
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32226
+ );
32227
+ const scaledWidth = renderLayout.width * fitScale;
32228
+ const scaledHeight = renderLayout.height * fitScale;
32229
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32230
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32231
+ const mainG = svg.append("g").attr(
32232
+ "transform",
32233
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32234
+ );
31735
32235
  if (ctx.isBelowFloor) {
31736
32236
  svg.attr("width", "100%");
31737
32237
  }
@@ -41075,7 +41575,7 @@ __export(renderer_exports11, {
41075
41575
  import * as d3Selection17 from "d3-selection";
41076
41576
  import * as d3Shape9 from "d3-shape";
41077
41577
  function analysisBlockChrome(palette, isDark) {
41078
- const surfaceBg = isDark ? palette.surface : palette.bg;
41578
+ const surfaceBg = themeBaseBg(palette, isDark);
41079
41579
  return {
41080
41580
  fill: mix(palette.surface, palette.bg, 40),
41081
41581
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -45786,7 +46286,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45786
46286
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45787
46287
  const gw = group.width + sGroupExtraPadding * 2;
45788
46288
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45789
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46289
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45790
46290
  const strokeColor = group.color ?? palette.textMuted;
45791
46291
  const groupWrapper = contentG.append("g").attr("class", "st-group-wrapper").attr("data-line-number", String(group.lineNumber)).attr("data-group-id", group.id).attr("data-group-toggle", group.id).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", `Collapse group ${group.label}`).style("cursor", "pointer");
45792
46292
  groupWrapper.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "st-group");
@@ -49758,6 +50258,16 @@ __export(resolver_exports, {
49758
50258
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49759
50259
  resolveMap: () => resolveMap
49760
50260
  });
50261
+ function containerOvershoot(span, usOriented) {
50262
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50263
+ return Math.max(
50264
+ CONTAINER_OVERSHOOT_MIN,
50265
+ Math.min(
50266
+ CONTAINER_OVERSHOOT_MAX,
50267
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50268
+ )
50269
+ );
50270
+ }
49761
50271
  function bboxArea(b) {
49762
50272
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49763
50273
  }
@@ -50254,7 +50764,7 @@ function resolveMap(parsed, data) {
50254
50764
  const containerUnion = unionExtent(containerBoxes, points);
50255
50765
  if (containerUnion)
50256
50766
  extent3 = pad(
50257
- clampContainerToCluster(containerUnion, points),
50767
+ clampContainerToCluster(containerUnion, points, usOriented),
50258
50768
  PAD_FRACTION
50259
50769
  );
50260
50770
  }
@@ -50338,17 +50848,21 @@ function mostCommonCountry(regions, poiCountries) {
50338
50848
  }
50339
50849
  return best;
50340
50850
  }
50341
- function clampContainerToCluster(container, points) {
50851
+ function clampContainerToCluster(container, points, usOriented) {
50342
50852
  const poi = unionExtent([], points);
50343
50853
  if (!poi) return container;
50344
50854
  let [[west, south], [east, north]] = container;
50345
50855
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50346
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50347
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50348
- if (east <= 180 && pEast <= 180) {
50349
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50350
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50351
- }
50856
+ const over = containerOvershoot(
50857
+ Math.max(pEast - pWest, pNorth - pSouth),
50858
+ usOriented
50859
+ );
50860
+ south = Math.max(south, pSouth - over);
50861
+ north = Math.min(north, pNorth + over);
50862
+ const wOver = pWest - over;
50863
+ const eOver = pEast + over;
50864
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
50865
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
50352
50866
  return [
50353
50867
  [west, south],
50354
50868
  [east, north]
@@ -50366,7 +50880,7 @@ function firstError(diags) {
50366
50880
  const e = diags.find((d) => d.severity === "error");
50367
50881
  return e ? formatDgmoError(e) : null;
50368
50882
  }
50369
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_DEG, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50883
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_MAX, CONTAINER_OVERSHOOT_MIN, CONTAINER_OVERSHOOT_DECAY, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50370
50884
  var init_resolver2 = __esm({
50371
50885
  "src/map/resolver.ts"() {
50372
50886
  "use strict";
@@ -50379,7 +50893,9 @@ var init_resolver2 = __esm({
50379
50893
  WORLD_LAT_SOUTH = -58;
50380
50894
  WORLD_LAT_NORTH = 78;
50381
50895
  POI_ZOOM_FLOOR_DEG = 7;
50382
- CONTAINER_OVERSHOOT_DEG = 8;
50896
+ CONTAINER_OVERSHOOT_MAX = 8;
50897
+ CONTAINER_OVERSHOOT_MIN = 3;
50898
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50383
50899
  US_NATIONAL_LON_SPAN = 48;
50384
50900
  CONUS_BBOX = [
50385
50901
  [-125, 25],
@@ -50545,12 +51061,12 @@ function tierBand(maxSpanDeg) {
50545
51061
  }
50546
51062
  function labelBudget(width, height, band) {
50547
51063
  const bandCap = {
50548
- world: 7,
50549
- continental: 6,
50550
- regional: 5,
50551
- local: 4
51064
+ world: 10,
51065
+ continental: 9,
51066
+ regional: 7,
51067
+ local: 6
50552
51068
  };
50553
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51069
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50554
51070
  return Math.max(0, Math.min(area2, bandCap[band]));
50555
51071
  }
50556
51072
  function waterEligible(tier, kind, band) {
@@ -50604,6 +51120,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50604
51120
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50605
51121
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50606
51122
  }
51123
+ function rectDist2(px, py, x0, y0, x1, y1) {
51124
+ const dx = Math.max(x0 - px, 0, px - x1);
51125
+ const dy = Math.max(y0 - py, 0, py - y1);
51126
+ return dx * dx + dy * dy;
51127
+ }
50607
51128
  function rectFits(r, width, height) {
50608
51129
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50609
51130
  }
@@ -50622,6 +51143,7 @@ function placeContextLabels(args) {
50622
51143
  palette,
50623
51144
  project,
50624
51145
  collides,
51146
+ contentPoints,
50625
51147
  overLand
50626
51148
  } = args;
50627
51149
  void projection;
@@ -50692,8 +51214,17 @@ function placeContextLabels(args) {
50692
51214
  const [x0, y0, x1, y1] = c.bbox;
50693
51215
  const w = x1 - x0;
50694
51216
  const h = y1 - y0;
50695
- return { c, w, h, area: w * h };
50696
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51217
+ let dist = Infinity;
51218
+ if (contentPoints?.length) {
51219
+ for (const p of contentPoints) {
51220
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51221
+ if (d < dist) dist = d;
51222
+ }
51223
+ }
51224
+ return { c, w, h, area: w * h, dist };
51225
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51226
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51227
+ );
50697
51228
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50698
51229
  let ci = 0;
50699
51230
  for (const r of ranked) {
@@ -50710,7 +51241,7 @@ function placeContextLabels(args) {
50710
51241
  );
50711
51242
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50712
51243
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50713
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51244
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50714
51245
  const text = c.name;
50715
51246
  const tw = labelWidth(text, 0, fontSize);
50716
51247
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50723,6 +51254,9 @@ function placeContextLabels(args) {
50723
51254
  letterSpacing: 0,
50724
51255
  color,
50725
51256
  fontSize,
51257
+ // Multi-position dodging: carry the ordered interior positions through to the
51258
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51259
+ ...c.positions ? { positions: c.positions } : {},
50726
51260
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50727
51261
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50728
51262
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50737,39 +51271,53 @@ function placeContextLabels(args) {
50737
51271
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50738
51272
  const waterCap = budget - Math.min(2, countryCount);
50739
51273
  let waterPlaced = 0;
50740
- for (const cand of candidates) {
50741
- if (placed.length >= budget) break;
50742
- if (cand.italic && waterPlaced >= waterCap) continue;
51274
+ const gateAt = (cx, cy, cand) => {
50743
51275
  const rect = rectAround(
50744
- cand.cx,
50745
- cand.cy,
51276
+ cx,
51277
+ cy,
50746
51278
  cand.lines,
50747
51279
  cand.letterSpacing,
50748
51280
  cand.fontSize
50749
51281
  );
50750
- if (!rectFits(rect, width, height)) continue;
51282
+ if (!rectFits(rect, width, height)) return null;
50751
51283
  if (cand.italic && overLand) {
50752
51284
  const inset = 2;
50753
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51285
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50754
51286
  const touchesLand = cand.lines.some((line11, li) => {
50755
51287
  const lw = labelWidth(line11, cand.letterSpacing);
50756
- const x0 = cand.cx - lw / 2 + inset;
50757
- const x1 = cand.cx + lw / 2 - inset;
50758
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51288
+ const x0 = cx - lw / 2 + inset;
51289
+ const x1 = cx + lw / 2 - inset;
51290
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
50759
51291
  const base = top + li * LINE_HEIGHT;
50760
51292
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50761
51293
  (y) => xs.some((x) => overLand(x, y))
50762
51294
  );
50763
51295
  });
50764
- if (touchesLand) continue;
51296
+ if (touchesLand) return null;
51297
+ }
51298
+ if (collides(rect)) return null;
51299
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51300
+ return null;
51301
+ return rect;
51302
+ };
51303
+ for (const cand of candidates) {
51304
+ if (placed.length >= budget) break;
51305
+ if (cand.italic && waterPlaced >= waterCap) continue;
51306
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51307
+ let chosen = null;
51308
+ for (const [px, py] of positions) {
51309
+ const rect = gateAt(px, py, cand);
51310
+ if (rect) {
51311
+ chosen = { x: px, y: py, rect };
51312
+ break;
51313
+ }
50765
51314
  }
50766
- if (collides(rect)) continue;
50767
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50768
- placedRects.push(rect);
51315
+ if (!chosen) continue;
51316
+ placedRects.push(chosen.rect);
50769
51317
  if (cand.italic) waterPlaced++;
50770
51318
  placed.push({
50771
- x: cand.cx,
50772
- y: cand.cy,
51319
+ x: chosen.x,
51320
+ y: chosen.y,
50773
51321
  text: cand.text,
50774
51322
  anchor: "middle",
50775
51323
  color: cand.color,
@@ -50787,7 +51335,7 @@ function placeContextLabels(args) {
50787
51335
  }
50788
51336
  return placed;
50789
51337
  }
50790
- var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, KIND_ORDER;
51338
+ var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, MAX_COUNTRY_POSITIONS, COUNTRY_POS_GRID, COUNTRY_POS_TOPN_MARGIN, KIND_ORDER;
50791
51339
  var init_context_labels = __esm({
50792
51340
  "src/map/context-labels.ts"() {
50793
51341
  "use strict";
@@ -50805,6 +51353,9 @@ var init_context_labels = __esm({
50805
51353
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50806
51354
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50807
51355
  COUNTRY_FADE_MAX = 45;
51356
+ MAX_COUNTRY_POSITIONS = 4;
51357
+ COUNTRY_POS_GRID = 5;
51358
+ COUNTRY_POS_TOPN_MARGIN = 3;
50808
51359
  KIND_ORDER = {
50809
51360
  ocean: 0,
50810
51361
  sea: 1,
@@ -50898,6 +51449,84 @@ function decodeLayer(topo) {
50898
51449
  decodeCache.set(topo, out);
50899
51450
  return out;
50900
51451
  }
51452
+ function countryLabelPositions(args) {
51453
+ const { geometry, bounds, project, width, height, curated } = args;
51454
+ const w0 = bounds[0][0];
51455
+ const s0 = bounds[0][1];
51456
+ const e0 = bounds[1][0];
51457
+ const n0 = bounds[1][1];
51458
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51459
+ return mkCurated(curated, project);
51460
+ }
51461
+ const N = COUNTRY_POS_GRID;
51462
+ const onLand = [];
51463
+ const kept = [];
51464
+ for (let i = 0; i < N; i++) {
51465
+ onLand[i] = [];
51466
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51467
+ for (let j = 0; j < N; j++) {
51468
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51469
+ const land = pointInGeometry(geometry, lon, lat);
51470
+ onLand[i][j] = land;
51471
+ if (!land) continue;
51472
+ const p = project(lon, lat);
51473
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51474
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51475
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51476
+ }
51477
+ }
51478
+ if (!kept.length) return mkCurated(curated, project);
51479
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51480
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51481
+ const interiorness = (c) => {
51482
+ let n = 0;
51483
+ for (let di = -1; di <= 1; di++)
51484
+ for (let dj = -1; dj <= 1; dj++) {
51485
+ if (di === 0 && dj === 0) continue;
51486
+ const ni = c.i + di;
51487
+ const nj = c.j + dj;
51488
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51489
+ }
51490
+ return n;
51491
+ };
51492
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51493
+ const pool = [...kept];
51494
+ pool.sort((a, b) => {
51495
+ const d = interiorness(b) - interiorness(a);
51496
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51497
+ });
51498
+ const ordered = [pool.shift()];
51499
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51500
+ let bestIdx = 0;
51501
+ let bestMin = -1;
51502
+ for (let k = 0; k < pool.length; k++) {
51503
+ const c = pool[k];
51504
+ let minD = Infinity;
51505
+ for (const o of ordered) {
51506
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51507
+ if (d < minD) minD = d;
51508
+ }
51509
+ if (minD > bestMin) {
51510
+ bestMin = minD;
51511
+ bestIdx = k;
51512
+ }
51513
+ }
51514
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51515
+ }
51516
+ const grid = ordered.map((c) => ({
51517
+ lonLat: [c.lon, c.lat],
51518
+ screen: [c.sx, c.sy]
51519
+ }));
51520
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51521
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51522
+ return out;
51523
+ }
51524
+ function mkCurated(curated, project) {
51525
+ if (!curated) return [];
51526
+ const p = project(curated[0], curated[1]);
51527
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51528
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51529
+ }
50901
51530
  function projectionFor(family, extent3) {
50902
51531
  switch (family) {
50903
51532
  case "albers-usa":
@@ -51725,6 +52354,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51725
52354
  if (p[1] < minY) minY = p[1];
51726
52355
  if (p[1] > maxY) maxY = p[1];
51727
52356
  }
52357
+ r.bbox = [minX, minY, maxX, maxY];
52358
+ r.rings = rings;
51728
52359
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51729
52360
  });
51730
52361
  const fillAt = (x, y) => {
@@ -52915,6 +53546,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52915
53546
  for (const box of insets)
52916
53547
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52917
53548
  const countryCandidates = [];
53549
+ const rawCountries = [];
52918
53550
  for (const f of worldLayer.values()) {
52919
53551
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52920
53552
  if (!iso || regionById.has(iso)) continue;
@@ -52931,11 +53563,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52931
53563
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52932
53564
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52933
53565
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52934
- countryCandidates.push({
53566
+ rawCountries.push({
53567
+ f,
53568
+ iso,
52935
53569
  name: f.properties?.name ?? iso,
52936
53570
  bbox: [x0, y0, x1, y1],
52937
53571
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52938
- curatedAnchor: !!anchorLngLat
53572
+ curatedLngLat: anchorLngLat ?? null,
53573
+ area: (x1 - x0) * (y1 - y0)
53574
+ });
53575
+ }
53576
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53577
+ const cBudget = labelBudget(width, height, cBand);
53578
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53579
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53580
+ const rankOrder = rawCountries.map((r, idx) => {
53581
+ let dist = Infinity;
53582
+ const [x0, y0, x1, y1] = r.bbox;
53583
+ for (const [px, py] of contentPoints) {
53584
+ const dx = Math.max(x0 - px, 0, px - x1);
53585
+ const dy = Math.max(y0 - py, 0, py - y1);
53586
+ const d = dx * dx + dy * dy;
53587
+ if (d < dist) dist = d;
53588
+ }
53589
+ return { idx, area: r.area, dist };
53590
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53591
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53592
+ ).slice(0, topN);
53593
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53594
+ for (let i = 0; i < rawCountries.length; i++) {
53595
+ const r = rawCountries[i];
53596
+ let anchor = r.anchor;
53597
+ let positions;
53598
+ if (genIdx.has(i) && anchor) {
53599
+ const gb = geoBounds2(r.f);
53600
+ const gen = countryLabelPositions({
53601
+ geometry: r.f.geometry,
53602
+ bounds: gb,
53603
+ project,
53604
+ width,
53605
+ height,
53606
+ curated: r.curatedLngLat
53607
+ });
53608
+ if (gen.length) {
53609
+ positions = gen.map((p) => p.screen);
53610
+ anchor = positions[0];
53611
+ }
53612
+ }
53613
+ countryCandidates.push({
53614
+ name: r.name,
53615
+ bbox: r.bbox,
53616
+ anchor,
53617
+ curatedAnchor: !!r.curatedLngLat,
53618
+ ...positions ? { positions } : {}
52939
53619
  });
52940
53620
  }
52941
53621
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52969,6 +53649,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52969
53649
  palette,
52970
53650
  project,
52971
53651
  collides,
53652
+ contentPoints,
52972
53653
  // Water labels must stay over open water — `fillAt` returns the ocean
52973
53654
  // backdrop colour off-land and a region fill on-land (lakes/states count
52974
53655
  // as land here, which is the safe side for an ocean name).
@@ -53221,7 +53902,7 @@ function ringToCoastPaths(ring, frame) {
53221
53902
  function coastlineOuterRings(regions, minExtent, frame) {
53222
53903
  const paths = [];
53223
53904
  for (const r of regions) {
53224
- const rings = parsePathRings(r.d);
53905
+ const rings = r.rings ?? parsePathRings(r.d);
53225
53906
  for (let i = 0; i < rings.length; i++) {
53226
53907
  const ring = rings[i];
53227
53908
  if (ring.length < 3) continue;
@@ -53416,14 +54097,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53416
54097
  if (decoCluster !== void 0)
53417
54098
  gPatch.attr("data-cluster-deco", decoCluster);
53418
54099
  for (const r of layout.regions) {
53419
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53420
- for (const ring of parsePathRings(r.d))
53421
- for (const [px, py] of ring) {
53422
- if (px < minX) minX = px;
53423
- if (px > maxX) maxX = px;
53424
- if (py < minY) minY = py;
53425
- if (py > maxY) maxY = py;
53426
- }
54100
+ let minX, minY, maxX, maxY;
54101
+ if (r.bbox) {
54102
+ [minX, minY, maxX, maxY] = r.bbox;
54103
+ } else {
54104
+ minX = Infinity;
54105
+ minY = Infinity;
54106
+ maxX = -Infinity;
54107
+ maxY = -Infinity;
54108
+ for (const ring of parsePathRings(r.d))
54109
+ for (const [px, py] of ring) {
54110
+ if (px < minX) minX = px;
54111
+ if (px > maxX) maxX = px;
54112
+ if (py < minY) minY = py;
54113
+ if (py > maxY) maxY = py;
54114
+ }
54115
+ }
53427
54116
  const hit = blobRects.some(
53428
54117
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53429
54118
  );
@@ -54650,7 +55339,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54650
55339
  const tasksAll = [...allTasks(parsed)];
54651
55340
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54652
55341
  const solid = parsed.options["solid-fill"] === "on";
54653
- const surfaceBg = isDark ? palette.surface : palette.bg;
55342
+ const surfaceBg = themeBaseBg(palette, isDark);
54654
55343
  const roleCount = Math.max(1, parsed.roles.length);
54655
55344
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54656
55345
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54828,10 +55517,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54828
55517
  const cx = roleX(i) + sColumnInset;
54829
55518
  const cw = roleColW - 2 * sColumnInset;
54830
55519
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54831
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55520
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54832
55521
  const headerFill = mix(
54833
55522
  roleColor,
54834
- isDark ? palette.surface : palette.bg,
55523
+ themeBaseBg(palette, isDark),
54835
55524
  30
54836
55525
  );
54837
55526
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -56558,7 +57247,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56558
57247
  const groupTagColor = getTagColor(groupTagValue || void 0);
56559
57248
  const fillColor = groupTagColor ? mix(
56560
57249
  groupTagColor,
56561
- isDark ? palette.surface : palette.bg,
57250
+ themeBaseBg(palette, isDark),
56562
57251
  isDark ? 15 : 20
56563
57252
  ) : isDark ? palette.surface : palette.bg;
56564
57253
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56612,7 +57301,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56612
57301
  participantG.append("title").text("Click to expand");
56613
57302
  const pFill = effectiveTagColor ? mix(
56614
57303
  effectiveTagColor,
56615
- isDark ? palette.surface : palette.bg,
57304
+ themeBaseBg(palette, isDark),
56616
57305
  isDark ? 30 : 40
56617
57306
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56618
57307
  const pStroke = effectiveTagColor || palette.border;
@@ -56819,7 +57508,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56819
57508
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56820
57509
  const actTagColor = getTagColor(actTagValue);
56821
57510
  const actBaseColor = actTagColor || palette.primary;
56822
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57511
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
56823
57512
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56824
57513
  const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
56825
57514
  if (tagKey && actTagValue) {
@@ -58520,7 +59209,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58520
59209
  const textColor = palette.text;
58521
59210
  const mutedColor = palette.border;
58522
59211
  const bgColor = palette.bg;
58523
- const bg = isDark ? palette.surface : palette.bg;
59212
+ const bg = themeBaseBg(palette, isDark);
58524
59213
  const colors = getSeriesColors(palette);
58525
59214
  const groupColorMap = /* @__PURE__ */ new Map();
58526
59215
  timelineGroups.forEach((grp, i) => {
@@ -58935,9 +59624,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58935
59624
  drawLegend2();
58936
59625
  }
58937
59626
  }
58938
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59627
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58939
59628
  const {
58940
59629
  width,
59630
+ height,
58941
59631
  tooltip,
58942
59632
  solid,
58943
59633
  textColor,
@@ -58989,9 +59679,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58989
59679
  const rowH = ctx.structural(28);
58990
59680
  const innerHeight = rowH * sorted.length;
58991
59681
  const usedHeight = margin.top + innerHeight + margin.bottom;
59682
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59683
+ const svgHeight = fitToContainer ? height : usedHeight;
58992
59684
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58993
- 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);
58994
- if (ctx.isBelowFloor) {
59685
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", svgHeight).attr("viewBox", `0 0 ${width} ${usedHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
59686
+ if (ctx.isBelowFloor && !fitToContainer) {
58995
59687
  svg.attr("width", "100%");
58996
59688
  }
58997
59689
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60665,7 +61357,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60665
61357
  onClickItem
60666
61358
  );
60667
61359
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60668
- const bg = isDark ? palette.surface : palette.bg;
61360
+ const bg = themeBaseBg(palette, isDark);
60669
61361
  const getQuadrantColor = (label, defaultIdx) => {
60670
61362
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60671
61363
  };
@@ -61002,18 +61694,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
61002
61694
  palette,
61003
61695
  viewState,
61004
61696
  options,
61005
- exportMode
61697
+ exportMode,
61698
+ isDark: theme === "dark"
61006
61699
  };
61007
61700
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
61008
61701
  return (handler ?? exportVisualization)(ctx);
61009
61702
  }
61703
+ function ctxTagOverride(ctx) {
61704
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61705
+ }
61010
61706
  async function exportOrg(ctx) {
61011
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61707
+ const { content, theme, palette, viewState, exportMode } = ctx;
61012
61708
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
61013
61709
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
61014
61710
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
61015
61711
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
61016
- const isDark = theme === "dark";
61712
+ const isDark = ctx.isDark;
61017
61713
  const effectivePalette = await resolveExportPalette(theme, palette);
61018
61714
  const orgParsed = parseOrg2(content, effectivePalette);
61019
61715
  if (orgParsed.error) return "";
@@ -61021,7 +61717,7 @@ async function exportOrg(ctx) {
61021
61717
  const activeTagGroup = resolveActiveTagGroup(
61022
61718
  orgParsed.tagGroups,
61023
61719
  orgParsed.options["active-tag"],
61024
- viewState?.tag ?? options?.tagGroup
61720
+ ctxTagOverride(ctx)
61025
61721
  );
61026
61722
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
61027
61723
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61054,12 +61750,12 @@ async function exportOrg(ctx) {
61054
61750
  return finalizeSvgExport(container, theme, effectivePalette);
61055
61751
  }
61056
61752
  async function exportSitemap(ctx) {
61057
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61753
+ const { content, theme, palette, viewState, exportMode } = ctx;
61058
61754
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
61059
61755
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
61060
61756
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
61061
61757
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
61062
- const isDark = theme === "dark";
61758
+ const isDark = ctx.isDark;
61063
61759
  const effectivePalette = await resolveExportPalette(theme, palette);
61064
61760
  const sitemapParsed = parseSitemap2(content, effectivePalette);
61065
61761
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -61067,7 +61763,7 @@ async function exportSitemap(ctx) {
61067
61763
  const activeTagGroup = resolveActiveTagGroup(
61068
61764
  sitemapParsed.tagGroups,
61069
61765
  sitemapParsed.options["active-tag"],
61070
- viewState?.tag ?? options?.tagGroup
61766
+ ctxTagOverride(ctx)
61071
61767
  );
61072
61768
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
61073
61769
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61098,7 +61794,7 @@ async function exportSitemap(ctx) {
61098
61794
  return finalizeSvgExport(container, theme, effectivePalette);
61099
61795
  }
61100
61796
  async function exportKanban(ctx) {
61101
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61797
+ const { content, theme, palette, viewState, exportMode } = ctx;
61102
61798
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
61103
61799
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
61104
61800
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61110,11 +61806,11 @@ async function exportKanban(ctx) {
61110
61806
  document.body.appendChild(container);
61111
61807
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
61112
61808
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
61113
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61809
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
61114
61810
  activeTagGroup: resolveActiveTagGroup(
61115
61811
  kanbanParsed.tagGroups,
61116
61812
  kanbanParsed.options["active-tag"],
61117
- viewState?.tag ?? options?.tagGroup
61813
+ ctxTagOverride(ctx)
61118
61814
  ),
61119
61815
  currentSwimlaneGroup: viewState?.swim ?? null,
61120
61816
  ...kanbanCollapsedLanes !== void 0 && {
@@ -61147,7 +61843,7 @@ async function exportClass(ctx) {
61147
61843
  classParsed,
61148
61844
  classLayout,
61149
61845
  effectivePalette,
61150
- theme === "dark",
61846
+ ctx.isDark,
61151
61847
  void 0,
61152
61848
  { width: exportWidth, height: exportHeight },
61153
61849
  void 0,
@@ -61156,7 +61852,7 @@ async function exportClass(ctx) {
61156
61852
  return finalizeSvgExport(container, theme, effectivePalette);
61157
61853
  }
61158
61854
  async function exportEr(ctx) {
61159
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61855
+ const { content, theme, palette, viewState, exportMode } = ctx;
61160
61856
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
61161
61857
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
61162
61858
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -61174,13 +61870,13 @@ async function exportEr(ctx) {
61174
61870
  erParsed,
61175
61871
  erLayout,
61176
61872
  effectivePalette,
61177
- theme === "dark",
61873
+ ctx.isDark,
61178
61874
  void 0,
61179
61875
  { width: exportWidth, height: exportHeight },
61180
61876
  resolveActiveTagGroup(
61181
61877
  erParsed.tagGroups,
61182
61878
  erParsed.options["active-tag"],
61183
- viewState?.tag ?? options?.tagGroup
61879
+ ctxTagOverride(ctx)
61184
61880
  ),
61185
61881
  viewState?.sem,
61186
61882
  exportMode
@@ -61188,7 +61884,7 @@ async function exportEr(ctx) {
61188
61884
  return finalizeSvgExport(container, theme, effectivePalette);
61189
61885
  }
61190
61886
  async function exportBoxesAndLines(ctx) {
61191
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61887
+ const { content, theme, palette, viewState, exportMode } = ctx;
61192
61888
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
61193
61889
  const effectivePalette = await resolveExportPalette(theme, palette);
61194
61890
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -61208,13 +61904,13 @@ async function exportBoxesAndLines(ctx) {
61208
61904
  const exportWidth = blLayout.width + PADDING3 * 2;
61209
61905
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
61210
61906
  const container = createExportContainer(exportWidth, exportHeight);
61211
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61907
+ const blActiveTagGroup = ctxTagOverride(ctx);
61212
61908
  renderBoxesAndLinesForExport2(
61213
61909
  container,
61214
61910
  blParsed,
61215
61911
  blLayout,
61216
61912
  effectivePalette,
61217
- theme === "dark",
61913
+ ctx.isDark,
61218
61914
  {
61219
61915
  exportDims: { width: exportWidth, height: exportHeight },
61220
61916
  ...blActiveTagGroup !== void 0 && {
@@ -61229,12 +61925,12 @@ async function exportBoxesAndLines(ctx) {
61229
61925
  return finalizeSvgExport(container, theme, effectivePalette);
61230
61926
  }
61231
61927
  async function exportMindmap(ctx) {
61232
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61928
+ const { content, theme, palette, viewState, exportMode } = ctx;
61233
61929
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
61234
61930
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
61235
61931
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
61236
61932
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
61237
- const isDark = theme === "dark";
61933
+ const isDark = ctx.isDark;
61238
61934
  const effectivePalette = await resolveExportPalette(theme, palette);
61239
61935
  const mmParsed = parseMindmap2(content, effectivePalette);
61240
61936
  if (mmParsed.error) return "";
@@ -61242,7 +61938,7 @@ async function exportMindmap(ctx) {
61242
61938
  const activeTagGroup = resolveActiveTagGroup(
61243
61939
  mmParsed.tagGroups,
61244
61940
  mmParsed.options["active-tag"],
61245
- viewState?.tag ?? options?.tagGroup
61941
+ ctxTagOverride(ctx)
61246
61942
  );
61247
61943
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
61248
61944
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61292,7 +61988,7 @@ async function exportWireframe(ctx) {
61292
61988
  wireframeParsed,
61293
61989
  wireframeLayout,
61294
61990
  effectivePalette,
61295
- theme === "dark",
61991
+ ctx.isDark,
61296
61992
  void 0,
61297
61993
  { width: exportWidth, height: exportHeight },
61298
61994
  theme
@@ -61300,7 +61996,7 @@ async function exportWireframe(ctx) {
61300
61996
  return finalizeSvgExport(container, theme, effectivePalette);
61301
61997
  }
61302
61998
  async function exportC4(ctx) {
61303
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61999
+ const { content, theme, palette, viewState, exportMode } = ctx;
61304
62000
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61305
62001
  const {
61306
62002
  layoutC4Context: layoutC4Context2,
@@ -61312,9 +62008,9 @@ async function exportC4(ctx) {
61312
62008
  const effectivePalette = await resolveExportPalette(theme, palette);
61313
62009
  const c4Parsed = parseC42(content, effectivePalette);
61314
62010
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61315
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61316
- const c4System = options?.c4System ?? viewState?.c4s;
61317
- const c4Container = options?.c4Container ?? viewState?.c4c;
62011
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62012
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62013
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61318
62014
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61319
62015
  if (c4Layout.nodes.length === 0) return "";
61320
62016
  const PADDING3 = 20;
@@ -61328,13 +62024,13 @@ async function exportC4(ctx) {
61328
62024
  c4Parsed,
61329
62025
  c4Layout,
61330
62026
  effectivePalette,
61331
- theme === "dark",
62027
+ ctx.isDark,
61332
62028
  void 0,
61333
62029
  { width: exportWidth, height: exportHeight },
61334
62030
  resolveActiveTagGroup(
61335
62031
  c4Parsed.tagGroups,
61336
62032
  c4Parsed.options["active-tag"],
61337
- viewState?.tag ?? options?.tagGroup
62033
+ ctxTagOverride(ctx)
61338
62034
  ),
61339
62035
  exportMode
61340
62036
  );
@@ -61355,14 +62051,14 @@ async function exportFlowchart(ctx) {
61355
62051
  fcParsed,
61356
62052
  layout,
61357
62053
  effectivePalette,
61358
- theme === "dark",
62054
+ ctx.isDark,
61359
62055
  void 0,
61360
62056
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61361
62057
  );
61362
62058
  return finalizeSvgExport(container, theme, effectivePalette);
61363
62059
  }
61364
62060
  async function exportInfra(ctx) {
61365
- const { content, theme, palette, viewState, options } = ctx;
62061
+ const { content, theme, palette, viewState } = ctx;
61366
62062
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61367
62063
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61368
62064
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61375,7 +62071,7 @@ async function exportInfra(ctx) {
61375
62071
  const activeTagGroup = resolveActiveTagGroup(
61376
62072
  infraParsed.tagGroups,
61377
62073
  infraParsed.options["active-tag"],
61378
- viewState?.tag ?? options?.tagGroup
62074
+ ctxTagOverride(ctx)
61379
62075
  );
61380
62076
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61381
62077
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61393,7 +62089,7 @@ async function exportInfra(ctx) {
61393
62089
  container,
61394
62090
  infraLayout,
61395
62091
  effectivePalette,
61396
- theme === "dark",
62092
+ ctx.isDark,
61397
62093
  showInfraTitle ? infraParsed.title : null,
61398
62094
  showInfraTitle ? infraParsed.titleLineNumber : null,
61399
62095
  infraTagGroups,
@@ -61440,7 +62136,7 @@ async function exportPert(ctx) {
61440
62136
  pertResolved,
61441
62137
  pertLayout,
61442
62138
  effectivePalette,
61443
- theme === "dark",
62139
+ ctx.isDark,
61444
62140
  {
61445
62141
  title: pertParsed.title,
61446
62142
  exportDims: { width: exportW, height: exportH },
@@ -61453,7 +62149,7 @@ async function exportPert(ctx) {
61453
62149
  return finalizeSvgExport(container, theme, effectivePalette);
61454
62150
  }
61455
62151
  async function exportGantt(ctx) {
61456
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62152
+ const { content, theme, palette, viewState, exportMode } = ctx;
61457
62153
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61458
62154
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61459
62155
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61471,7 +62167,7 @@ async function exportGantt(ctx) {
61471
62167
  container,
61472
62168
  resolved,
61473
62169
  effectivePalette,
61474
- theme === "dark",
62170
+ ctx.isDark,
61475
62171
  {
61476
62172
  ...ganttCollapsedGroups !== void 0 && {
61477
62173
  collapsedGroups: ganttCollapsedGroups
@@ -61485,7 +62181,7 @@ async function exportGantt(ctx) {
61485
62181
  currentActiveGroup: resolveActiveTagGroup(
61486
62182
  resolved.tagGroups,
61487
62183
  resolved.options.activeTag ?? void 0,
61488
- viewState?.tag ?? options?.tagGroup
62184
+ ctxTagOverride(ctx)
61489
62185
  ),
61490
62186
  exportMode
61491
62187
  },
@@ -61508,7 +62204,7 @@ async function exportState(ctx) {
61508
62204
  stateParsed,
61509
62205
  layout,
61510
62206
  effectivePalette,
61511
- theme === "dark",
62207
+ ctx.isDark,
61512
62208
  void 0,
61513
62209
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61514
62210
  );
@@ -61528,7 +62224,7 @@ async function exportTechRadar(ctx) {
61528
62224
  container,
61529
62225
  radarParsed,
61530
62226
  effectivePalette,
61531
- theme === "dark",
62227
+ ctx.isDark,
61532
62228
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61533
62229
  viewState,
61534
62230
  exportMode
@@ -61545,13 +62241,13 @@ async function exportJourneyMap(ctx) {
61545
62241
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61546
62242
  return "";
61547
62243
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61548
- isDark: theme === "dark"
62244
+ isDark: ctx.isDark
61549
62245
  });
61550
62246
  const container = createExportContainer(
61551
62247
  jmLayout.totalWidth,
61552
62248
  jmLayout.totalHeight
61553
62249
  );
61554
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62250
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61555
62251
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61556
62252
  exportMode
61557
62253
  });
@@ -61569,7 +62265,7 @@ async function exportCycle(ctx) {
61569
62265
  container,
61570
62266
  cycleParsed,
61571
62267
  effectivePalette,
61572
- theme === "dark",
62268
+ ctx.isDark,
61573
62269
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61574
62270
  viewState,
61575
62271
  exportMode
@@ -61606,7 +62302,7 @@ async function exportMap(ctx) {
61606
62302
  mapResolved,
61607
62303
  mapData,
61608
62304
  effectivePalette,
61609
- theme === "dark",
62305
+ ctx.isDark,
61610
62306
  dims
61611
62307
  );
61612
62308
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61623,7 +62319,7 @@ async function exportPyramid(ctx) {
61623
62319
  container,
61624
62320
  pyramidParsed,
61625
62321
  effectivePalette,
61626
- theme === "dark",
62322
+ ctx.isDark,
61627
62323
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61628
62324
  );
61629
62325
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61640,7 +62336,7 @@ async function exportRing(ctx) {
61640
62336
  container,
61641
62337
  ringParsed,
61642
62338
  effectivePalette,
61643
- theme === "dark",
62339
+ ctx.isDark,
61644
62340
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61645
62341
  );
61646
62342
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61657,7 +62353,7 @@ async function exportRaci(ctx) {
61657
62353
  container,
61658
62354
  raciParsed,
61659
62355
  effectivePalette,
61660
- theme === "dark",
62356
+ ctx.isDark,
61661
62357
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61662
62358
  );
61663
62359
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61680,7 +62376,7 @@ async function exportSlope(ctx) {
61680
62376
  container,
61681
62377
  parsed,
61682
62378
  effectivePalette,
61683
- theme === "dark",
62379
+ ctx.isDark,
61684
62380
  void 0,
61685
62381
  dims
61686
62382
  );
@@ -61696,14 +62392,14 @@ async function exportArc(ctx) {
61696
62392
  container,
61697
62393
  parsed,
61698
62394
  effectivePalette,
61699
- theme === "dark",
62395
+ ctx.isDark,
61700
62396
  void 0,
61701
62397
  dims
61702
62398
  );
61703
62399
  return finalizeSvgExport(container, theme, effectivePalette);
61704
62400
  }
61705
62401
  async function exportTimeline(ctx) {
61706
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62402
+ const { content, theme, palette, viewState, exportMode } = ctx;
61707
62403
  const parsed = parseTimeline(content, palette);
61708
62404
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61709
62405
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61712,13 +62408,13 @@ async function exportTimeline(ctx) {
61712
62408
  container,
61713
62409
  parsed,
61714
62410
  effectivePalette,
61715
- theme === "dark",
62411
+ ctx.isDark,
61716
62412
  void 0,
61717
62413
  dims,
61718
62414
  resolveActiveTagGroup(
61719
62415
  parsed.timelineTagGroups,
61720
62416
  parsed.timelineActiveTag,
61721
- viewState?.tag ?? options?.tagGroup
62417
+ ctxTagOverride(ctx)
61722
62418
  ),
61723
62419
  viewState?.swim,
61724
62420
  void 0,
@@ -61737,7 +62433,7 @@ async function exportWordcloud(ctx) {
61737
62433
  container,
61738
62434
  parsed,
61739
62435
  effectivePalette,
61740
- theme === "dark",
62436
+ ctx.isDark,
61741
62437
  dims
61742
62438
  );
61743
62439
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61752,7 +62448,7 @@ async function exportVenn(ctx) {
61752
62448
  container,
61753
62449
  parsed,
61754
62450
  effectivePalette,
61755
- theme === "dark",
62451
+ ctx.isDark,
61756
62452
  void 0,
61757
62453
  dims
61758
62454
  );
@@ -61768,14 +62464,14 @@ async function exportQuadrant(ctx) {
61768
62464
  container,
61769
62465
  parsed,
61770
62466
  effectivePalette,
61771
- theme === "dark",
62467
+ ctx.isDark,
61772
62468
  void 0,
61773
62469
  dims
61774
62470
  );
61775
62471
  return finalizeSvgExport(container, theme, effectivePalette);
61776
62472
  }
61777
62473
  async function exportVisualization(ctx) {
61778
- const { content, theme, palette, viewState, options } = ctx;
62474
+ const { content, theme, palette, viewState } = ctx;
61779
62475
  const parsed = parseVisualization(content, palette);
61780
62476
  if (parsed.type !== "sequence") {
61781
62477
  if (parsed.error) {
@@ -61786,7 +62482,7 @@ async function exportVisualization(ctx) {
61786
62482
  }
61787
62483
  }
61788
62484
  const effectivePalette = await resolveExportPalette(theme, palette);
61789
- const isDark = theme === "dark";
62485
+ const isDark = ctx.isDark;
61790
62486
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61791
62487
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61792
62488
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61794,7 +62490,7 @@ async function exportVisualization(ctx) {
61794
62490
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61795
62491
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61796
62492
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61797
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62493
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61798
62494
  renderSequenceDiagram2(
61799
62495
  container,
61800
62496
  seqParsed,
@@ -62040,16 +62736,13 @@ var themes = {
62040
62736
 
62041
62737
  // src/dimensions.ts
62042
62738
  init_dgmo_router();
62043
- init_scaling();
62044
62739
  init_chart_type_registry();
62045
62740
  function getMinDimensions(content) {
62046
62741
  const { chartType } = parseDgmo(content);
62047
62742
  if (!chartType) return { width: 300, height: 200 };
62048
- const counts = extractContentCounts(content, chartType);
62049
- return computeMinDimensions(chartType, counts);
62050
- }
62051
- function extractContentCounts(content, chartType) {
62052
- return REGISTRY_BY_ID.get(chartType)?.measure?.(content) ?? {};
62743
+ const descriptor = REGISTRY_BY_ID.get(chartType);
62744
+ const counts = descriptor?.measure?.(content) ?? {};
62745
+ return descriptor?.minDims?.(counts) ?? { width: 300, height: 200 };
62053
62746
  }
62054
62747
 
62055
62748
  // src/utils/svg-embed.ts