@diagrammo/dgmo 0.30.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 (85) hide show
  1. package/.cursorrules +4 -1
  2. package/.github/copilot-instructions.md +4 -1
  3. package/.windsurfrules +4 -1
  4. package/README.md +21 -3
  5. package/SKILL.md +4 -1
  6. package/dist/advanced.cjs +1853 -623
  7. package/dist/advanced.d.cts +143 -16
  8. package/dist/advanced.d.ts +143 -16
  9. package/dist/advanced.js +1846 -623
  10. package/dist/auto.cjs +1640 -581
  11. package/dist/auto.js +99 -99
  12. package/dist/auto.mjs +1640 -581
  13. package/dist/cli.cjs +148 -147
  14. package/dist/index.cjs +1643 -662
  15. package/dist/index.js +1643 -662
  16. package/docs/ai-integration.md +4 -1
  17. package/docs/language-reference.md +282 -27
  18. package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
  19. package/gallery/fixtures/c4-full.dgmo +4 -5
  20. package/gallery/fixtures/c4.dgmo +2 -3
  21. package/package.json +7 -1
  22. package/src/advanced.ts +10 -0
  23. package/src/boxes-and-lines/focus.ts +257 -0
  24. package/src/boxes-and-lines/layout-search.ts +345 -65
  25. package/src/boxes-and-lines/layout.ts +11 -1
  26. package/src/boxes-and-lines/parser.ts +97 -4
  27. package/src/boxes-and-lines/renderer.ts +111 -8
  28. package/src/boxes-and-lines/types.ts +9 -0
  29. package/src/c4/parser.ts +8 -7
  30. package/src/c4/renderer.ts +7 -5
  31. package/src/chart-type-registry.ts +129 -4
  32. package/src/chart-types.ts +3 -3
  33. package/src/chart.ts +18 -1
  34. package/src/class/renderer.ts +4 -2
  35. package/src/cli-banner.ts +107 -0
  36. package/src/cli.ts +13 -0
  37. package/src/colors.ts +247 -2
  38. package/src/cycle/parser.ts +2 -7
  39. package/src/d3.ts +67 -54
  40. package/src/diagnostics.ts +17 -0
  41. package/src/dimensions.ts +9 -13
  42. package/src/echarts.ts +42 -14
  43. package/src/er/parser.ts +6 -1
  44. package/src/er/renderer.ts +4 -2
  45. package/src/gantt/parser.ts +44 -7
  46. package/src/graph/flowchart-parser.ts +77 -3
  47. package/src/graph/flowchart-renderer.ts +4 -2
  48. package/src/graph/state-renderer.ts +6 -4
  49. package/src/infra/parser.ts +80 -0
  50. package/src/infra/renderer.ts +8 -4
  51. package/src/journey-map/parser.ts +23 -8
  52. package/src/journey-map/renderer.ts +1 -1
  53. package/src/kanban/parser.ts +8 -7
  54. package/src/kanban/renderer.ts +1 -1
  55. package/src/map/context-labels.ts +134 -27
  56. package/src/map/geo.ts +10 -2
  57. package/src/map/layout.ts +259 -4
  58. package/src/map/parser.ts +2 -0
  59. package/src/map/renderer.ts +49 -25
  60. package/src/map/resolver.ts +68 -19
  61. package/src/mindmap/parser.ts +15 -7
  62. package/src/mindmap/renderer.ts +55 -15
  63. package/src/org/parser.ts +8 -7
  64. package/src/org/renderer.ts +89 -127
  65. package/src/palettes/color-utils.ts +19 -4
  66. package/src/palettes/index.ts +1 -0
  67. package/src/pert/renderer.ts +15 -10
  68. package/src/pyramid/parser.ts +2 -7
  69. package/src/quadrant/renderer.ts +2 -2
  70. package/src/raci/parser.ts +2 -7
  71. package/src/raci/renderer.ts +5 -5
  72. package/src/ring/parser.ts +2 -7
  73. package/src/sequence/parser.ts +18 -7
  74. package/src/sequence/renderer.ts +4 -4
  75. package/src/sitemap/parser.ts +8 -7
  76. package/src/sitemap/renderer.ts +37 -39
  77. package/src/tech-radar/parser.ts +2 -7
  78. package/src/timeline/renderer.ts +15 -5
  79. package/src/utils/card.ts +183 -0
  80. package/src/utils/parsing.ts +13 -1
  81. package/src/utils/scaling.ts +38 -81
  82. package/src/utils/tag-groups.ts +48 -10
  83. package/src/utils/visual-conventions.ts +61 -0
  84. package/src/visualizations/parse.ts +6 -1
  85. package/src/wireframe/parser.ts +6 -1
package/dist/index.cjs CHANGED
@@ -40,6 +40,14 @@ function makeDgmoError(line11, message, severity = "error", code) {
40
40
  function formatDgmoError(err) {
41
41
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
42
42
  }
43
+ function makeFail(result) {
44
+ return (line11, message) => {
45
+ const diag = makeDgmoError(line11, message);
46
+ result.diagnostics.push(diag);
47
+ result.error = formatDgmoError(diag);
48
+ return result;
49
+ };
50
+ }
43
51
  function levenshtein(a, b) {
44
52
  const m = a.length;
45
53
  const n = b.length;
@@ -306,21 +314,99 @@ function resolveColor(color, palette) {
306
314
  }
307
315
  return colorNames[lower] ?? null;
308
316
  }
317
+ function nearestNamedColor(input) {
318
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
319
+ if (cssHex) input = cssHex;
320
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
321
+ if (!m) return null;
322
+ let h = m[1].toLowerCase();
323
+ if (h.length === 3)
324
+ h = h.split("").map((c) => c + c).join("");
325
+ const r = parseInt(h.slice(0, 2), 16) / 255;
326
+ const g = parseInt(h.slice(2, 4), 16) / 255;
327
+ const b = parseInt(h.slice(4, 6), 16) / 255;
328
+ const max = Math.max(r, g, b);
329
+ const min = Math.min(r, g, b);
330
+ const delta = max - min;
331
+ const l = (max + min) / 2;
332
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
333
+ if (s < 0.15) {
334
+ if (l < 0.2) return "black";
335
+ if (l > 0.85) return "white";
336
+ return "gray";
337
+ }
338
+ let hue;
339
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
340
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
341
+ else hue = 60 * ((r - g) / delta + 4);
342
+ if (hue < 0) hue += 360;
343
+ const anchors = [
344
+ ["red", 0],
345
+ ["orange", 30],
346
+ ["yellow", 55],
347
+ ["green", 120],
348
+ ["teal", 170],
349
+ ["cyan", 190],
350
+ ["blue", 225],
351
+ ["purple", 285],
352
+ ["red", 360]
353
+ ];
354
+ let best = "red";
355
+ let bestD = Infinity;
356
+ for (const [name, deg] of anchors) {
357
+ const d = Math.abs(hue - deg);
358
+ if (d < bestD) {
359
+ bestD = d;
360
+ best = name;
361
+ }
362
+ }
363
+ return best;
364
+ }
365
+ function isInvalidColorToken(token) {
366
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
367
+ const lower = token.toLowerCase();
368
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
369
+ }
370
+ function invalidColorDiagnostic(token, line11) {
371
+ if (!isInvalidColorToken(token)) return null;
372
+ const nearest = nearestNamedColor(token);
373
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
374
+ return makeDgmoError(
375
+ line11,
376
+ `Color "${token}" is not a valid DGMO color \u2014 DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${near}`,
377
+ "warning",
378
+ INVALID_COLOR_CODE
379
+ );
380
+ }
309
381
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
310
382
  const resolved = resolveColor(color, palette);
311
383
  if (resolved !== null) return resolved;
384
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
385
+ const nearest = nearestNamedColor(color);
386
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
387
+ diagnostics.push(
388
+ makeDgmoError(
389
+ line11,
390
+ `Color "${color}" is not supported \u2014 DGMO does not accept hex or CSS color values. Use a named palette color: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${near}`,
391
+ "error",
392
+ INVALID_COLOR_CODE
393
+ )
394
+ );
395
+ return void 0;
396
+ }
312
397
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
313
398
  const suggestion = hint ? ` ${hint}` : "";
314
399
  diagnostics.push(
315
400
  makeDgmoError(
316
401
  line11,
317
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
318
- "warning"
402
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
403
+ "warning",
404
+ INVALID_COLOR_CODE
319
405
  )
320
406
  );
321
407
  return void 0;
322
408
  }
323
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
409
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
324
410
  var init_colors = __esm({
325
411
  "src/colors.ts"() {
326
412
  "use strict";
@@ -378,6 +464,102 @@ var init_colors = __esm({
378
464
  "black",
379
465
  "white"
380
466
  ]);
467
+ CATEGORICAL_COLOR_ORDER = Object.freeze([
468
+ "red",
469
+ "green",
470
+ "blue",
471
+ "yellow",
472
+ "teal",
473
+ "purple",
474
+ "orange",
475
+ "cyan"
476
+ ]);
477
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
478
+ INVALID_CSS_COLOR_HEX = Object.freeze({
479
+ pink: "#ffc0cb",
480
+ hotpink: "#ff69b4",
481
+ deeppink: "#ff1493",
482
+ lightpink: "#ffb6c1",
483
+ palevioletred: "#db7093",
484
+ crimson: "#dc143c",
485
+ scarlet: "#ff2400",
486
+ firebrick: "#b22222",
487
+ darkred: "#8b0000",
488
+ maroon: "#800000",
489
+ salmon: "#fa8072",
490
+ lightsalmon: "#ffa07a",
491
+ darksalmon: "#e9967a",
492
+ coral: "#ff7f50",
493
+ lightcoral: "#f08080",
494
+ tomato: "#ff6347",
495
+ orangered: "#ff4500",
496
+ darkorange: "#ff8c00",
497
+ gold: "#ffd700",
498
+ goldenrod: "#daa520",
499
+ darkgoldenrod: "#b8860b",
500
+ khaki: "#f0e68c",
501
+ darkkhaki: "#bdb76b",
502
+ amber: "#ffbf00",
503
+ lavender: "#e6e6fa",
504
+ violet: "#ee82ee",
505
+ magenta: "#ff00ff",
506
+ fuchsia: "#ff00ff",
507
+ orchid: "#da70d6",
508
+ plum: "#dda0dd",
509
+ indigo: "#4b0082",
510
+ navy: "#000080",
511
+ midnightblue: "#191970",
512
+ darkblue: "#00008b",
513
+ mediumblue: "#0000cd",
514
+ royalblue: "#4169e1",
515
+ cornflowerblue: "#6495ed",
516
+ dodgerblue: "#1e90ff",
517
+ deepskyblue: "#00bfff",
518
+ skyblue: "#87ceeb",
519
+ lightskyblue: "#87cefa",
520
+ lightblue: "#add8e6",
521
+ powderblue: "#b0e0e6",
522
+ steelblue: "#4682b4",
523
+ slateblue: "#6a5acd",
524
+ cadetblue: "#5f9ea0",
525
+ turquoise: "#40e0d0",
526
+ aqua: "#00ffff",
527
+ aquamarine: "#7fffd4",
528
+ lime: "#00ff00",
529
+ limegreen: "#32cd32",
530
+ lightgreen: "#90ee90",
531
+ palegreen: "#98fb98",
532
+ seagreen: "#2e8b57",
533
+ mediumseagreen: "#3cb371",
534
+ forestgreen: "#228b22",
535
+ darkgreen: "#006400",
536
+ olive: "#808000",
537
+ olivedrab: "#6b8e23",
538
+ darkolivegreen: "#556b2f",
539
+ chartreuse: "#7fff00",
540
+ lawngreen: "#7cfc00",
541
+ springgreen: "#00ff7f",
542
+ greenyellow: "#adff2f",
543
+ brown: "#a52a2a",
544
+ sienna: "#a0522d",
545
+ chocolate: "#d2691e",
546
+ peru: "#cd853f",
547
+ tan: "#d2b48c",
548
+ beige: "#f5f5dc",
549
+ wheat: "#f5deb3",
550
+ ivory: "#fffff0",
551
+ silver: "#c0c0c0",
552
+ lightgray: "#d3d3d3",
553
+ lightgrey: "#d3d3d3",
554
+ darkgray: "#a9a9a9",
555
+ darkgrey: "#a9a9a9",
556
+ dimgray: "#696969",
557
+ dimgrey: "#696969",
558
+ slategray: "#708090",
559
+ slategrey: "#708090",
560
+ gainsboro: "#dcdcdc",
561
+ grey: "#808080"
562
+ });
381
563
  seriesColors = [
382
564
  nord.nord10,
383
565
  // blue
@@ -552,7 +734,13 @@ function extractColor(label, palette, diagnostics, line11) {
552
734
  );
553
735
  if (lastSpaceIdx < 0) return { label };
554
736
  const trailing = label.substring(lastSpaceIdx + 1);
555
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
737
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
738
+ if (diagnostics && line11 !== void 0) {
739
+ const diag = invalidColorDiagnostic(trailing, line11);
740
+ if (diag) diagnostics.push(diag);
741
+ }
742
+ return { label };
743
+ }
556
744
  let color;
557
745
  if (diagnostics && line11 !== void 0) {
558
746
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1176,6 +1364,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1176
1364
  }
1177
1365
  }
1178
1366
  }
1367
+ function cascadeTagMetadata(roots, tagGroups) {
1368
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1369
+ if (keys.length === 0) return;
1370
+ const walk = (node, inherited) => {
1371
+ const childInherited = { ...inherited };
1372
+ for (const key of keys) {
1373
+ const own = node.metadata[key];
1374
+ if (own) {
1375
+ childInherited[key] = own;
1376
+ } else if (inherited[key]) {
1377
+ node.metadata[key] = inherited[key];
1378
+ }
1379
+ }
1380
+ for (const child of node.children) walk(child, childInherited);
1381
+ };
1382
+ for (const root of roots) walk(root, {});
1383
+ }
1179
1384
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1180
1385
  const defaults = [];
1181
1386
  for (const group of tagGroups) {
@@ -1231,11 +1436,7 @@ var init_tag_groups = __esm({
1231
1436
  init_diagnostics();
1232
1437
  init_colors();
1233
1438
  AUTO_TAG_COLOR_SENTINEL = "";
1234
- autoTagColorCycle = Object.freeze(
1235
- RECOGNIZED_COLOR_NAMES.filter(
1236
- (n) => n !== "gray" && n !== "black" && n !== "white"
1237
- )
1238
- );
1439
+ autoTagColorCycle = CATEGORICAL_COLOR_ORDER;
1239
1440
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
1240
1441
  VALID_TAG_IDENT_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
1241
1442
  }
@@ -1718,7 +1919,12 @@ function parseVisualizationFull(content, palette) {
1718
1919
  }
1719
1920
  if (currentTimelineTagGroup && indent > 0) {
1720
1921
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1721
- const { label, color } = extractColor(entryText, palette);
1922
+ const { label, color } = extractColor(
1923
+ entryText,
1924
+ palette,
1925
+ result.diagnostics,
1926
+ lineNumber
1927
+ );
1722
1928
  if (color) {
1723
1929
  if (isDefault) {
1724
1930
  currentTimelineTagGroup.defaultValue = label;
@@ -3019,13 +3225,16 @@ function contrastText(bg, lightText, darkText) {
3019
3225
  }
3020
3226
  return lightText;
3021
3227
  }
3228
+ function themeBaseBg(palette, isDark) {
3229
+ return isDark ? palette.surface : palette.bg;
3230
+ }
3022
3231
  function shapeFill(palette, intent, isDark, opts) {
3023
3232
  if (opts?.solid) return intent;
3024
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3233
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3025
3234
  }
3026
3235
  function getSeriesColors(palette) {
3027
3236
  const c = palette.colors;
3028
- return [c.blue, c.green, c.yellow, c.orange, c.purple, c.red, c.teal, c.cyan];
3237
+ return CATEGORICAL_COLOR_ORDER.map((name) => c[name]);
3029
3238
  }
3030
3239
  function getSegmentColors(palette, count) {
3031
3240
  if (count <= 0) return [];
@@ -3052,7 +3261,7 @@ function getSegmentColors(palette, count) {
3052
3261
  }
3053
3262
  function politicalTints(palette, count, isDark) {
3054
3263
  if (count <= 0) return [];
3055
- const base = isDark ? palette.surface : palette.bg;
3264
+ const base = themeBaseBg(palette, isDark);
3056
3265
  const c = palette.colors;
3057
3266
  const swatches = [
3058
3267
  .../* @__PURE__ */ new Set([
@@ -3078,6 +3287,7 @@ var POLITICAL_TINT_BANDS;
3078
3287
  var init_color_utils = __esm({
3079
3288
  "src/palettes/color-utils.ts"() {
3080
3289
  "use strict";
3290
+ init_colors();
3081
3291
  POLITICAL_TINT_BANDS = {
3082
3292
  light: [32, 48, 64, 80],
3083
3293
  dark: [44, 58, 72, 86]
@@ -3861,6 +4071,7 @@ __export(palettes_exports, {
3861
4071
  shade: () => shade,
3862
4072
  shapeFill: () => shapeFill,
3863
4073
  slatePalette: () => slatePalette,
4074
+ themeBaseBg: () => themeBaseBg,
3864
4075
  tidewaterPalette: () => tidewaterPalette,
3865
4076
  tint: () => tint,
3866
4077
  tokyoNightPalette: () => tokyoNightPalette
@@ -4075,81 +4286,7 @@ var init_text_measure = __esm({
4075
4286
  });
4076
4287
 
4077
4288
  // src/utils/scaling.ts
4078
- function computeMinDimensions(chartType, counts) {
4079
- switch (chartType) {
4080
- case "sequence":
4081
- return {
4082
- width: Math.max((counts.participants ?? 2) * 80, 320),
4083
- height: Math.max((counts.messages ?? 1) * 20 + 120, 200)
4084
- };
4085
- case "raci":
4086
- return {
4087
- width: Math.max((counts.roles ?? 2) * 50 + 180, 300),
4088
- height: Math.max((counts.tasks ?? 1) * 28 + 80, 200)
4089
- };
4090
- case "mindmap":
4091
- return {
4092
- width: Math.max((counts.nodes ?? 3) * 30, 300),
4093
- height: Math.max((counts.depth ?? 2) * 60, 200)
4094
- };
4095
- case "tech-radar":
4096
- return { width: 360, height: 400 };
4097
- case "heatmap":
4098
- return {
4099
- width: Math.max((counts.columns ?? 3) * 40, 300),
4100
- height: Math.max((counts.rows ?? 3) * 30 + 60, 200)
4101
- };
4102
- case "arc":
4103
- return {
4104
- width: 300,
4105
- height: Math.max((counts.nodes ?? 3) * 20 + 120, 200)
4106
- };
4107
- case "org":
4108
- return {
4109
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4110
- height: Math.max((counts.depth ?? 2) * 80, 200)
4111
- };
4112
- case "gantt":
4113
- return {
4114
- width: 400,
4115
- height: Math.max((counts.tasks ?? 3) * 24 + 80, 200)
4116
- };
4117
- case "kanban":
4118
- return {
4119
- width: Math.max((counts.columns ?? 3) * 120, 360),
4120
- height: 300
4121
- };
4122
- case "er":
4123
- return {
4124
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4125
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4126
- };
4127
- case "class":
4128
- return {
4129
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4130
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4131
- };
4132
- case "flowchart":
4133
- case "state":
4134
- return {
4135
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4136
- height: Math.max((counts.nodes ?? 3) * 50, 200)
4137
- };
4138
- case "pert":
4139
- return {
4140
- width: Math.max((counts.tasks ?? 3) * 80, 340),
4141
- height: Math.max((counts.tasks ?? 3) * 40 + 80, 200)
4142
- };
4143
- case "infra":
4144
- return {
4145
- width: Math.max((counts.nodes ?? 3) * 80, 300),
4146
- height: Math.max((counts.nodes ?? 3) * 60, 200)
4147
- };
4148
- default:
4149
- return { ...DEFAULT_MIN };
4150
- }
4151
- }
4152
- var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext, DEFAULT_MIN;
4289
+ var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext;
4153
4290
  var init_scaling = __esm({
4154
4291
  "src/utils/scaling.ts"() {
4155
4292
  "use strict";
@@ -4168,6 +4305,30 @@ var init_scaling = __esm({
4168
4305
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4169
4306
  return new _ScaleContext(clamped, minScaleFactor);
4170
4307
  }
4308
+ /**
4309
+ * Fit content into a bounding box, scaling by whichever dimension is more
4310
+ * constraining (the smaller of the width- and height-fit ratios) so the
4311
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4312
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4313
+ * never shrunk past the readability floor).
4314
+ */
4315
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4316
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4317
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4318
+ const raw = Math.min(wRaw, hRaw);
4319
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4320
+ return new _ScaleContext(clamped, minScaleFactor);
4321
+ }
4322
+ /**
4323
+ * Build a context from an explicit raw factor (clamped to
4324
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4325
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4326
+ * can still overflow — re-measure the laid-out result and tighten.
4327
+ */
4328
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4329
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4330
+ return new _ScaleContext(clamped, minScaleFactor);
4331
+ }
4171
4332
  static identity() {
4172
4333
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4173
4334
  }
@@ -4184,7 +4345,6 @@ var init_scaling = __esm({
4184
4345
  return Math.max(fontSize * this.factor, floor);
4185
4346
  }
4186
4347
  };
4187
- DEFAULT_MIN = { width: 300, height: 200 };
4188
4348
  }
4189
4349
  });
4190
4350
 
@@ -5869,12 +6029,7 @@ function parseSequenceDgmo(content, palette) {
5869
6029
  const trimmed = token.trim();
5870
6030
  return nameAliasMap.get(trimmed) ?? trimmed;
5871
6031
  };
5872
- const fail = (line11, message) => {
5873
- const diag = makeDgmoError(line11, message);
5874
- result.diagnostics.push(diag);
5875
- result.error = formatDgmoError(diag);
5876
- return result;
5877
- };
6032
+ const fail = makeFail(result);
5878
6033
  const pushError = (line11, message) => {
5879
6034
  const diag = makeDgmoError(line11, message);
5880
6035
  result.diagnostics.push(diag);
@@ -5889,6 +6044,7 @@ function parseSequenceDgmo(content, palette) {
5889
6044
  const lines = content.split("\n");
5890
6045
  let hasExplicitChart = false;
5891
6046
  let contentStarted = false;
6047
+ let bodyStarted = false;
5892
6048
  let firstLineIndex = -1;
5893
6049
  for (let fi = 0; fi < lines.length; fi++) {
5894
6050
  const fl = lines[fi].trim();
@@ -6200,6 +6356,7 @@ function parseSequenceDgmo(content, palette) {
6200
6356
  );
6201
6357
  }
6202
6358
  contentStarted = true;
6359
+ bodyStarted = true;
6203
6360
  const section = {
6204
6361
  kind: "section",
6205
6362
  // Capture group 1 guaranteed present after successful match.
@@ -6415,7 +6572,7 @@ function parseSequenceDgmo(content, palette) {
6415
6572
  alias: bareAlias
6416
6573
  } = splitPipe(trimmed, lineNumber);
6417
6574
  const inGroup = activeGroup && measureIndent(raw) > 0;
6418
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6575
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6419
6576
  contentStarted = true;
6420
6577
  const id = bareCore;
6421
6578
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6462,6 +6619,7 @@ function parseSequenceDgmo(content, palette) {
6462
6619
  }
6463
6620
  if (labeledArrow) {
6464
6621
  contentStarted = true;
6622
+ bodyStarted = true;
6465
6623
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6466
6624
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6467
6625
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6526,6 +6684,7 @@ function parseSequenceDgmo(content, palette) {
6526
6684
  const bareCall = bareCallSync || bareCallAsync;
6527
6685
  if (bareCall) {
6528
6686
  contentStarted = true;
6687
+ bodyStarted = true;
6529
6688
  const from = bareCall[1];
6530
6689
  const to = bareCall[2];
6531
6690
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6547,6 +6706,7 @@ function parseSequenceDgmo(content, palette) {
6547
6706
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6548
6707
  if (ifMatch) {
6549
6708
  contentStarted = true;
6709
+ bodyStarted = true;
6550
6710
  const block = {
6551
6711
  kind: "block",
6552
6712
  type: "if",
@@ -6563,6 +6723,7 @@ function parseSequenceDgmo(content, palette) {
6563
6723
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6564
6724
  if (loopMatch) {
6565
6725
  contentStarted = true;
6726
+ bodyStarted = true;
6566
6727
  const block = {
6567
6728
  kind: "block",
6568
6729
  type: "loop",
@@ -6579,6 +6740,7 @@ function parseSequenceDgmo(content, palette) {
6579
6740
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6580
6741
  if (parallelMatch) {
6581
6742
  contentStarted = true;
6743
+ bodyStarted = true;
6582
6744
  const block = {
6583
6745
  kind: "block",
6584
6746
  type: "parallel",
@@ -6675,6 +6837,7 @@ function parseSequenceDgmo(content, palette) {
6675
6837
  lineNumber,
6676
6838
  endLineNumber: lineNumber
6677
6839
  };
6840
+ bodyStarted = true;
6678
6841
  currentContainer().push(note);
6679
6842
  continue;
6680
6843
  }
@@ -6699,6 +6862,7 @@ function parseSequenceDgmo(content, palette) {
6699
6862
  endLineNumber: i + 1
6700
6863
  // i has advanced past the body lines (1-based)
6701
6864
  };
6865
+ bodyStarted = true;
6702
6866
  currentContainer().push(note);
6703
6867
  continue;
6704
6868
  }
@@ -7543,6 +7707,15 @@ function parseNodeRef(text) {
7543
7707
  }
7544
7708
  return null;
7545
7709
  }
7710
+ function parseNodeRefLoose(text) {
7711
+ const t = text.trim();
7712
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7713
+ const m = t.match(shapeRe);
7714
+ if (!m) return null;
7715
+ const ref = parseNodeRef(m[1]);
7716
+ if (!ref) return null;
7717
+ return { ref, trailing: m[2].trim() };
7718
+ }
7546
7719
  function splitArrows(line11) {
7547
7720
  const segments = [];
7548
7721
  const arrowPositions = [];
@@ -7638,6 +7811,7 @@ function parseFlowchart(content, palette) {
7638
7811
  const notes = [];
7639
7812
  let contentStarted = false;
7640
7813
  let firstLineParsed = false;
7814
+ let prevLineLastNodeId = null;
7641
7815
  const nameAliasMap = /* @__PURE__ */ new Map();
7642
7816
  function peelAlias2(seg) {
7643
7817
  const trimmed = seg.trim();
@@ -7645,6 +7819,19 @@ function parseFlowchart(content, palette) {
7645
7819
  if (!m) return { seg: trimmed };
7646
7820
  return { seg: m[1].trim(), alias: m[2] };
7647
7821
  }
7822
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7823
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7824
+ if (suffixWarnedLines.has(lineNumber)) return;
7825
+ suffixWarnedLines.add(lineNumber);
7826
+ result.diagnostics.push(
7827
+ makeDgmoError(
7828
+ lineNumber,
7829
+ `Ignored unsupported text after a node shape: "${trailing}". Flowcharts have no tag groups or node metadata; node colors are assigned automatically by shape (e.g. terminals green/red, decisions yellow). Remove the suffix.`,
7830
+ "warning",
7831
+ "W_FLOWCHART_NODE_SUFFIX"
7832
+ )
7833
+ );
7834
+ }
7648
7835
  function getOrCreateNode(ref, lineNumber) {
7649
7836
  const key = ref.id;
7650
7837
  const existing = nodeMap.get(key);
@@ -7703,6 +7890,8 @@ function parseFlowchart(content, palette) {
7703
7890
  indentStack[indentStack.length - 1].nodeId
7704
7891
  ) : null;
7705
7892
  const segments = splitArrows(trimmed);
7893
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7894
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7706
7895
  if (segments.length === 1) {
7707
7896
  const peeled = peelAlias2(segments[0]);
7708
7897
  const ref = parseNodeRef(peeled.seg);
@@ -7712,6 +7901,13 @@ function parseFlowchart(content, palette) {
7712
7901
  indentStack.push({ nodeId: node.id, indent });
7713
7902
  return node.id;
7714
7903
  }
7904
+ const loose = parseNodeRefLoose(peeled.seg);
7905
+ if (loose) {
7906
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7907
+ const node = getOrCreateNode(loose.ref, lineNumber);
7908
+ indentStack.push({ nodeId: node.id, indent });
7909
+ return node.id;
7910
+ }
7715
7911
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7716
7912
  if (aliasResolved !== void 0) {
7717
7913
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7747,16 +7943,23 @@ function parseFlowchart(content, palette) {
7747
7943
  }
7748
7944
  }
7749
7945
  }
7946
+ if (!ref) {
7947
+ const loose = parseNodeRefLoose(peeled.seg);
7948
+ if (loose) {
7949
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7950
+ ref = loose.ref;
7951
+ }
7952
+ }
7750
7953
  if (!ref) continue;
7751
7954
  const node = getOrCreateNode(ref, lineNumber);
7752
7955
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7753
7956
  if (pendingArrow !== null) {
7754
- const sourceId = lastNodeId ?? implicitSourceId;
7957
+ const sourceId = lastNodeId ?? effectiveSource;
7755
7958
  if (sourceId) {
7756
7959
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7757
7960
  }
7758
7961
  pendingArrow = null;
7759
- } else if (lastNodeId === null && implicitSourceId === null) {
7962
+ } else if (lastNodeId === null && effectiveSource === null) {
7760
7963
  }
7761
7964
  lastNodeId = node.id;
7762
7965
  }
@@ -7838,7 +8041,8 @@ function parseFlowchart(content, palette) {
7838
8041
  continue;
7839
8042
  }
7840
8043
  }
7841
- processContentLine(trimmed, lineNumber, indent);
8044
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8045
+ if (lastId) prevLineLastNodeId = lastId;
7842
8046
  }
7843
8047
  if (result.nodes.length === 0 && !result.error) {
7844
8048
  const diag = makeDgmoError(
@@ -8921,7 +9125,12 @@ function parseERDiagram(content, palette) {
8921
9125
  }
8922
9126
  if (currentTagGroup && !contentStarted && indent > 0) {
8923
9127
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8924
- const { label, color } = extractColor(cleanEntry, palette);
9128
+ const { label, color } = extractColor(
9129
+ cleanEntry,
9130
+ palette,
9131
+ result.diagnostics,
9132
+ lineNumber
9133
+ );
8925
9134
  if (isDefault) {
8926
9135
  currentTagGroup.defaultValue = label;
8927
9136
  } else if (currentTagGroup.entries.length === 0) {
@@ -9239,12 +9448,7 @@ function parseOrg(content, palette) {
9239
9448
  diagnostics: [],
9240
9449
  error: null
9241
9450
  };
9242
- const fail = (line11, message) => {
9243
- const diag = makeDgmoError(line11, message);
9244
- result.diagnostics.push(diag);
9245
- result.error = formatDgmoError(diag);
9246
- return result;
9247
- };
9451
+ const fail = makeFail(result);
9248
9452
  const pushError = (line11, message) => {
9249
9453
  const diag = makeDgmoError(line11, message);
9250
9454
  result.diagnostics.push(diag);
@@ -9353,7 +9557,12 @@ function parseOrg(content, palette) {
9353
9557
  const indent2 = measureIndent(line11);
9354
9558
  if (indent2 > 0) {
9355
9559
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9356
- const { label, color } = extractColor(cleanEntry, palette);
9560
+ const { label, color } = extractColor(
9561
+ cleanEntry,
9562
+ palette,
9563
+ result.diagnostics,
9564
+ lineNumber
9565
+ );
9357
9566
  if (isDefault) {
9358
9567
  currentTagGroup.defaultValue = label;
9359
9568
  } else if (currentTagGroup.entries.length === 0) {
@@ -9639,12 +9848,7 @@ function parseSitemap(content, palette) {
9639
9848
  diagnostics: [],
9640
9849
  error: null
9641
9850
  };
9642
- const fail = (line11, message) => {
9643
- const diag = makeDgmoError(line11, message);
9644
- result.diagnostics.push(diag);
9645
- result.error = formatDgmoError(diag);
9646
- return result;
9647
- };
9851
+ const fail = makeFail(result);
9648
9852
  const pushError = (line11, message) => {
9649
9853
  const diag = makeDgmoError(line11, message);
9650
9854
  result.diagnostics.push(diag);
@@ -9745,7 +9949,12 @@ function parseSitemap(content, palette) {
9745
9949
  const indent2 = measureIndent(line11);
9746
9950
  if (indent2 > 0) {
9747
9951
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9748
- const { label, color } = extractColor(cleanEntry, palette);
9952
+ const { label, color } = extractColor(
9953
+ cleanEntry,
9954
+ palette,
9955
+ result.diagnostics,
9956
+ lineNumber
9957
+ );
9749
9958
  currentTagGroup.entries.push({
9750
9959
  value: label,
9751
9960
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11460,7 +11669,7 @@ var init_chart_types = __esm({
11460
11669
  },
11461
11670
  {
11462
11671
  id: "sequence",
11463
- description: "Message / interaction flows",
11672
+ description: "Message request and response interaction flows",
11464
11673
  fallback: true
11465
11674
  },
11466
11675
  {
@@ -11551,7 +11760,7 @@ var init_chart_types = __esm({
11551
11760
  },
11552
11761
  {
11553
11762
  id: "slope",
11554
- description: "Change between two periods"
11763
+ description: "Change between 2 time periods"
11555
11764
  },
11556
11765
  {
11557
11766
  id: "sankey",
@@ -11580,7 +11789,7 @@ var init_chart_types = __esm({
11580
11789
  // ── Tier 4 — General-purpose data charts ──────────────────
11581
11790
  {
11582
11791
  id: "bar",
11583
- description: "Categorical comparisons",
11792
+ description: "Categorical comparisons for 3 - 5 figures",
11584
11793
  fallback: true
11585
11794
  },
11586
11795
  {
@@ -11842,7 +12051,9 @@ function parseChart(content, palette) {
11842
12051
  if (dataValues) {
11843
12052
  const { label: rawLabel, color: pointColor } = extractColor(
11844
12053
  dataValues.label,
11845
- palette
12054
+ palette,
12055
+ result.diagnostics,
12056
+ lineNumber
11846
12057
  );
11847
12058
  const [first, ...rest] = dataValues.values;
11848
12059
  result.data.push({
@@ -11907,6 +12118,12 @@ function parseChart(content, palette) {
11907
12118
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11908
12119
  );
11909
12120
  }
12121
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12122
+ warn(
12123
+ result.seriesLineNumber ?? 1,
12124
+ `Plain "bar" shows only the first series ("${result.seriesNames[0]}"); the other ${result.seriesNames.length - 1} are dropped at render. Use "bar-stacked" for stacked bars or "multi-line" to plot every series.`
12125
+ );
12126
+ }
11910
12127
  if (!result.error && result.seriesNames) {
11911
12128
  const expectedCount = result.seriesNames.length;
11912
12129
  for (const dp of result.data) {
@@ -12155,7 +12372,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12155
12372
  if (!dataRow || dataRow.values.length < 2) return null;
12156
12373
  const { label: rawLabel, color: pointColor } = extractColor(
12157
12374
  dataRow.label,
12158
- palette
12375
+ palette,
12376
+ diagnostics,
12377
+ lineNumber
12159
12378
  );
12160
12379
  return {
12161
12380
  name: rawLabel,
@@ -12293,11 +12512,15 @@ function parseExtendedChartFull(content, palette) {
12293
12512
  const targetResolved = resolveSlot(rawTarget);
12294
12513
  const { label: source, color: sourceColor } = extractColor(
12295
12514
  sourceResolved,
12296
- palette
12515
+ palette,
12516
+ result.diagnostics,
12517
+ lineNumber
12297
12518
  );
12298
12519
  const { label: target, color: targetColor } = extractColor(
12299
12520
  targetResolved,
12300
- palette
12521
+ palette,
12522
+ result.diagnostics,
12523
+ lineNumber
12301
12524
  );
12302
12525
  if (sourceColor || targetColor) {
12303
12526
  if (!result.nodeColors) result.nodeColors = {};
@@ -12349,7 +12572,9 @@ function parseExtendedChartFull(content, palette) {
12349
12572
  const targetResolved = resolveSlot(dataRow2.label);
12350
12573
  const { label: target, color: targetColor } = extractColor(
12351
12574
  targetResolved,
12352
- palette
12575
+ palette,
12576
+ result.diagnostics,
12577
+ lineNumber
12353
12578
  );
12354
12579
  if (targetColor) {
12355
12580
  if (!result.nodeColors) result.nodeColors = {};
@@ -12382,7 +12607,9 @@ function parseExtendedChartFull(content, palette) {
12382
12607
  const trimmedResolved = resolveSlot(trimmed);
12383
12608
  const { label: nodeName, color: nodeColor2 } = extractColor(
12384
12609
  trimmedResolved,
12385
- palette
12610
+ palette,
12611
+ result.diagnostics,
12612
+ lineNumber
12386
12613
  );
12387
12614
  if (nodeColor2) {
12388
12615
  if (!result.nodeColors) result.nodeColors = {};
@@ -12472,8 +12699,11 @@ function parseExtendedChartFull(content, palette) {
12472
12699
  min: parseFloat(rangeMatch[1]),
12473
12700
  max: parseFloat(rangeMatch[2])
12474
12701
  };
12702
+ continue;
12703
+ }
12704
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12705
+ continue;
12475
12706
  }
12476
- continue;
12477
12707
  }
12478
12708
  }
12479
12709
  if (firstToken === "no-name") {
@@ -12533,7 +12763,9 @@ function parseExtendedChartFull(content, palette) {
12533
12763
  if (colonIndex >= 0) {
12534
12764
  const { label: fnName, color: fnColor } = extractColor(
12535
12765
  trimmed.substring(0, colonIndex).trim(),
12536
- palette
12766
+ palette,
12767
+ result.diagnostics,
12768
+ lineNumber
12537
12769
  );
12538
12770
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12539
12771
  if (!result.functions) result.functions = [];
@@ -12581,7 +12813,9 @@ function parseExtendedChartFull(content, palette) {
12581
12813
  if (dataRow?.values.length === 1) {
12582
12814
  const { label: rawLabel, color: pointColor } = extractColor(
12583
12815
  dataRow.label,
12584
- palette
12816
+ palette,
12817
+ result.diagnostics,
12818
+ lineNumber
12585
12819
  );
12586
12820
  result.data.push({
12587
12821
  label: rawLabel,
@@ -12680,11 +12914,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12680
12914
  const sc = ctx ?? ScaleContext.identity();
12681
12915
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12682
12916
  if (parsed.type === "sankey") {
12683
- const bg = isDark ? palette.surface : palette.bg;
12917
+ const bg = themeBaseBg(palette, isDark);
12684
12918
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12685
12919
  }
12686
12920
  if (parsed.type === "chord") {
12687
- const bg = isDark ? palette.surface : palette.bg;
12921
+ const bg = themeBaseBg(palette, isDark);
12688
12922
  return buildChordOption(
12689
12923
  parsed,
12690
12924
  palette,
@@ -12725,7 +12959,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12725
12959
  );
12726
12960
  }
12727
12961
  if (parsed.type === "funnel") {
12728
- const bg = isDark ? palette.surface : palette.bg;
12962
+ const bg = themeBaseBg(palette, isDark);
12729
12963
  return buildFunnelOption(
12730
12964
  parsed,
12731
12965
  palette,
@@ -13321,7 +13555,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13321
13555
  const gridLeft = parsed.ylabel ? 12 : 3;
13322
13556
  const gridRight = 4;
13323
13557
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13324
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13558
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13559
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13325
13560
  let graphic;
13326
13561
  if (showLabels && points.length > 0) {
13327
13562
  const labelPoints = [];
@@ -13461,7 +13696,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13461
13696
  }
13462
13697
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13463
13698
  const sc = ctx ?? ScaleContext.identity();
13464
- const bg = isDark ? palette.surface : palette.bg;
13699
+ const bg = themeBaseBg(palette, isDark);
13465
13700
  const heatmapRows = parsed.heatmapRows ?? [];
13466
13701
  const columns = parsed.columns ?? [];
13467
13702
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13780,7 +14015,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13780
14015
  colors,
13781
14016
  titleConfig
13782
14017
  } = buildChartCommons(parsed, palette, isDark, sc);
13783
- const bg = isDark ? palette.surface : palette.bg;
14018
+ const bg = themeBaseBg(palette, isDark);
13784
14019
  switch (parsed.type) {
13785
14020
  case "bar":
13786
14021
  return buildBarOption(
@@ -14732,12 +14967,7 @@ function parseKanban(content, palette) {
14732
14967
  diagnostics: [],
14733
14968
  error: null
14734
14969
  };
14735
- const fail = (line11, message) => {
14736
- const diag = makeDgmoError(line11, message);
14737
- result.diagnostics.push(diag);
14738
- result.error = formatDgmoError(diag);
14739
- return result;
14740
- };
14970
+ const fail = makeFail(result);
14741
14971
  const warn = (line11, message) => {
14742
14972
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14743
14973
  };
@@ -14840,7 +15070,12 @@ function parseKanban(content, palette) {
14840
15070
  const indent2 = measureIndent(line11);
14841
15071
  if (indent2 > 0) {
14842
15072
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14843
- const { label, color } = extractColor(cleanEntry, palette);
15073
+ const { label, color } = extractColor(
15074
+ cleanEntry,
15075
+ palette,
15076
+ result.diagnostics,
15077
+ lineNumber
15078
+ );
14844
15079
  if (isDefault) {
14845
15080
  currentTagGroup.defaultValue = label;
14846
15081
  } else if (currentTagGroup.entries.length === 0) {
@@ -15184,12 +15419,7 @@ function parseC4(content, palette) {
15184
15419
  if (!result.error && severity === "error")
15185
15420
  result.error = formatDgmoError(diag);
15186
15421
  };
15187
- const fail = (line11, message) => {
15188
- const diag = makeDgmoError(line11, message);
15189
- result.diagnostics.push(diag);
15190
- result.error = formatDgmoError(diag);
15191
- return result;
15192
- };
15422
+ const fail = makeFail(result);
15193
15423
  if (!content?.trim()) {
15194
15424
  return fail(0, "No content provided");
15195
15425
  }
@@ -15290,7 +15520,12 @@ function parseC4(content, palette) {
15290
15520
  const indent2 = measureIndent(line11);
15291
15521
  if (indent2 > 0) {
15292
15522
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15293
- const { label, color } = extractColor(cleanEntry, palette);
15523
+ const { label, color } = extractColor(
15524
+ cleanEntry,
15525
+ palette,
15526
+ result.diagnostics,
15527
+ lineNumber
15528
+ );
15294
15529
  if (isDefault) {
15295
15530
  currentTagGroup.defaultValue = label;
15296
15531
  } else if (currentTagGroup.entries.length === 0) {
@@ -16955,8 +17190,66 @@ function parseInfra(content) {
16955
17190
  }
16956
17191
  }
16957
17192
  validateTagGroupNames(result.tagGroups, warn, setError);
17193
+ checkReachability(result);
16958
17194
  return result;
16959
17195
  }
17196
+ function checkReachability(result) {
17197
+ if (result.nodes.length === 0) return;
17198
+ const entries = result.nodes.filter((n) => n.isEdge);
17199
+ if (entries.length === 0) {
17200
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17201
+ result.diagnostics.push(
17202
+ makeDgmoError(
17203
+ line11,
17204
+ `Infra diagram has no 'internet' or 'edge' entry point \u2014 an infra diagram traces request traffic from an entry inward, so without one nothing carries traffic. Add an 'internet' or 'edge' node and route from it.`,
17205
+ "warning",
17206
+ "W_INFRA_NO_ENTRY"
17207
+ )
17208
+ );
17209
+ return;
17210
+ }
17211
+ const groupChildMap = /* @__PURE__ */ new Map();
17212
+ for (const node of result.nodes) {
17213
+ if (node.groupId) {
17214
+ const list = groupChildMap.get(node.groupId) ?? [];
17215
+ list.push(node.id);
17216
+ groupChildMap.set(node.groupId, list);
17217
+ }
17218
+ }
17219
+ const outbound = /* @__PURE__ */ new Map();
17220
+ for (const edge of result.edges) {
17221
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17222
+ const list = outbound.get(edge.sourceId) ?? [];
17223
+ list.push(...targets);
17224
+ outbound.set(edge.sourceId, list);
17225
+ }
17226
+ const reachable = /* @__PURE__ */ new Set();
17227
+ const queue = [];
17228
+ for (const entry of entries) {
17229
+ reachable.add(entry.id);
17230
+ queue.push(entry.id);
17231
+ }
17232
+ while (queue.length > 0) {
17233
+ const current = queue.shift();
17234
+ for (const next of outbound.get(current) ?? []) {
17235
+ if (!reachable.has(next)) {
17236
+ reachable.add(next);
17237
+ queue.push(next);
17238
+ }
17239
+ }
17240
+ }
17241
+ for (const node of result.nodes) {
17242
+ if (node.isEdge || reachable.has(node.id)) continue;
17243
+ result.diagnostics.push(
17244
+ makeDgmoError(
17245
+ node.lineNumber,
17246
+ `'${node.label}' is unreachable from an 'internet'/'edge' entry \u2014 no request traffic flows to it, so it's dead on an infra diagram. Connect it downstream of an entry, or remove it.`,
17247
+ "warning",
17248
+ "W_INFRA_UNREACHABLE"
17249
+ )
17250
+ );
17251
+ }
17252
+ }
16960
17253
  function stripNodeDecorations(name) {
16961
17254
  let s = name.trim();
16962
17255
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17246,7 +17539,12 @@ function parseGantt(content, palette) {
17246
17539
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17247
17540
  if (eraEntryMatch) {
17248
17541
  const eraLabelRaw = eraEntryMatch[3].trim();
17249
- const eraExtracted = extractColor(eraLabelRaw, palette);
17542
+ const eraExtracted = extractColor(
17543
+ eraLabelRaw,
17544
+ palette,
17545
+ diagnostics,
17546
+ lineNumber
17547
+ );
17250
17548
  result.eras.push({
17251
17549
  startDate: eraEntryMatch[1],
17252
17550
  endDate: eraEntryMatch[2],
@@ -17268,7 +17566,12 @@ function parseGantt(content, palette) {
17268
17566
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17269
17567
  if (markerEntryMatch) {
17270
17568
  const markerLabelRaw = markerEntryMatch[2].trim();
17271
- const markerExtracted = extractColor(markerLabelRaw, palette);
17569
+ const markerExtracted = extractColor(
17570
+ markerLabelRaw,
17571
+ palette,
17572
+ diagnostics,
17573
+ lineNumber
17574
+ );
17272
17575
  result.markers.push({
17273
17576
  date: markerEntryMatch[1],
17274
17577
  label: markerExtracted.label,
@@ -17289,7 +17592,12 @@ function parseGantt(content, palette) {
17289
17592
  } else {
17290
17593
  if (COMMENT_RE.test(line11)) continue;
17291
17594
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17292
- const extracted = extractColor(cleanEntry, palette);
17595
+ const extracted = extractColor(
17596
+ cleanEntry,
17597
+ palette,
17598
+ diagnostics,
17599
+ lineNumber
17600
+ );
17293
17601
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17294
17602
  const isFirstEntry = currentTagGroup.entries.length === 0;
17295
17603
  currentTagGroup.entries.push({
@@ -17484,7 +17792,12 @@ function parseGantt(content, palette) {
17484
17792
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17485
17793
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17486
17794
  const eraLabelRaw = eraOffsetMatch[3].trim();
17487
- const eraExtracted = extractColor(eraLabelRaw, palette);
17795
+ const eraExtracted = extractColor(
17796
+ eraLabelRaw,
17797
+ palette,
17798
+ diagnostics,
17799
+ lineNumber
17800
+ );
17488
17801
  result.eras.push({
17489
17802
  startDate: "",
17490
17803
  endDate: "",
@@ -17500,7 +17813,12 @@ function parseGantt(content, palette) {
17500
17813
  if (markerOffsetMatch) {
17501
17814
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17502
17815
  const markerLabelRaw = markerOffsetMatch[2].trim();
17503
- const markerExtracted = extractColor(markerLabelRaw, palette);
17816
+ const markerExtracted = extractColor(
17817
+ markerLabelRaw,
17818
+ palette,
17819
+ diagnostics,
17820
+ lineNumber
17821
+ );
17504
17822
  result.markers.push({
17505
17823
  date: "",
17506
17824
  label: markerExtracted.label,
@@ -17574,7 +17892,12 @@ function parseGantt(content, palette) {
17574
17892
  const eraMatch = line11.match(ERA_RE);
17575
17893
  if (eraMatch) {
17576
17894
  const eraLabelRaw = eraMatch[3].trim();
17577
- const eraExtracted = extractColor(eraLabelRaw, palette);
17895
+ const eraExtracted = extractColor(
17896
+ eraLabelRaw,
17897
+ palette,
17898
+ diagnostics,
17899
+ lineNumber
17900
+ );
17578
17901
  result.eras.push({
17579
17902
  startDate: eraMatch[1],
17580
17903
  endDate: eraMatch[2],
@@ -17592,7 +17915,12 @@ function parseGantt(content, palette) {
17592
17915
  const markerMatch = line11.match(MARKER_RE);
17593
17916
  if (markerMatch) {
17594
17917
  const markerLabelRaw = markerMatch[2].trim();
17595
- const markerExtracted = extractColor(markerLabelRaw, palette);
17918
+ const markerExtracted = extractColor(
17919
+ markerLabelRaw,
17920
+ palette,
17921
+ diagnostics,
17922
+ lineNumber
17923
+ );
17596
17924
  result.markers.push({
17597
17925
  date: markerMatch[1],
17598
17926
  label: markerExtracted.label,
@@ -18989,6 +19317,7 @@ function parseBoxesAndLines(content, palette) {
18989
19317
  const nodes = [];
18990
19318
  const edges = [];
18991
19319
  const groups = [];
19320
+ const nodePositions = /* @__PURE__ */ new Map();
18992
19321
  const result = {
18993
19322
  type: "boxes-and-lines",
18994
19323
  title: null,
@@ -19022,6 +19351,8 @@ function parseBoxesAndLines(content, palette) {
19022
19351
  }
19023
19352
  const groupStack = [];
19024
19353
  let contentStarted = false;
19354
+ let inLayoutBlock = false;
19355
+ const LAYOUT_ENTRY_RE = /^(.+?):\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/;
19025
19356
  let currentTagGroup = null;
19026
19357
  const metaAliasMap = /* @__PURE__ */ new Map();
19027
19358
  const nameAliasMap = /* @__PURE__ */ new Map();
@@ -19061,7 +19392,7 @@ function parseBoxesAndLines(content, palette) {
19061
19392
  const trimmed = raw.trim();
19062
19393
  const indent = measureIndent2(raw);
19063
19394
  if (!trimmed || trimmed.startsWith("//")) continue;
19064
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19395
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
19065
19396
  result.diagnostics.push(
19066
19397
  makeDgmoError(
19067
19398
  lineNum,
@@ -19190,7 +19521,12 @@ function parseBoxesAndLines(content, palette) {
19190
19521
  if (tagBlockMatch.inlineValues) {
19191
19522
  for (const rawVal of tagBlockMatch.inlineValues) {
19192
19523
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19193
- const { label, color } = extractColor(cleanVal);
19524
+ const { label, color } = extractColor(
19525
+ cleanVal,
19526
+ palette,
19527
+ result.diagnostics,
19528
+ lineNum
19529
+ );
19194
19530
  newTagGroup.entries.push({
19195
19531
  value: label,
19196
19532
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19207,7 +19543,12 @@ function parseBoxesAndLines(content, palette) {
19207
19543
  }
19208
19544
  if (currentTagGroup && !contentStarted && indent > 0) {
19209
19545
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19210
- const { label, color } = extractColor(cleanEntry);
19546
+ const { label, color } = extractColor(
19547
+ cleanEntry,
19548
+ palette,
19549
+ result.diagnostics,
19550
+ lineNum
19551
+ );
19211
19552
  currentTagGroup.entries.push({
19212
19553
  value: label,
19213
19554
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19223,6 +19564,42 @@ function parseBoxesAndLines(content, palette) {
19223
19564
  if (currentTagGroup && indent === 0) {
19224
19565
  currentTagGroup = null;
19225
19566
  }
19567
+ if (!inLayoutBlock && indent === 0 && trimmed === "layout") {
19568
+ let isBlock = false;
19569
+ for (let j = i + 1; j < lines.length; j++) {
19570
+ const peek = lines[j];
19571
+ if (!peek.trim()) continue;
19572
+ isBlock = measureIndent2(peek) > 0 && LAYOUT_ENTRY_RE.test(peek.trim());
19573
+ break;
19574
+ }
19575
+ if (isBlock) {
19576
+ flushDescription();
19577
+ closeGroupsToIndent(0);
19578
+ inLayoutBlock = true;
19579
+ continue;
19580
+ }
19581
+ }
19582
+ if (inLayoutBlock) {
19583
+ if (indent > 0) {
19584
+ const lm = trimmed.match(LAYOUT_ENTRY_RE);
19585
+ if (lm) {
19586
+ nodePositions.set(lm[1].trim(), {
19587
+ x: Number(lm[2]),
19588
+ y: Number(lm[3])
19589
+ });
19590
+ } else {
19591
+ result.diagnostics.push(
19592
+ makeDgmoError(
19593
+ lineNum,
19594
+ `Invalid layout entry "${trimmed}" \u2014 expected "<node-id>: <x>, <y>"`,
19595
+ "warning"
19596
+ )
19597
+ );
19598
+ }
19599
+ continue;
19600
+ }
19601
+ inLayoutBlock = false;
19602
+ }
19226
19603
  if (descState !== null) {
19227
19604
  if (indent > descState.indent) {
19228
19605
  if (trimmed.includes("->") || trimmed.includes("<->")) {
@@ -19475,6 +19852,22 @@ function parseBoxesAndLines(content, palette) {
19475
19852
  );
19476
19853
  }
19477
19854
  finalizeAutoTagColors(result.tagGroups);
19855
+ if (nodePositions.size > 0) {
19856
+ const nodeLabelSet = new Set(result.nodes.map((n) => n.label));
19857
+ for (const id of nodePositions.keys()) {
19858
+ if (!nodeLabelSet.has(id)) {
19859
+ pushWarning(0, `layout entry for unknown node "${id}" (ignored)`);
19860
+ }
19861
+ }
19862
+ const unpositioned = result.nodes.filter((n) => !nodePositions.has(n.label)).map((n) => n.label);
19863
+ if (unpositioned.length > 0) {
19864
+ pushWarning(
19865
+ 0,
19866
+ `layout block is partial \u2014 ${unpositioned.length} node(s) without coordinates (${unpositioned.slice(0, 5).join(", ")}${unpositioned.length > 5 ? "\u2026" : ""}); ignoring the block and auto-laying-out`
19867
+ );
19868
+ }
19869
+ result.nodePositions = nodePositions;
19870
+ }
19478
19871
  if (result.tagGroups.length > 0) {
19479
19872
  injectDefaultTagMetadata(result.nodes, result.tagGroups);
19480
19873
  validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
@@ -19753,7 +20146,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19753
20146
  metadata
19754
20147
  };
19755
20148
  }
19756
- var MAX_GROUP_DEPTH;
20149
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19757
20150
  var init_parser18 = __esm({
19758
20151
  "src/boxes-and-lines/parser.ts"() {
19759
20152
  "use strict";
@@ -19765,6 +20158,8 @@ var init_parser18 = __esm({
19765
20158
  init_reserved_key_registry();
19766
20159
  init_notes();
19767
20160
  MAX_GROUP_DEPTH = 2;
20161
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20162
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19768
20163
  }
19769
20164
  });
19770
20165
 
@@ -19784,12 +20179,7 @@ function parseMindmap(content, palette) {
19784
20179
  diagnostics: [],
19785
20180
  error: null
19786
20181
  };
19787
- const fail = (line11, message) => {
19788
- const diag = makeDgmoError(line11, message);
19789
- result.diagnostics.push(diag);
19790
- result.error = formatDgmoError(diag);
19791
- return result;
19792
- };
20182
+ const fail = makeFail(result);
19793
20183
  const pushError = (line11, message) => {
19794
20184
  const diag = makeDgmoError(line11, message);
19795
20185
  result.diagnostics.push(diag);
@@ -19902,7 +20292,12 @@ function parseMindmap(content, palette) {
19902
20292
  const indent2 = measureIndent(line11);
19903
20293
  if (indent2 > 0) {
19904
20294
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19905
- const { label, color } = extractColor(cleanEntry, palette);
20295
+ const { label, color } = extractColor(
20296
+ cleanEntry,
20297
+ palette,
20298
+ result.diagnostics,
20299
+ lineNumber
20300
+ );
19906
20301
  if (isDefault) {
19907
20302
  currentTagGroup.defaultValue = label;
19908
20303
  } else if (currentTagGroup.entries.length === 0) {
@@ -19979,6 +20374,7 @@ function parseMindmap(content, palette) {
19979
20374
  collectAll(result.roots);
19980
20375
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
19981
20376
  validateTagGroupNames(result.tagGroups, pushWarning);
20377
+ cascadeTagMetadata(result.roots, result.tagGroups);
19982
20378
  }
19983
20379
  if (result.roots.length === 0 && !result.error) {
19984
20380
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20585,7 +20981,12 @@ function parseWireframe(content) {
20585
20981
  }
20586
20982
  if (indent > 0 && currentTagGroup) {
20587
20983
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20588
- const { label, color } = extractColor(cleanEntry);
20984
+ const { label, color } = extractColor(
20985
+ cleanEntry,
20986
+ void 0,
20987
+ diagnostics,
20988
+ lineNumber
20989
+ );
20589
20990
  currentTagGroup.entries.push({
20590
20991
  value: label,
20591
20992
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20833,12 +21234,7 @@ function parseTechRadar(content) {
20833
21234
  diagnostics: [],
20834
21235
  error: null
20835
21236
  };
20836
- const fail = (line11, message) => {
20837
- const diag = makeDgmoError(line11, message);
20838
- result.diagnostics.push(diag);
20839
- result.error = formatDgmoError(diag);
20840
- return result;
20841
- };
21237
+ const fail = makeFail(result);
20842
21238
  const warn = (line11, message) => {
20843
21239
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20844
21240
  };
@@ -21206,12 +21602,7 @@ function parseCycle(content) {
21206
21602
  let state = "top";
21207
21603
  let currentNode = null;
21208
21604
  let currentEdge = null;
21209
- const fail = (line11, message) => {
21210
- const diag = makeDgmoError(line11, message);
21211
- result.diagnostics.push(diag);
21212
- result.error = formatDgmoError(diag);
21213
- return result;
21214
- };
21605
+ const fail = makeFail(result);
21215
21606
  const warn = (line11, message, severity = "warning") => {
21216
21607
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21217
21608
  };
@@ -21488,12 +21879,7 @@ function parseJourneyMap(content, palette) {
21488
21879
  diagnostics: [],
21489
21880
  error: null
21490
21881
  };
21491
- const fail = (line11, message) => {
21492
- const diag = makeDgmoError(line11, message);
21493
- result.diagnostics.push(diag);
21494
- result.error = formatDgmoError(diag);
21495
- return result;
21496
- };
21882
+ const fail = makeFail(result);
21497
21883
  const warn = (line11, message) => {
21498
21884
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21499
21885
  };
@@ -21575,7 +21961,18 @@ function parseJourneyMap(content, palette) {
21575
21961
  }
21576
21962
  }
21577
21963
  } else {
21578
- personaName = afterKeyword;
21964
+ const colorMatch = afterKeyword.match(/^(.+?)\s+color:\s*(\S+)$/i);
21965
+ if (colorMatch) {
21966
+ personaName = colorMatch[1].trim();
21967
+ personaColor = resolveColorWithDiagnostic(
21968
+ colorMatch[2],
21969
+ lineNumber,
21970
+ result.diagnostics,
21971
+ palette
21972
+ ) ?? void 0;
21973
+ } else {
21974
+ personaName = afterKeyword;
21975
+ }
21579
21976
  }
21580
21977
  if (!personaName) {
21581
21978
  return fail(lineNumber, "persona requires a name");
@@ -21631,7 +22028,12 @@ function parseJourneyMap(content, palette) {
21631
22028
  if (currentTagGroup && !contentStarted) {
21632
22029
  if (indent > 0) {
21633
22030
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21634
- const { label, color } = extractColor(cleanEntry, palette);
22031
+ const { label, color } = extractColor(
22032
+ cleanEntry,
22033
+ palette,
22034
+ result.diagnostics,
22035
+ lineNumber
22036
+ );
21635
22037
  if (isDefault) {
21636
22038
  currentTagGroup.defaultValue = label;
21637
22039
  } else if (currentTagGroup.entries.length === 0) {
@@ -22003,12 +22405,7 @@ function parsePyramid(content) {
22003
22405
  const lines = content.split("\n");
22004
22406
  let headerParsed = false;
22005
22407
  let currentLayer = null;
22006
- const fail = (line11, message) => {
22007
- const diag = makeDgmoError(line11, message);
22008
- result.diagnostics.push(diag);
22009
- result.error = formatDgmoError(diag);
22010
- return result;
22011
- };
22408
+ const fail = makeFail(result);
22012
22409
  const warn = (line11, message, severity = "warning") => {
22013
22410
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22014
22411
  };
@@ -22155,12 +22552,7 @@ function parseRing(content) {
22155
22552
  const lines = content.split("\n");
22156
22553
  let headerParsed = false;
22157
22554
  let currentLayer = null;
22158
- const fail = (line11, message) => {
22159
- const diag = makeDgmoError(line11, message);
22160
- result.diagnostics.push(diag);
22161
- result.error = formatDgmoError(diag);
22162
- return result;
22163
- };
22555
+ const fail = makeFail(result);
22164
22556
  const warn = (line11, message, severity = "warning") => {
22165
22557
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22166
22558
  };
@@ -22624,12 +23016,7 @@ function parseRaci(content, palette) {
22624
23016
  diagnostics: [],
22625
23017
  error: null
22626
23018
  };
22627
- const fail = (line11, message) => {
22628
- const diag = makeDgmoError(line11, message);
22629
- result.diagnostics.push(diag);
22630
- result.error = formatDgmoError(diag);
22631
- return result;
22632
- };
23019
+ const fail = makeFail(result);
22633
23020
  const warn = (line11, message, code) => {
22634
23021
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22635
23022
  };
@@ -23232,6 +23619,81 @@ function measureInfra(content) {
23232
23619
  const parsed = parseInfra(content);
23233
23620
  return { nodes: parsed.nodes.length };
23234
23621
  }
23622
+ function minDimsSequence(c) {
23623
+ return {
23624
+ width: Math.max((c.participants ?? 2) * 80, 320),
23625
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23626
+ };
23627
+ }
23628
+ function minDimsRaci(c) {
23629
+ return {
23630
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23631
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23632
+ };
23633
+ }
23634
+ function minDimsMindmap(c) {
23635
+ return {
23636
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23637
+ height: Math.max((c.depth ?? 2) * 60, 200)
23638
+ };
23639
+ }
23640
+ function minDimsTechRadar() {
23641
+ return { width: 360, height: 400 };
23642
+ }
23643
+ function minDimsHeatmap(c) {
23644
+ return {
23645
+ width: Math.max((c.columns ?? 3) * 40, 300),
23646
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23647
+ };
23648
+ }
23649
+ function minDimsArc(c) {
23650
+ return {
23651
+ width: 300,
23652
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23653
+ };
23654
+ }
23655
+ function minDimsOrg(c) {
23656
+ return {
23657
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23658
+ height: Math.max((c.depth ?? 2) * 80, 200)
23659
+ };
23660
+ }
23661
+ function minDimsGantt(c) {
23662
+ return {
23663
+ width: 400,
23664
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23665
+ };
23666
+ }
23667
+ function minDimsKanban(c) {
23668
+ return {
23669
+ width: Math.max((c.columns ?? 3) * 120, 360),
23670
+ height: 300
23671
+ };
23672
+ }
23673
+ function minDimsEntities(c) {
23674
+ return {
23675
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23676
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23677
+ };
23678
+ }
23679
+ function minDimsGraph(c) {
23680
+ return {
23681
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23682
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23683
+ };
23684
+ }
23685
+ function minDimsPert(c) {
23686
+ return {
23687
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23688
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23689
+ };
23690
+ }
23691
+ function minDimsInfra(c) {
23692
+ return {
23693
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23694
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23695
+ };
23696
+ }
23235
23697
  function isExtendedChartParser(parse) {
23236
23698
  return EXTENDED_CHART_DOORS.has(parse);
23237
23699
  }
@@ -23275,33 +23737,50 @@ var init_chart_type_registry = __esm({
23275
23737
  id: "sequence",
23276
23738
  category: "diagram",
23277
23739
  parse: parseSequenceDgmo,
23278
- measure: measureSequence
23740
+ measure: measureSequence,
23741
+ minDims: minDimsSequence
23279
23742
  },
23280
23743
  {
23281
23744
  id: "flowchart",
23282
23745
  category: "diagram",
23283
23746
  parse: parseFlowchart,
23284
- measure: measureFlowchart
23747
+ measure: measureFlowchart,
23748
+ minDims: minDimsGraph
23285
23749
  },
23286
23750
  {
23287
23751
  id: "class",
23288
23752
  category: "diagram",
23289
23753
  parse: parseClassDiagram,
23290
- measure: measureClass
23754
+ measure: measureClass,
23755
+ minDims: minDimsEntities
23756
+ },
23757
+ {
23758
+ id: "er",
23759
+ category: "diagram",
23760
+ parse: parseERDiagram,
23761
+ measure: measureER,
23762
+ minDims: minDimsEntities
23291
23763
  },
23292
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23293
23764
  {
23294
23765
  id: "state",
23295
23766
  category: "diagram",
23296
23767
  parse: parseState,
23297
- measure: measureStateGraph
23768
+ measure: measureStateGraph,
23769
+ minDims: minDimsGraph
23770
+ },
23771
+ {
23772
+ id: "org",
23773
+ category: "diagram",
23774
+ parse: parseOrg,
23775
+ measure: measureOrg,
23776
+ minDims: minDimsOrg
23298
23777
  },
23299
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23300
23778
  {
23301
23779
  id: "kanban",
23302
23780
  category: "diagram",
23303
23781
  parse: parseKanban,
23304
- measure: measureKanban
23782
+ measure: measureKanban,
23783
+ minDims: minDimsKanban
23305
23784
  },
23306
23785
  { id: "c4", category: "diagram", parse: parseC4 },
23307
23786
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23309,25 +23788,40 @@ var init_chart_type_registry = __esm({
23309
23788
  id: "infra",
23310
23789
  category: "diagram",
23311
23790
  parse: parseInfra,
23312
- measure: measureInfra
23791
+ measure: measureInfra,
23792
+ minDims: minDimsInfra
23313
23793
  },
23314
23794
  {
23315
23795
  id: "gantt",
23316
23796
  category: "diagram",
23317
23797
  parse: parseGantt,
23318
- measure: measureGantt
23798
+ measure: measureGantt,
23799
+ minDims: minDimsGantt
23800
+ },
23801
+ {
23802
+ id: "pert",
23803
+ category: "diagram",
23804
+ parse: parsePert,
23805
+ measure: measurePert,
23806
+ minDims: minDimsPert
23319
23807
  },
23320
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23321
23808
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23322
23809
  {
23323
23810
  id: "mindmap",
23324
23811
  category: "diagram",
23325
23812
  parse: parseMindmap,
23326
- measure: measureMindmap
23813
+ measure: measureMindmap,
23814
+ minDims: minDimsMindmap
23327
23815
  },
23328
23816
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23329
23817
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23330
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23818
+ {
23819
+ id: "raci",
23820
+ category: "diagram",
23821
+ parse: parseRaci,
23822
+ measure: measureRaci,
23823
+ minDims: minDimsRaci
23824
+ },
23331
23825
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23332
23826
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23333
23827
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23349,7 +23843,8 @@ var init_chart_type_registry = __esm({
23349
23843
  id: "heatmap",
23350
23844
  category: "data-chart",
23351
23845
  parse: parseHeatmap,
23352
- measure: measureHeatmap
23846
+ measure: measureHeatmap,
23847
+ minDims: minDimsHeatmap
23353
23848
  },
23354
23849
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23355
23850
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23359,7 +23854,8 @@ var init_chart_type_registry = __esm({
23359
23854
  id: "arc",
23360
23855
  category: "visualization",
23361
23856
  parse: parseArc,
23362
- measure: measureArc
23857
+ measure: measureArc,
23858
+ minDims: minDimsArc
23363
23859
  },
23364
23860
  { id: "timeline", category: "visualization", parse: parseTimeline },
23365
23861
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23369,7 +23865,8 @@ var init_chart_type_registry = __esm({
23369
23865
  id: "tech-radar",
23370
23866
  category: "visualization",
23371
23867
  parse: parseTechRadar,
23372
- measure: measureTechRadar
23868
+ measure: measureTechRadar,
23869
+ minDims: minDimsTechRadar
23373
23870
  },
23374
23871
  { id: "cycle", category: "visualization", parse: parseCycle },
23375
23872
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24516,6 +25013,63 @@ var init_export_container = __esm({
24516
25013
  }
24517
25014
  });
24518
25015
 
25016
+ // src/utils/card.ts
25017
+ function renderNodeCard(container, opts) {
25018
+ const rect = container.append("rect").attr("x", 0).attr("y", 0).attr("width", opts.width).attr("height", opts.height).attr("rx", opts.rx).attr("fill", opts.fill).attr("stroke", opts.stroke).attr("stroke-width", opts.strokeWidth);
25019
+ if (opts.dashed) {
25020
+ rect.attr("stroke-dasharray", "6 3");
25021
+ }
25022
+ container.append("text").attr("x", opts.width / 2).attr("y", opts.headerHeight / 2 + opts.labelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", opts.labelColor).attr("font-size", opts.labelFontSize).attr("font-weight", "bold").text(opts.label);
25023
+ const meta = opts.meta;
25024
+ if (!meta || meta.rows.length === 0) return;
25025
+ container.append("line").attr("x1", 0).attr("y1", opts.headerHeight).attr("x2", opts.width).attr("y2", opts.headerHeight).attr("stroke", meta.separatorColor).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
25026
+ const keyX = meta.keyX ?? 10;
25027
+ const maxKeyWidth = Math.max(
25028
+ ...meta.rows.map(([key]) => measureText(`${key}: `, meta.fontSize))
25029
+ );
25030
+ const valueX = keyX + maxKeyWidth;
25031
+ const metaStartY = opts.headerHeight + meta.separatorGap + meta.fontSize;
25032
+ for (let i = 0; i < meta.rows.length; i++) {
25033
+ const [displayKey, value] = meta.rows[i];
25034
+ const rowY = metaStartY + i * meta.lineHeight;
25035
+ container.append("text").attr("x", keyX).attr("y", rowY).attr("fill", meta.textColor).attr("font-size", meta.fontSize).text(`${displayKey}: `);
25036
+ container.append("text").attr("x", valueX).attr("y", rowY).attr("fill", meta.textColor).attr("font-size", meta.fontSize).text(value);
25037
+ }
25038
+ }
25039
+ function renderCollapseBar(container, opts) {
25040
+ container.append("clipPath").attr("id", opts.clipId).append("rect").attr("width", opts.width).attr("height", opts.height).attr("rx", opts.rx);
25041
+ container.append("rect").attr("x", opts.inset).attr("y", opts.height - opts.barHeight).attr("width", opts.width - opts.inset * 2).attr("height", opts.barHeight).attr("fill", opts.fill).attr("clip-path", `url(#${opts.clipId})`).attr("class", opts.className);
25042
+ }
25043
+ var init_card = __esm({
25044
+ "src/utils/card.ts"() {
25045
+ "use strict";
25046
+ init_text_measure();
25047
+ }
25048
+ });
25049
+
25050
+ // src/utils/visual-conventions.ts
25051
+ var NODE_STROKE_WIDTH, EDGE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, COLLAPSE_BAR_INSET, HEADER_HEIGHT2, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT2, SEPARATOR_GAP2, COLLAPSE_BAR_HEIGHT, CONTAINER_HEADER_HEIGHT, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2;
25052
+ var init_visual_conventions = __esm({
25053
+ "src/utils/visual-conventions.ts"() {
25054
+ "use strict";
25055
+ NODE_STROKE_WIDTH = 1.5;
25056
+ EDGE_STROKE_WIDTH = 1.5;
25057
+ CARD_RADIUS = 6;
25058
+ CONTAINER_RADIUS = 8;
25059
+ COLLAPSE_BAR_INSET = 0;
25060
+ HEADER_HEIGHT2 = 28;
25061
+ LABEL_FONT_SIZE2 = 13;
25062
+ META_FONT_SIZE2 = 11;
25063
+ META_LINE_HEIGHT2 = 16;
25064
+ SEPARATOR_GAP2 = 6;
25065
+ COLLAPSE_BAR_HEIGHT = 6;
25066
+ CONTAINER_HEADER_HEIGHT = 28;
25067
+ CONTAINER_LABEL_FONT_SIZE = 13;
25068
+ CONTAINER_META_FONT_SIZE = 11;
25069
+ CONTAINER_META_LINE_HEIGHT2 = 16;
25070
+ }
25071
+ });
25072
+
24519
25073
  // src/org/renderer.ts
24520
25074
  var renderer_exports = {};
24521
25075
  __export(renderer_exports, {
@@ -24533,7 +25087,7 @@ function nodeStroke(palette, nodeColor2) {
24533
25087
  }
24534
25088
  function containerFill(palette, isDark, nodeColor2) {
24535
25089
  if (nodeColor2) {
24536
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25090
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24537
25091
  }
24538
25092
  return mix(palette.surface, palette.bg, 40);
24539
25093
  }
@@ -24673,9 +25227,16 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24673
25227
  }
24674
25228
  }
24675
25229
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
24676
- const clipId = `clip-${c.nodeId}`;
24677
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", sContainerRadius);
24678
- cG.append("rect").attr("x", sCollapseBarInset).attr("y", c.height - sCollapseBarHeight).attr("width", c.width - sCollapseBarInset * 2).attr("height", sCollapseBarHeight).attr("fill", containerStroke(palette, colorOff ? void 0 : c.color)).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
25230
+ renderCollapseBar(cG, {
25231
+ width: c.width,
25232
+ height: c.height,
25233
+ barHeight: sCollapseBarHeight,
25234
+ inset: sCollapseBarInset,
25235
+ rx: sContainerRadius,
25236
+ fill: containerStroke(palette, colorOff ? void 0 : c.color),
25237
+ clipId: `clip-${c.nodeId}`,
25238
+ className: "org-collapse-bar"
25239
+ });
24679
25240
  }
24680
25241
  if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
24681
25242
  const iconSize = 14;
@@ -24684,10 +25245,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24684
25245
  const iconY = iconPad;
24685
25246
  const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24686
25247
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25248
+ const iconColor = contrastText(
25249
+ fill2,
25250
+ palette.textOnFillLight,
25251
+ palette.textOnFillDark
25252
+ );
24687
25253
  const cx = iconSize / 2;
24688
25254
  const cy = iconSize / 2;
24689
- 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);
24690
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25255
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25256
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24691
25257
  }
24692
25258
  }
24693
25259
  for (const edge of layout.edges) {
@@ -24726,42 +25292,48 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24726
25292
  solid
24727
25293
  );
24728
25294
  const stroke2 = nodeStroke(palette, colorOff ? void 0 : node.color);
24729
- const rect = nodeG.append("rect").attr("x", 0).attr("y", 0).attr("width", node.width).attr("height", node.height).attr("rx", sCardRadius).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", sNodeStrokeWidth);
24730
- if (node.isContainer) {
24731
- rect.attr("stroke-dasharray", "6 3");
24732
- }
24733
25295
  const labelColor = contrastText(
24734
25296
  fill2,
24735
25297
  palette.textOnFillLight,
24736
25298
  palette.textOnFillDark
24737
25299
  );
24738
- nodeG.append("text").attr("x", node.width / 2).attr("y", sHeaderHeight / 2 + sLabelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", sLabelFontSize).attr("font-weight", "bold").text(node.label);
24739
25300
  const metaEntries = Object.entries(node.metadata);
24740
- if (metaEntries.length > 0) {
24741
- nodeG.append("line").attr("x1", 0).attr("y1", sHeaderHeight).attr("x2", node.width).attr("y2", sHeaderHeight).attr("stroke", solid ? labelColor : stroke2).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
24742
- const metaDisplayKeys = metaEntries.map(
24743
- ([k]) => displayNames.get(k) ?? k
24744
- );
24745
- const maxKeyWidth = Math.max(
24746
- ...metaDisplayKeys.map((k) => measureText(`${k}: `, sMetaFontSize))
24747
- );
24748
- const valueX = 10 + maxKeyWidth;
24749
- const metaStartY = sHeaderHeight + sSeparatorGap + sMetaFontSize;
24750
- for (let i = 0; i < metaEntries.length; i++) {
24751
- const [, value] = metaEntries[i];
24752
- const displayKey = metaDisplayKeys[i];
24753
- const rowY = metaStartY + i * sMetaLineHeight;
24754
- nodeG.append("text").attr("x", 10).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(`${displayKey}: `);
24755
- nodeG.append("text").attr("x", valueX).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(value);
25301
+ renderNodeCard(nodeG, {
25302
+ width: node.width,
25303
+ height: node.height,
25304
+ rx: sCardRadius,
25305
+ fill: fill2,
25306
+ stroke: stroke2,
25307
+ strokeWidth: sNodeStrokeWidth,
25308
+ ...node.isContainer && { dashed: true },
25309
+ label: node.label,
25310
+ labelColor,
25311
+ labelFontSize: sLabelFontSize,
25312
+ headerHeight: sHeaderHeight,
25313
+ ...metaEntries.length > 0 && {
25314
+ meta: {
25315
+ rows: metaEntries.map(
25316
+ ([k, value]) => [displayNames.get(k) ?? k, value]
25317
+ ),
25318
+ fontSize: sMetaFontSize,
25319
+ lineHeight: sMetaLineHeight,
25320
+ separatorGap: sSeparatorGap,
25321
+ separatorColor: solid ? labelColor : stroke2,
25322
+ textColor: labelColor
25323
+ }
24756
25324
  }
24757
- }
25325
+ });
24758
25326
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
24759
- const clipId = `clip-${node.id}`;
24760
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", sCardRadius);
24761
- nodeG.append("rect").attr("x", sCollapseBarInset).attr("y", node.height - sCollapseBarHeight).attr("width", node.width - sCollapseBarInset * 2).attr("height", sCollapseBarHeight).attr(
24762
- "fill",
24763
- solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color)
24764
- ).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
25327
+ renderCollapseBar(nodeG, {
25328
+ width: node.width,
25329
+ height: node.height,
25330
+ barHeight: sCollapseBarHeight,
25331
+ inset: sCollapseBarInset,
25332
+ rx: sCardRadius,
25333
+ fill: solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color),
25334
+ clipId: `clip-${node.id}`,
25335
+ className: "org-collapse-bar"
25336
+ });
24765
25337
  }
24766
25338
  if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
24767
25339
  const iconSize = 14;
@@ -24770,10 +25342,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24770
25342
  const iconY = iconPad;
24771
25343
  const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24772
25344
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25345
+ const iconColor = labelColor;
24773
25346
  const cx = iconSize / 2;
24774
25347
  const cy = iconSize / 2;
24775
- 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);
24776
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25348
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25349
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24777
25350
  }
24778
25351
  }
24779
25352
  if (hasAncestorTrail) {
@@ -24905,7 +25478,7 @@ function renderOrgForExport(content, theme, palette) {
24905
25478
  return extractExportSvg(container, theme);
24906
25479
  });
24907
25480
  }
24908
- var d3Selection6, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
25481
+ var d3Selection6, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
24909
25482
  var init_renderer = __esm({
24910
25483
  "src/org/renderer.ts"() {
24911
25484
  "use strict";
@@ -24919,27 +25492,14 @@ var init_renderer = __esm({
24919
25492
  init_layout();
24920
25493
  init_legend_constants();
24921
25494
  init_legend_integration();
25495
+ init_card();
24922
25496
  init_text_measure();
24923
25497
  init_legend_layout();
24924
25498
  init_title_constants();
25499
+ init_visual_conventions();
24925
25500
  DIAGRAM_PADDING = 20;
24926
25501
  MAX_SCALE = 3;
24927
25502
  TITLE_HEIGHT = 30;
24928
- LABEL_FONT_SIZE2 = 13;
24929
- META_FONT_SIZE2 = 11;
24930
- META_LINE_HEIGHT2 = 16;
24931
- HEADER_HEIGHT2 = 28;
24932
- SEPARATOR_GAP2 = 6;
24933
- EDGE_STROKE_WIDTH = 1.5;
24934
- NODE_STROKE_WIDTH = 1.5;
24935
- CARD_RADIUS = 6;
24936
- CONTAINER_RADIUS = 8;
24937
- CONTAINER_LABEL_FONT_SIZE = 13;
24938
- CONTAINER_META_FONT_SIZE = 11;
24939
- CONTAINER_META_LINE_HEIGHT2 = 16;
24940
- CONTAINER_HEADER_HEIGHT = 28;
24941
- COLLAPSE_BAR_HEIGHT = 6;
24942
- COLLAPSE_BAR_INSET = 0;
24943
25503
  ANCESTOR_DOT_R = 4;
24944
25504
  ANCESTOR_LABEL_FONT_SIZE = 11;
24945
25505
  ANCESTOR_ROW_HEIGHT = 22;
@@ -25707,7 +26267,7 @@ function nodeStroke2(_palette, nodeColor2) {
25707
26267
  }
25708
26268
  function containerFill2(palette, isDark, nodeColor2) {
25709
26269
  if (nodeColor2) {
25710
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26270
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25711
26271
  }
25712
26272
  return mix(palette.surface, palette.bg, 40);
25713
26273
  }
@@ -25721,21 +26281,21 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25721
26281
  if (width <= 0 || height <= 0) return;
25722
26282
  const ctx = ScaleContext.identity();
25723
26283
  const sDiagramPadding = ctx.aesthetic(DIAGRAM_PADDING2);
25724
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE4);
25725
- const sMetaFontSize = ctx.text(META_FONT_SIZE4);
25726
- const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT4);
25727
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT4);
25728
- const sSeparatorGap = ctx.structural(SEPARATOR_GAP4);
25729
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH2);
25730
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH2);
26284
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE2);
26285
+ const sMetaFontSize = ctx.text(META_FONT_SIZE2);
26286
+ const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT2);
26287
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT2);
26288
+ const sSeparatorGap = ctx.structural(SEPARATOR_GAP2);
26289
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
26290
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
25731
26291
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE);
25732
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE3);
25733
- const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE2);
25734
- const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT4);
25735
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT2);
26292
+ const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE);
26293
+ const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE);
26294
+ const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT2);
26295
+ const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT);
25736
26296
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
25737
26297
  const sTitleHeight = ctx.structural(TITLE_HEIGHT2);
25738
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT2);
26298
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
25739
26299
  const sLegendFixedGap = ctx.aesthetic(LEGEND_FIXED_GAP2);
25740
26300
  const hasLegend = layout.legend.length > 0;
25741
26301
  const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
@@ -25826,7 +26386,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25826
26386
  }
25827
26387
  const fill2 = containerFill2(palette, isDark, c.color);
25828
26388
  const stroke2 = containerStroke2(palette, c.color);
25829
- cG.append("rect").attr("x", 0).attr("y", 0).attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS2).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-opacity", 0.35).attr("stroke-width", sNodeStrokeWidth);
26389
+ cG.append("rect").attr("x", 0).attr("y", 0).attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-opacity", 0.35).attr("stroke-width", sNodeStrokeWidth);
25830
26390
  cG.append("text").attr("x", c.width / 2).attr("y", sContainerHeaderHeight / 2 + sContainerLabelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sContainerLabelFontSize).attr("font-weight", "bold").text(c.label);
25831
26391
  const metaEntries = Object.entries(c.metadata);
25832
26392
  if (metaEntries.length > 0) {
@@ -25851,7 +26411,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25851
26411
  }
25852
26412
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
25853
26413
  const clipId = `clip-${c.nodeId}`;
25854
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS2);
26414
+ cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
25855
26415
  cG.append("rect").attr("y", c.height - sCollapseBarHeight).attr("width", c.width).attr("height", sCollapseBarHeight).attr("fill", c.color ?? palette.primary).attr("opacity", 0.5).attr("clip-path", `url(#${clipId})`);
25856
26416
  cG.append("text").attr("x", c.width / 2).attr("y", c.height - sCollapseBarHeight - 6).attr("text-anchor", "middle").attr("fill", palette.textMuted).attr("font-size", sMetaFontSize).text(`+${c.hiddenCount}`);
25857
26417
  }
@@ -25890,13 +26450,23 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25890
26450
  const solid = parsed.options["solid-fill"] === "on";
25891
26451
  const fill2 = nodeFill2(palette, isDark, node.color, solid);
25892
26452
  const stroke2 = nodeStroke2(palette, node.color);
25893
- nodeG.append("rect").attr("x", 0).attr("y", 0).attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS2).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", sNodeStrokeWidth);
25894
26453
  const labelColor = contrastText(
25895
26454
  fill2,
25896
26455
  palette.textOnFillLight,
25897
26456
  palette.textOnFillDark
25898
26457
  );
25899
- nodeG.append("text").attr("x", node.width / 2).attr("y", sHeaderHeight / 2 + sLabelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", sLabelFontSize).attr("font-weight", "bold").text(node.label);
26458
+ renderNodeCard(nodeG, {
26459
+ width: node.width,
26460
+ height: node.height,
26461
+ rx: CARD_RADIUS,
26462
+ fill: fill2,
26463
+ stroke: stroke2,
26464
+ strokeWidth: sNodeStrokeWidth,
26465
+ label: node.label,
26466
+ labelColor,
26467
+ labelFontSize: sLabelFontSize,
26468
+ headerHeight: sHeaderHeight
26469
+ });
25900
26470
  const metaEntries = Object.entries(node.metadata);
25901
26471
  if (metaEntries.length > 0) {
25902
26472
  nodeG.append("line").attr("x1", 0).attr("y1", sHeaderHeight).attr("x2", node.width).attr("y2", sHeaderHeight).attr("stroke", solid ? labelColor : stroke2).attr("stroke-opacity", 0.3);
@@ -25931,7 +26501,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25931
26501
  }
25932
26502
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
25933
26503
  const clipId = `clip-${node.id}`;
25934
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS2);
26504
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
25935
26505
  nodeG.append("rect").attr("y", node.height - sCollapseBarHeight).attr("width", node.width).attr("height", sCollapseBarHeight).attr("fill", solid ? labelColor : node.color ?? palette.primary).attr("opacity", 0.5).attr("clip-path", `url(#${clipId})`);
25936
26506
  }
25937
26507
  }
@@ -26061,7 +26631,7 @@ async function renderSitemapForExport(content, theme, palette) {
26061
26631
  document.body.removeChild(container);
26062
26632
  return svgHtml;
26063
26633
  }
26064
- var d3Selection7, d3Shape2, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, LABEL_FONT_SIZE4, META_FONT_SIZE4, META_LINE_HEIGHT4, HEADER_HEIGHT4, SEPARATOR_GAP4, EDGE_STROKE_WIDTH2, NODE_STROKE_WIDTH2, CARD_RADIUS2, CONTAINER_RADIUS2, CONTAINER_LABEL_FONT_SIZE3, CONTAINER_META_FONT_SIZE2, CONTAINER_META_LINE_HEIGHT4, CONTAINER_HEADER_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, COLLAPSE_BAR_HEIGHT2, LEGEND_FIXED_GAP2, lineGenerator, lineGeneratorLinear;
26634
+ var d3Selection7, d3Shape2, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, LEGEND_FIXED_GAP2, lineGenerator, lineGeneratorLinear;
26065
26635
  var init_renderer2 = __esm({
26066
26636
  "src/sitemap/renderer.ts"() {
26067
26637
  "use strict";
@@ -26076,27 +26646,15 @@ var init_renderer2 = __esm({
26076
26646
  init_legend_integration();
26077
26647
  init_legend_layout();
26078
26648
  init_scaling();
26649
+ init_card();
26079
26650
  init_title_constants();
26651
+ init_visual_conventions();
26080
26652
  DIAGRAM_PADDING2 = 20;
26081
26653
  MAX_SCALE2 = 3;
26082
26654
  TITLE_HEIGHT2 = 30;
26083
- LABEL_FONT_SIZE4 = 13;
26084
- META_FONT_SIZE4 = 11;
26085
- META_LINE_HEIGHT4 = 16;
26086
- HEADER_HEIGHT4 = 28;
26087
- SEPARATOR_GAP4 = 6;
26088
- EDGE_STROKE_WIDTH2 = 1.5;
26089
- NODE_STROKE_WIDTH2 = 1.5;
26090
- CARD_RADIUS2 = 6;
26091
- CONTAINER_RADIUS2 = 8;
26092
- CONTAINER_LABEL_FONT_SIZE3 = 13;
26093
- CONTAINER_META_FONT_SIZE2 = 11;
26094
- CONTAINER_META_LINE_HEIGHT4 = 16;
26095
- CONTAINER_HEADER_HEIGHT2 = 28;
26096
26655
  ARROWHEAD_W = 10;
26097
26656
  ARROWHEAD_H = 7;
26098
26657
  EDGE_LABEL_FONT_SIZE = 11;
26099
- COLLAPSE_BAR_HEIGHT2 = 6;
26100
26658
  LEGEND_FIXED_GAP2 = 8;
26101
26659
  lineGenerator = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveBasis);
26102
26660
  lineGeneratorLinear = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveLinear);
@@ -26263,7 +26821,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
26263
26821
  const sCardMetaLineHeight = ctx.structural(CARD_META_LINE_HEIGHT);
26264
26822
  const sCardSeparatorGap = ctx.structural(CARD_SEPARATOR_GAP);
26265
26823
  const sCardGap = ctx.aesthetic(CARD_GAP);
26266
- const sCardRadius = ctx.structural(CARD_RADIUS3);
26824
+ const sCardRadius = ctx.structural(CARD_RADIUS);
26267
26825
  const sCardPaddingX = ctx.aesthetic(CARD_PADDING_X);
26268
26826
  const sCardPaddingY = ctx.aesthetic(CARD_PADDING_Y);
26269
26827
  const sCardStrokeWidth = ctx.structural(CARD_STROKE_WIDTH);
@@ -26643,7 +27201,7 @@ function computeSwimlaneLayout(parsed, buckets, baseLayout, collapsedLanes, coll
26643
27201
  const totalHeight = laneY - sLaneGap + sColumnPadding + sDiagramPadding;
26644
27202
  return { columnXs, lanes, totalWidth, totalHeight, startY };
26645
27203
  }
26646
- function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, isDark, activeTagGroup, collapsedLanes, collapsedColumns, hiddenMetaGroups, sDiagramPadding = DIAGRAM_PADDING3, sTitleHeight = TITLE_HEIGHT3, sLaneHeaderWidth = LANE_HEADER_WIDTH, sColumnGap = COLUMN_GAP, sCollapsedColumnWidth = COLLAPSED_COLUMN_WIDTH, sColumnHeaderHeight = COLUMN_HEADER_HEIGHT, sColumnPadding = COLUMN_PADDING, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardPaddingY = CARD_PADDING_Y, sCardGap = CARD_GAP, sCollapsedLaneHeight = COLLAPSED_LANE_HEIGHT, sLaneGap = LANE_GAP, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT, sColumnHeaderFontSize = COLUMN_HEADER_FONT_SIZE, sColumnRadius = COLUMN_RADIUS, sColumnHeaderRadius = COLUMN_HEADER_RADIUS, sWipFontSize = WIP_FONT_SIZE, sCardRadius = CARD_RADIUS3, sCardPaddingX = CARD_PADDING_X, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE) {
27204
+ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, isDark, activeTagGroup, collapsedLanes, collapsedColumns, hiddenMetaGroups, sDiagramPadding = DIAGRAM_PADDING3, sTitleHeight = TITLE_HEIGHT3, sLaneHeaderWidth = LANE_HEADER_WIDTH, sColumnGap = COLUMN_GAP, sCollapsedColumnWidth = COLLAPSED_COLUMN_WIDTH, sColumnHeaderHeight = COLUMN_HEADER_HEIGHT, sColumnPadding = COLUMN_PADDING, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardPaddingY = CARD_PADDING_Y, sCardGap = CARD_GAP, sCollapsedLaneHeight = COLLAPSED_LANE_HEIGHT, sLaneGap = LANE_GAP, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT, sColumnHeaderFontSize = COLUMN_HEADER_FONT_SIZE, sColumnRadius = COLUMN_RADIUS, sColumnHeaderRadius = COLUMN_HEADER_RADIUS, sWipFontSize = WIP_FONT_SIZE, sCardRadius = CARD_RADIUS, sCardPaddingX = CARD_PADDING_X, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE) {
26647
27205
  const visibleColumns = parsed.columns.filter((c) => !isArchiveColumn(c.name));
26648
27206
  const buckets = bucketCardsBySwimlane(visibleColumns, swimlaneGroup);
26649
27207
  const grid = computeSwimlaneLayout(
@@ -26783,7 +27341,7 @@ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, is
26783
27341
  }
26784
27342
  }
26785
27343
  }
26786
- function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palette, isDark, hiddenMetaGroups, solid, sCardRadius = CARD_RADIUS3, sCardPaddingX = CARD_PADDING_X, sCardPaddingY = CARD_PADDING_Y, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT) {
27344
+ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palette, isDark, hiddenMetaGroups, solid, sCardRadius = CARD_RADIUS, sCardPaddingX = CARD_PADDING_X, sCardPaddingY = CARD_PADDING_Y, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT) {
26787
27345
  const card = cardLayout.card;
26788
27346
  const resolvedColor = resolveCardTagColor(card, tagGroups, activeTagGroup);
26789
27347
  const tagMeta = resolveCardTagMeta(card, tagGroups, hiddenMetaGroups);
@@ -26833,7 +27391,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
26833
27391
  }
26834
27392
  }
26835
27393
  }
26836
- var d3Selection8, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_RADIUS3, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS, COLLAPSED_COLUMN_WIDTH, COLLAPSED_LANE_HEIGHT, LANE_HEADER_WIDTH, LANE_GAP;
27394
+ var d3Selection8, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS, COLLAPSED_COLUMN_WIDTH, COLLAPSED_LANE_HEIGHT, LANE_HEADER_WIDTH, LANE_GAP;
26837
27395
  var init_renderer3 = __esm({
26838
27396
  "src/kanban/renderer.ts"() {
26839
27397
  "use strict";
@@ -26848,6 +27406,7 @@ var init_renderer3 = __esm({
26848
27406
  init_legend_integration();
26849
27407
  init_scaling();
26850
27408
  init_text_measure();
27409
+ init_visual_conventions();
26851
27410
  init_title_constants();
26852
27411
  DIAGRAM_PADDING3 = 20;
26853
27412
  COLUMN_GAP = 16;
@@ -26858,7 +27417,6 @@ var init_renderer3 = __esm({
26858
27417
  CARD_META_LINE_HEIGHT = 14;
26859
27418
  CARD_SEPARATOR_GAP = 4;
26860
27419
  CARD_GAP = 8;
26861
- CARD_RADIUS3 = 6;
26862
27420
  CARD_PADDING_X = 10;
26863
27421
  CARD_PADDING_Y = 6;
26864
27422
  CARD_STROKE_WIDTH = 1.5;
@@ -27159,8 +27717,8 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
27159
27717
  const sClassFontSize = ctx.text(CLASS_FONT_SIZE2);
27160
27718
  const sMemberFontSize = ctx.text(MEMBER_FONT_SIZE2);
27161
27719
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE2);
27162
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH3);
27163
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH3);
27720
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
27721
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
27164
27722
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT2);
27165
27723
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y2);
27166
27724
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X);
@@ -27419,7 +27977,7 @@ function renderClassDiagramForExport(content, theme, palette) {
27419
27977
  return extractExportSvg(container, theme);
27420
27978
  });
27421
27979
  }
27422
- var d3Selection9, d3Shape3, DIAGRAM_PADDING4, MAX_SCALE3, CLASS_FONT_SIZE2, MEMBER_FONT_SIZE2, EDGE_LABEL_FONT_SIZE2, EDGE_STROKE_WIDTH3, NODE_STROKE_WIDTH3, MEMBER_LINE_HEIGHT2, COMPARTMENT_PADDING_Y2, MEMBER_PADDING_X, CLASS_TYPE_MAP, CLASS_TYPE_ORDER, LEGEND_GROUP_NAME, lineGenerator2;
27980
+ var d3Selection9, d3Shape3, DIAGRAM_PADDING4, MAX_SCALE3, CLASS_FONT_SIZE2, MEMBER_FONT_SIZE2, EDGE_LABEL_FONT_SIZE2, MEMBER_LINE_HEIGHT2, COMPARTMENT_PADDING_Y2, MEMBER_PADDING_X, CLASS_TYPE_MAP, CLASS_TYPE_ORDER, LEGEND_GROUP_NAME, lineGenerator2;
27423
27981
  var init_renderer4 = __esm({
27424
27982
  "src/class/renderer.ts"() {
27425
27983
  "use strict";
@@ -27436,13 +27994,12 @@ var init_renderer4 = __esm({
27436
27994
  init_scaling();
27437
27995
  init_text_measure();
27438
27996
  init_note_box();
27997
+ init_visual_conventions();
27439
27998
  DIAGRAM_PADDING4 = 20;
27440
27999
  MAX_SCALE3 = 3;
27441
28000
  CLASS_FONT_SIZE2 = 13;
27442
28001
  MEMBER_FONT_SIZE2 = 11;
27443
28002
  EDGE_LABEL_FONT_SIZE2 = 11;
27444
- EDGE_STROKE_WIDTH3 = 1.5;
27445
- NODE_STROKE_WIDTH3 = 1.5;
27446
28003
  MEMBER_LINE_HEIGHT2 = 18;
27447
28004
  COMPARTMENT_PADDING_Y2 = 8;
27448
28005
  MEMBER_PADDING_X = 10;
@@ -27936,7 +28493,7 @@ function constraintIcon(constraint) {
27936
28493
  return "\u25CB";
27937
28494
  }
27938
28495
  }
27939
- function drawCardinality(g, point, prevPoint, cardinality, color, useLabels, edgeLabelFontSize = EDGE_LABEL_FONT_SIZE4, edgeStrokeWidth = EDGE_STROKE_WIDTH4) {
28496
+ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels, edgeLabelFontSize = EDGE_LABEL_FONT_SIZE4, edgeStrokeWidth = EDGE_STROKE_WIDTH) {
27940
28497
  const dx = point.x - prevPoint.x;
27941
28498
  const dy = point.y - prevPoint.y;
27942
28499
  const len = Math.sqrt(dx * dx + dy * dy);
@@ -27996,8 +28553,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
27996
28553
  const sTableFontSize = ctx.text(TABLE_FONT_SIZE2);
27997
28554
  const sColumnFontSize = ctx.text(COLUMN_FONT_SIZE2);
27998
28555
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE4);
27999
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH4);
28000
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH4);
28556
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
28557
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
28001
28558
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT4);
28002
28559
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y4);
28003
28560
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X2);
@@ -28275,7 +28832,7 @@ function renderERDiagramForExport(content, theme, palette) {
28275
28832
  document.body.removeChild(container);
28276
28833
  }
28277
28834
  }
28278
- var d3Selection10, d3Shape4, DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABEL_FONT_SIZE4, EDGE_STROKE_WIDTH4, NODE_STROKE_WIDTH4, MEMBER_LINE_HEIGHT4, COMPARTMENT_PADDING_Y4, MEMBER_PADDING_X2, lineGenerator3;
28835
+ var d3Selection10, d3Shape4, DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABEL_FONT_SIZE4, MEMBER_LINE_HEIGHT4, COMPARTMENT_PADDING_Y4, MEMBER_PADDING_X2, lineGenerator3;
28279
28836
  var init_renderer5 = __esm({
28280
28837
  "src/er/renderer.ts"() {
28281
28838
  "use strict";
@@ -28294,13 +28851,12 @@ var init_renderer5 = __esm({
28294
28851
  init_parser9();
28295
28852
  init_layout4();
28296
28853
  init_classify();
28854
+ init_visual_conventions();
28297
28855
  DIAGRAM_PADDING5 = 20;
28298
28856
  MAX_SCALE4 = 3;
28299
28857
  TABLE_FONT_SIZE2 = 13;
28300
28858
  COLUMN_FONT_SIZE2 = 11;
28301
28859
  EDGE_LABEL_FONT_SIZE4 = 11;
28302
- EDGE_STROKE_WIDTH4 = 1.5;
28303
- NODE_STROKE_WIDTH4 = 1.5;
28304
28860
  MEMBER_LINE_HEIGHT4 = 18;
28305
28861
  COMPARTMENT_PADDING_Y4 = 8;
28306
28862
  MEMBER_PADDING_X2 = 10;
@@ -28518,7 +29074,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28518
29074
  onToggleDescriptions,
28519
29075
  onToggleControlsExpand,
28520
29076
  exportMode = false,
28521
- controlsHost
29077
+ controlsHost,
29078
+ rampDomain
28522
29079
  } = options ?? {};
28523
29080
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28524
29081
  const width = exportDims?.width ?? container.clientWidth;
@@ -28528,9 +29085,9 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28528
29085
  const sDiagramPadding = sctx.aesthetic(DIAGRAM_PADDING6);
28529
29086
  const sMinNodeFontSize = sctx.text(MIN_NODE_FONT_SIZE);
28530
29087
  const sEdgeLabelFontSize = sctx.text(EDGE_LABEL_FONT_SIZE5);
28531
- const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH5);
28532
- const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH5);
28533
- const sCollapseBarHeight = sctx.structural(COLLAPSE_BAR_HEIGHT3);
29088
+ const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH);
29089
+ const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH);
29090
+ const sCollapseBarHeight = sctx.structural(COLLAPSE_BAR_HEIGHT2);
28534
29091
  const sDescFontSize = sctx.text(DESC_FONT_SIZE);
28535
29092
  const sGroupLabelFontSize = sctx.text(GROUP_LABEL_FONT_SIZE);
28536
29093
  const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
@@ -28538,8 +29095,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28538
29095
  const sTitleY = sctx.structural(TITLE_Y);
28539
29096
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28540
29097
  const hasRamp = nodeValues.length > 0;
28541
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28542
- const rampMax = Math.max(...nodeValues);
29098
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29099
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28543
29100
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28544
29101
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28545
29102
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28622,8 +29179,32 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28622
29179
  const scaleX = width / (contentW + sDiagramPadding * 2);
28623
29180
  const scaleY = height / (contentH + sDiagramPadding * 2);
28624
29181
  const scale = Math.min(scaleX, scaleY, 3);
28625
- const offsetX = (width - contentW * scale) / 2;
28626
- const offsetY = sDiagramPadding + titleOffset + legendH;
29182
+ let centerShiftX = 0;
29183
+ let centerShiftY = 0;
29184
+ if (parsed.nodePositions && parsed.nodePositions.size > 0) {
29185
+ let bMinX = Infinity, bMinY = Infinity, bMaxX = -Infinity, bMaxY = -Infinity;
29186
+ const accB = (x, y) => {
29187
+ if (x < bMinX) bMinX = x;
29188
+ if (x > bMaxX) bMaxX = x;
29189
+ if (y < bMinY) bMinY = y;
29190
+ if (y > bMaxY) bMaxY = y;
29191
+ };
29192
+ for (const n of layout.nodes) {
29193
+ accB(n.x - n.width / 2, n.y - n.height / 2);
29194
+ accB(n.x + n.width / 2, n.y + n.height / 2);
29195
+ }
29196
+ for (const g of layout.groups) {
29197
+ accB(g.x - g.width / 2, g.y - g.height / 2);
29198
+ accB(g.x + g.width / 2, g.y + g.height / 2);
29199
+ }
29200
+ for (const e of layout.edges) for (const p of e.points) accB(p.x, p.y);
29201
+ if (Number.isFinite(bMinX)) {
29202
+ centerShiftX = (layout.width - bMaxX - bMinX) / 2;
29203
+ centerShiftY = (layout.height - bMaxY - bMinY) / 2;
29204
+ }
29205
+ }
29206
+ const offsetX = (width - contentW * scale) / 2 + centerShiftX * scale;
29207
+ const offsetY = sDiagramPadding + titleOffset + legendH + centerShiftY * scale;
28627
29208
  const svg = d3Selection11.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY).style("background", palette.bg);
28628
29209
  if (sctx.isBelowFloor) {
28629
29210
  svg.attr("width", "100%");
@@ -28667,7 +29248,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28667
29248
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28668
29249
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28669
29250
  if (group.collapsed) {
28670
- const fillColor = isDark ? palette.surface : palette.bg;
29251
+ const fillColor = themeBaseBg(palette, isDark);
28671
29252
  const strokeColor = palette.border;
28672
29253
  groupG.append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", group.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", sNodeStrokeWidth);
28673
29254
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -28759,7 +29340,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28759
29340
  const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
28760
29341
  edgeGroups.set(i, edgeG);
28761
29342
  const markerId = `bl-arrow-${color.replace("#", "")}`;
28762
- const gen = parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
29343
+ const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
28763
29344
  const path = edgeG.append("path").attr("class", "bl-edge").attr("d", gen(points) ?? "").attr("fill", "none").attr("stroke", color).attr("stroke-width", sEdgeStrokeWidth).attr("marker-end", `url(#${markerId})`);
28764
29345
  if (le.bidirectional) {
28765
29346
  const revId = `bl-arrow-rev-${color.replace("#", "")}`;
@@ -29023,6 +29604,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29023
29604
  });
29024
29605
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29025
29606
  }
29607
+ if (!exportDims && !exportMode) {
29608
+ const iconSize = 14;
29609
+ const focusG = svg.append("g").attr("class", "bl-focus-icon").attr("data-export-ignore", "true").style("display", "none").style("pointer-events", "auto").style("cursor", "pointer");
29610
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29611
+ const cx = iconSize / 2;
29612
+ const cy = iconSize / 2;
29613
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", palette.bg).attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
29614
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29615
+ }
29026
29616
  }
29027
29617
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29028
29618
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29040,7 +29630,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
29040
29630
  }
29041
29631
  });
29042
29632
  }
29043
- var d3Selection11, d3Shape5, DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE5, EDGE_STROKE_WIDTH5, NODE_STROKE_WIDTH5, NODE_RX, COLLAPSE_BAR_HEIGHT3, ARROWHEAD_W2, ARROWHEAD_H2, DESC_FONT_SIZE, DESC_LINE_HEIGHT, MAX_DESC_LINES, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, GROUP_LABEL_ZONE, RAMP_FLOOR, VALUE_FONT_SIZE, lineGeneratorLR, lineGeneratorTB;
29633
+ var d3Selection11, d3Shape5, DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE5, NODE_RX, COLLAPSE_BAR_HEIGHT2, ARROWHEAD_W2, ARROWHEAD_H2, DESC_FONT_SIZE, DESC_LINE_HEIGHT, MAX_DESC_LINES, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, GROUP_LABEL_ZONE, RAMP_FLOOR, VALUE_FONT_SIZE, lineGeneratorLR, lineGeneratorTB, lineGeneratorStraight;
29044
29634
  var init_renderer6 = __esm({
29045
29635
  "src/boxes-and-lines/renderer.ts"() {
29046
29636
  "use strict";
@@ -29058,14 +29648,13 @@ var init_renderer6 = __esm({
29058
29648
  init_wrapped_desc();
29059
29649
  init_scaling();
29060
29650
  init_text_measure();
29651
+ init_visual_conventions();
29061
29652
  DIAGRAM_PADDING6 = 20;
29062
29653
  NODE_FONT_SIZE = 11;
29063
29654
  MIN_NODE_FONT_SIZE = 9;
29064
29655
  EDGE_LABEL_FONT_SIZE5 = 11;
29065
- EDGE_STROKE_WIDTH5 = 1.5;
29066
- NODE_STROKE_WIDTH5 = 1.5;
29067
29656
  NODE_RX = 8;
29068
- COLLAPSE_BAR_HEIGHT3 = 4;
29657
+ COLLAPSE_BAR_HEIGHT2 = 4;
29069
29658
  ARROWHEAD_W2 = 5;
29070
29659
  ARROWHEAD_H2 = 4;
29071
29660
  DESC_FONT_SIZE = 10;
@@ -29079,6 +29668,7 @@ var init_renderer6 = __esm({
29079
29668
  VALUE_FONT_SIZE = 11;
29080
29669
  lineGeneratorLR = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29081
29670
  lineGeneratorTB = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29671
+ lineGeneratorStraight = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveLinear);
29082
29672
  }
29083
29673
  });
29084
29674
 
@@ -29619,7 +30209,7 @@ function shuffle(a, r) {
29619
30209
  return x;
29620
30210
  }
29621
30211
  function flatten(d) {
29622
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30212
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29623
30213
  const pts = [];
29624
30214
  let i = 0, cx = 0, cy = 0, cmd = "";
29625
30215
  const num = () => parseFloat(toks[i++]);
@@ -29663,17 +30253,10 @@ function flatten(d) {
29663
30253
  }
29664
30254
  return pts;
29665
30255
  }
29666
- function segPoint(p1, p2, p3, p4) {
29667
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29668
- if (Math.abs(den) < 1e-9) return null;
29669
- 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;
29670
- 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;
29671
- }
29672
- function countSplineCrossings(layout) {
29673
- const center = /* @__PURE__ */ new Map();
29674
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29675
- for (const g of layout.groups)
29676
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30256
+ function flatPolys(layout) {
30257
+ const key = layout.edges;
30258
+ const hit = FLAT_CACHE.get(key);
30259
+ if (hit) return hit;
29677
30260
  const polys = layout.edges.map((e) => {
29678
30261
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29679
30262
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29683,8 +30266,24 @@ function countSplineCrossings(layout) {
29683
30266
  if (p.y < y0) y0 = p.y;
29684
30267
  if (p.y > y1) y1 = p.y;
29685
30268
  }
29686
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30269
+ return { pts, x0, y0, x1, y1 };
29687
30270
  });
30271
+ FLAT_CACHE.set(key, polys);
30272
+ return polys;
30273
+ }
30274
+ function segPoint(p1, p2, p3, p4) {
30275
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30276
+ if (Math.abs(den) < 1e-9) return null;
30277
+ const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
30278
+ return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
30279
+ }
30280
+ function countSplineCrossings(layout, floor = Infinity) {
30281
+ const center = /* @__PURE__ */ new Map();
30282
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30283
+ for (const g of layout.groups)
30284
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30285
+ const polys = flatPolys(layout);
30286
+ const edges = layout.edges;
29688
30287
  const R = 34;
29689
30288
  let total = 0;
29690
30289
  for (let a = 0; a < polys.length; a++)
@@ -29692,23 +30291,33 @@ function countSplineCrossings(layout) {
29692
30291
  const A = polys[a], B = polys[b];
29693
30292
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29694
30293
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29695
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30294
+ const ea = edges[a], eb = edges[b];
30295
+ let sh0, sh1;
30296
+ if (ea.source === eb.source || ea.source === eb.target)
30297
+ sh0 = center.get(ea.source);
30298
+ if (ea.target === eb.source || ea.target === eb.target)
30299
+ sh1 = center.get(ea.target);
29696
30300
  const hits = [];
29697
- for (let i = 1; i < A.pts.length; i++)
29698
- for (let j = 1; j < B.pts.length; j++) {
29699
- const p = segPoint(
29700
- A.pts[i - 1],
29701
- A.pts[i],
29702
- B.pts[j - 1],
29703
- B.pts[j]
29704
- );
30301
+ const ap = A.pts, bp = B.pts;
30302
+ for (let i = 1; i < ap.length; i++) {
30303
+ const a0 = ap[i - 1], a1 = ap[i];
30304
+ const axMin = a0.x < a1.x ? a0.x : a1.x, axMax = a0.x > a1.x ? a0.x : a1.x, ayMin = a0.y < a1.y ? a0.y : a1.y, ayMax = a0.y > a1.y ? a0.y : a1.y;
30305
+ for (let j = 1; j < bp.length; j++) {
30306
+ const b0 = bp[j - 1], b1 = bp[j];
30307
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30308
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30309
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30310
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30311
+ const p = segPoint(a0, a1, b0, b1);
29705
30312
  if (!p) continue;
29706
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29707
- continue;
30313
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30314
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29708
30315
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29709
30316
  hits.push(p);
29710
30317
  }
30318
+ }
29711
30319
  total += hits.length;
30320
+ if (total > floor) return total;
29712
30321
  }
29713
30322
  return total;
29714
30323
  }
@@ -29746,17 +30355,8 @@ function detectEdgeOverlaps(layout, opts) {
29746
30355
  w: g.width,
29747
30356
  h: g.height
29748
30357
  });
29749
- const polys = layout.edges.map((e) => {
29750
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29751
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29752
- for (const p of pts) {
29753
- if (p.x < x0) x0 = p.x;
29754
- if (p.x > x1) x1 = p.x;
29755
- if (p.y < y0) y0 = p.y;
29756
- if (p.y > y1) y1 = p.y;
29757
- }
29758
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29759
- });
30358
+ const polys = flatPolys(layout);
30359
+ const edges = layout.edges;
29760
30360
  const runs = [];
29761
30361
  for (let a = 0; a < polys.length; a++)
29762
30362
  for (let b = a + 1; b < polys.length; b++) {
@@ -29764,7 +30364,12 @@ function detectEdgeOverlaps(layout, opts) {
29764
30364
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29765
30365
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29766
30366
  continue;
29767
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30367
+ const ea = edges[a], eb = edges[b];
30368
+ let shr0, shr1;
30369
+ if (ea.source === eb.source || ea.source === eb.target)
30370
+ shr0 = rect.get(ea.source);
30371
+ if (ea.target === eb.source || ea.target === eb.target)
30372
+ shr1 = rect.get(ea.target);
29768
30373
  let run = [];
29769
30374
  let runLen = 0;
29770
30375
  const flush = () => {
@@ -29778,7 +30383,7 @@ function detectEdgeOverlaps(layout, opts) {
29778
30383
  runLen = 0;
29779
30384
  };
29780
30385
  for (const p of A.pts) {
29781
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30386
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
29782
30387
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29783
30388
  if (covered) {
29784
30389
  if (run.length)
@@ -29813,9 +30418,10 @@ function detectEdgeNodePierces(layout, opts) {
29813
30418
  });
29814
30419
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
29815
30420
  const out = [];
30421
+ const polys = flatPolys(layout);
29816
30422
  layout.edges.forEach((e, idx) => {
29817
30423
  if (e.points.length < 2) return;
29818
- const poly = flatten(splineGen(e.points) ?? "");
30424
+ const poly = polys[idx].pts;
29819
30425
  for (const r of rects) {
29820
30426
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29821
30427
  continue;
@@ -30162,8 +30768,10 @@ function edgeLength(layout) {
30162
30768
  );
30163
30769
  return total;
30164
30770
  }
30165
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30771
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30166
30772
  const hideDescriptions = opts?.hideDescriptions ?? false;
30773
+ const onProgress = opts?.onProgress;
30774
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30167
30775
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30168
30776
  if (collapseInfo) {
30169
30777
  const missing = /* @__PURE__ */ new Set();
@@ -30266,6 +30874,156 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30266
30874
  return { x: rect.x + dx * s, y: rect.y + dy * s };
30267
30875
  };
30268
30876
  const isInsideRect = (p, rect) => Math.abs(p.x - rect.x) <= rect.w / 2 && Math.abs(p.y - rect.y) <= rect.h / 2;
30877
+ const pinned = parsed.nodePositions;
30878
+ const groupLabelSet = new Set(parsed.groups.map((g) => g.label));
30879
+ const groupsAreFlat = parsed.groups.every(
30880
+ (g) => !g.parentGroup && !g.children.some((c) => groupLabelSet.has(c))
30881
+ );
30882
+ const allOriginalGroupLabels = new Set(
30883
+ (collapseInfo?.originalGroups ?? parsed.groups).map((g) => g.label)
30884
+ );
30885
+ const collapsedAreFlatPinned = collapsedGroupLabels.size === 0 || pinned !== void 0 && collapseInfo !== void 0 && [...collapsedGroupLabels].every((label) => {
30886
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
30887
+ if (!og || og.parentGroup) return false;
30888
+ return og.children.every(
30889
+ (c) => pinned.has(c) && !allOriginalGroupLabels.has(c)
30890
+ );
30891
+ });
30892
+ const allPinned = pinned !== void 0 && (parsed.nodes.length > 0 || collapsedGroupLabels.size > 0) && parsed.nodes.every((n2) => pinned.has(n2.label)) && groupsAreFlat && collapsedAreFlatPinned;
30893
+ function placePinned(pins) {
30894
+ const collapsedPosByGid = /* @__PURE__ */ new Map();
30895
+ const collapsedBoxes = [];
30896
+ if (collapseInfo)
30897
+ for (const label of collapsedGroupLabels) {
30898
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
30899
+ if (!og) continue;
30900
+ let cx0 = Infinity, cy0 = Infinity, cx1 = -Infinity, cy1 = -Infinity;
30901
+ for (const c of og.children) {
30902
+ const p = pins.get(c);
30903
+ if (!p) continue;
30904
+ cx0 = Math.min(cx0, p.x);
30905
+ cx1 = Math.max(cx1, p.x);
30906
+ cy0 = Math.min(cy0, p.y);
30907
+ cy1 = Math.max(cy1, p.y);
30908
+ }
30909
+ if (!Number.isFinite(cx0)) continue;
30910
+ const cx = (cx0 + cx1) / 2;
30911
+ const cy = (cy0 + cy1) / 2;
30912
+ collapsedPosByGid.set(`__group_${label}`, { x: cx, y: cy });
30913
+ collapsedBoxes.push({
30914
+ label,
30915
+ lineNumber: og.lineNumber,
30916
+ childCount: collapseInfo.collapsedChildCounts.get(label) ?? og.children.length,
30917
+ x: cx,
30918
+ y: cy
30919
+ });
30920
+ }
30921
+ const posOf = (label) => pins.get(label) ?? collapsedPosByGid.get(label);
30922
+ const rectOf = (label) => {
30923
+ const p = posOf(label);
30924
+ const s = sizes.get(label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30925
+ return { x: p.x, y: p.y, w: s.width, h: s.height };
30926
+ };
30927
+ const nodes = parsed.nodes.map((n2) => {
30928
+ const r = rectOf(n2.label);
30929
+ return { label: n2.label, x: r.x, y: r.y, width: r.w, height: r.h };
30930
+ });
30931
+ const edges = parsed.edges.flatMap((e) => {
30932
+ const sp = posOf(e.source);
30933
+ const tp = posOf(e.target);
30934
+ if (!sp || !tp) return [];
30935
+ const srcRect = rectOf(e.source);
30936
+ const tgtRect = rectOf(e.target);
30937
+ const p0 = rectBorderPoint(srcRect, tp);
30938
+ const p1 = rectBorderPoint(tgtRect, sp);
30939
+ return [
30940
+ {
30941
+ source: e.source,
30942
+ target: e.target,
30943
+ ...e.label !== void 0 && { label: e.label },
30944
+ bidirectional: e.bidirectional,
30945
+ lineNumber: e.lineNumber,
30946
+ points: [p0, p1],
30947
+ yOffset: 0,
30948
+ parallelCount: 1,
30949
+ metadata: e.metadata,
30950
+ straight: true
30951
+ }
30952
+ ];
30953
+ });
30954
+ const GROUP_PAD = 16;
30955
+ const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
30956
+ const groups = [];
30957
+ for (const grp of parsed.groups) {
30958
+ let gx0 = Infinity, gy0 = Infinity, gx1 = -Infinity, gy1 = -Infinity;
30959
+ for (const c of grp.children) {
30960
+ const n2 = nodeByLabel.get(c);
30961
+ if (!n2) continue;
30962
+ gx0 = Math.min(gx0, n2.x - n2.width / 2);
30963
+ gx1 = Math.max(gx1, n2.x + n2.width / 2);
30964
+ gy0 = Math.min(gy0, n2.y - n2.height / 2);
30965
+ gy1 = Math.max(gy1, n2.y + n2.height / 2);
30966
+ }
30967
+ if (!Number.isFinite(gx0)) continue;
30968
+ const x0 = gx0 - GROUP_PAD;
30969
+ const x1 = gx1 + GROUP_PAD;
30970
+ const y0 = gy0 - GROUP_LABEL_ZONE2;
30971
+ const y1 = gy1 + GROUP_PAD;
30972
+ groups.push({
30973
+ label: grp.label,
30974
+ lineNumber: grp.lineNumber,
30975
+ x: (x0 + x1) / 2,
30976
+ y: (y0 + y1) / 2,
30977
+ width: x1 - x0,
30978
+ height: y1 - y0,
30979
+ collapsed: false,
30980
+ childCount: grp.children.length
30981
+ });
30982
+ }
30983
+ for (const cb of collapsedBoxes) {
30984
+ groups.push({
30985
+ label: cb.label,
30986
+ lineNumber: cb.lineNumber,
30987
+ x: cb.x,
30988
+ y: cb.y,
30989
+ width: NODE_WIDTH,
30990
+ height: NODE_HEIGHT,
30991
+ collapsed: true,
30992
+ childCount: cb.childCount
30993
+ });
30994
+ }
30995
+ const M = 40;
30996
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
30997
+ const acc = (x, y) => {
30998
+ if (x < minX) minX = x;
30999
+ if (x > maxX) maxX = x;
31000
+ if (y < minY) minY = y;
31001
+ if (y > maxY) maxY = y;
31002
+ };
31003
+ for (const n2 of nodes) {
31004
+ acc(n2.x - n2.width / 2, n2.y - n2.height / 2);
31005
+ acc(n2.x + n2.width / 2, n2.y + n2.height / 2);
31006
+ }
31007
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
31008
+ for (const gr of groups) {
31009
+ acc(gr.x - gr.width / 2, gr.y - gr.height / 2);
31010
+ acc(gr.x + gr.width / 2, gr.y + gr.height / 2);
31011
+ }
31012
+ const TOL = 2;
31013
+ const sx = minX < M - TOL ? M - minX : 0;
31014
+ const sy = minY < M - TOL ? M - minY : 0;
31015
+ const shifted = sx !== 0 || sy !== 0;
31016
+ return {
31017
+ nodes: shifted ? nodes.map((n2) => ({ ...n2, x: n2.x + sx, y: n2.y + sy })) : nodes,
31018
+ edges: shifted ? edges.map((e) => ({
31019
+ ...e,
31020
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
31021
+ })) : edges,
31022
+ groups: shifted ? groups.map((gr) => ({ ...gr, x: gr.x + sx, y: gr.y + sy })) : groups,
31023
+ width: maxX + sx + M,
31024
+ height: maxY + sy + M
31025
+ };
31026
+ }
30269
31027
  function place(cfg) {
30270
31028
  const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
30271
31029
  const ord = (a) => r ? shuffle(a, r) : a.slice();
@@ -30404,6 +31162,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30404
31162
  height: gg.height ?? 600
30405
31163
  };
30406
31164
  }
31165
+ if (allPinned) return placePinned(pinned);
30407
31166
  const n = parsed.nodes.length;
30408
31167
  const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
30409
31168
  const REFINE_K = opts?.refineK ?? 6;
@@ -30426,17 +31185,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30426
31185
  seed: s
30427
31186
  });
30428
31187
  const badness = (lay, floor) => {
30429
- const x = countSplineCrossings(lay);
31188
+ const x = countSplineCrossings(lay, floor);
30430
31189
  if (x > floor) return Infinity;
30431
31190
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30432
31191
  };
30433
31192
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31193
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31194
+ let progressDone = 0;
31195
+ const step = async (phase) => {
31196
+ if (!onProgress) return;
31197
+ onProgress(++progressDone, progressTotal, phase);
31198
+ await tick();
31199
+ };
30434
31200
  const pool = [];
30435
31201
  for (const cfg of configs) {
30436
31202
  try {
30437
31203
  pool.push(place(cfg));
30438
31204
  } catch {
30439
31205
  }
31206
+ await step("Optimizing layout");
30440
31207
  }
30441
31208
  if (!pool.length)
30442
31209
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30445,9 +31212,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30445
31212
  layered = layeredCandidates(parsed, sizes);
30446
31213
  } catch {
30447
31214
  }
30448
- pool.sort(
30449
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30450
- );
31215
+ const fastKey = /* @__PURE__ */ new Map();
31216
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31217
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30451
31218
  const refineK = Math.min(REFINE_K, pool.length);
30452
31219
  let best = pool[0];
30453
31220
  let bestObj = Infinity;
@@ -30462,7 +31229,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30462
31229
  best = lay;
30463
31230
  }
30464
31231
  };
30465
- for (const lay of pool.slice(0, refineK)) consider(lay);
31232
+ for (const lay of pool.slice(0, refineK)) {
31233
+ consider(lay);
31234
+ await step("Refining layout");
31235
+ }
30466
31236
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30467
31237
  const extra = [];
30468
31238
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30478,9 +31248,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30478
31248
  } catch {
30479
31249
  }
30480
31250
  }
30481
- extra.sort(
30482
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30483
- );
31251
+ const extraKey = /* @__PURE__ */ new Map();
31252
+ for (const lay of extra)
31253
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31254
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30484
31255
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30485
31256
  }
30486
31257
  for (const lay of layered) {
@@ -30507,7 +31278,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30507
31278
  }
30508
31279
  return best;
30509
31280
  }
30510
- var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31281
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30511
31282
  var init_layout_search = __esm({
30512
31283
  "src/boxes-and-lines/layout-search.ts"() {
30513
31284
  "use strict";
@@ -30521,6 +31292,8 @@ var init_layout_search = __esm({
30521
31292
  ESCALATE_SEEDS = 18;
30522
31293
  ESCALATE_REFINE = 10;
30523
31294
  splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
31295
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31296
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30524
31297
  GROUP_LABEL_ZONE2 = 32;
30525
31298
  }
30526
31299
  });
@@ -30600,17 +31373,20 @@ function computeNodeSize(node, reserveValueRow) {
30600
31373
  }
30601
31374
  totalRenderedLines = Math.min(totalRenderedLines, MAX_DESC_LINES2);
30602
31375
  const descriptionHeight = totalRenderedLines * DESC_FONT_SIZE2 * DESC_LINE_HEIGHT2;
30603
- const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
31376
+ const totalHeight = labelHeight + SEPARATOR_GAP4 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
30604
31377
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
30605
31378
  }
30606
31379
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30607
31380
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30608
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31381
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30609
31382
  ...layoutOptions?.hideDescriptions !== void 0 && {
30610
31383
  hideDescriptions: layoutOptions.hideDescriptions
30611
31384
  },
30612
31385
  ...layoutOptions?.previousPositions !== void 0 && {
30613
31386
  previousPositions: layoutOptions.previousPositions
31387
+ },
31388
+ ...layoutOptions?.onProgress !== void 0 && {
31389
+ onProgress: layoutOptions.onProgress
30614
31390
  }
30615
31391
  });
30616
31392
  return attachNotes(
@@ -30729,7 +31505,7 @@ function applyParallelEdgeOffsets(layout) {
30729
31505
  }))
30730
31506
  };
30731
31507
  }
30732
- var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP5, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
31508
+ var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
30733
31509
  var init_layout5 = __esm({
30734
31510
  "src/boxes-and-lines/layout.ts"() {
30735
31511
  "use strict";
@@ -30745,13 +31521,13 @@ var init_layout5 = __esm({
30745
31521
  DESC_FONT_SIZE2 = 10;
30746
31522
  DESC_LINE_HEIGHT2 = 1.4;
30747
31523
  DESC_PADDING = 8;
30748
- SEPARATOR_GAP5 = 4;
31524
+ SEPARATOR_GAP4 = 4;
30749
31525
  MAX_DESC_LINES2 = 6;
30750
31526
  MAX_LABEL_LINES = 3;
30751
31527
  LABEL_LINE_HEIGHT = 1.3;
30752
31528
  LABEL_PAD = 12;
30753
31529
  VALUE_ROW_FONT = 11;
30754
- VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
31530
+ VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
30755
31531
  }
30756
31532
  });
30757
31533
 
@@ -31394,7 +32170,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31394
32170
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31395
32171
  const availWidth = containerWidth;
31396
32172
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31397
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32173
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32174
+ availWidth,
32175
+ layout.width,
32176
+ availHeight,
32177
+ layout.height
32178
+ );
31398
32179
  let renderLayout = layout;
31399
32180
  if (ctx.factor < 1) {
31400
32181
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31403,17 +32184,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31403
32184
  hiddenCounts.set(n.id, n.hiddenCount);
31404
32185
  }
31405
32186
  }
31406
- renderLayout = layoutMindmap(parsed, palette, {
32187
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31407
32188
  interactive: !isExport,
31408
32189
  ...hiddenCounts.size > 0 && { hiddenCounts },
31409
32190
  activeTagGroup: activeTagGroup ?? null,
31410
32191
  ...hideDescriptions !== void 0 && { hideDescriptions },
31411
- ctx
32192
+ ctx: c
31412
32193
  });
32194
+ renderLayout = relayout(ctx);
32195
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32196
+ const refit = Math.min(
32197
+ availWidth / renderLayout.width,
32198
+ availHeight / renderLayout.height
32199
+ );
32200
+ if (refit >= 0.999) break;
32201
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32202
+ renderLayout = relayout(ctx);
32203
+ }
31413
32204
  }
31414
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31415
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31416
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32205
+ const fitScale = isExport ? 1 : Math.min(
32206
+ 1,
32207
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32208
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32209
+ );
32210
+ const scaledWidth = renderLayout.width * fitScale;
32211
+ const scaledHeight = renderLayout.height * fitScale;
32212
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32213
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32214
+ const mainG = svg.append("g").attr(
32215
+ "transform",
32216
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32217
+ );
31417
32218
  if (ctx.isBelowFloor) {
31418
32219
  svg.attr("width", "100%");
31419
32220
  }
@@ -31512,11 +32313,11 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31512
32313
  });
31513
32314
  }
31514
32315
  for (const edge of renderLayout.edges) {
31515
- mainG.append("path").attr("class", "mindmap-edge").attr("d", edge.path).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", EDGE_STROKE_WIDTH6).attr("stroke-opacity", 0.5);
32316
+ mainG.append("path").attr("class", "mindmap-edge").attr("d", edge.path).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", EDGE_STROKE_WIDTH).attr("stroke-opacity", 0.5);
31516
32317
  }
31517
32318
  for (const node of renderLayout.nodes) {
31518
32319
  const isRoot = node.radius === 0 && renderLayout.nodes.indexOf(node) === 0;
31519
- const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH6;
32320
+ const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH;
31520
32321
  const effectiveColor = options?.colorByDepth ? depthColor(node.depth, palette) : node.color;
31521
32322
  const fill2 = nodeFill4(
31522
32323
  palette,
@@ -31599,7 +32400,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31599
32400
  const clipId = `collapse-clip-${node.id}`;
31600
32401
  const defs = mainG.append("defs");
31601
32402
  defs.append("clipPath").attr("id", clipId).append("rect").attr("x", node.x).attr("y", node.y).attr("width", node.width).attr("height", node.height).attr("rx", NODE_RADIUS).attr("ry", NODE_RADIUS);
31602
- nodeG.append("rect").attr("class", "collapse-bar").attr("x", node.x).attr("y", node.y + node.height - COLLAPSE_BAR_HEIGHT4).attr("width", node.width).attr("height", COLLAPSE_BAR_HEIGHT4).attr("fill", stroke2).attr("clip-path", `url(#${clipId})`);
32403
+ nodeG.append("rect").attr("class", "collapse-bar").attr("x", node.x).attr("y", node.y + node.height - COLLAPSE_BAR_HEIGHT).attr("width", node.width).attr("height", COLLAPSE_BAR_HEIGHT).attr("fill", stroke2).attr("clip-path", `url(#${clipId})`);
31603
32404
  }
31604
32405
  if (onClickItem) {
31605
32406
  nodeG.style("cursor", "pointer").on("click", (event) => {
@@ -31647,7 +32448,7 @@ function renderMindmapForExport(content, theme, palette) {
31647
32448
  return extractExportSvg(container, theme);
31648
32449
  });
31649
32450
  }
31650
- var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, NODE_STROKE_WIDTH6, EDGE_STROKE_WIDTH6, COLLAPSE_BAR_HEIGHT4, DEPTH_COLOR_KEYS;
32451
+ var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
31651
32452
  var init_renderer7 = __esm({
31652
32453
  "src/mindmap/renderer.ts"() {
31653
32454
  "use strict";
@@ -31665,6 +32466,7 @@ var init_renderer7 = __esm({
31665
32466
  init_legend_layout();
31666
32467
  init_title_constants();
31667
32468
  init_scaling();
32469
+ init_visual_conventions();
31668
32470
  DIAGRAM_PADDING7 = 20;
31669
32471
  TITLE_HEIGHT4 = 30;
31670
32472
  SINGLE_LABEL_HEIGHT2 = 28;
@@ -31672,9 +32474,6 @@ var init_renderer7 = __esm({
31672
32474
  DESC_LINE_HEIGHT4 = 14;
31673
32475
  NODE_RADIUS = 6;
31674
32476
  ROOT_STROKE_WIDTH = 2.5;
31675
- NODE_STROKE_WIDTH6 = 1.5;
31676
- EDGE_STROKE_WIDTH6 = 1.5;
31677
- COLLAPSE_BAR_HEIGHT4 = 6;
31678
32477
  DEPTH_COLOR_KEYS = [
31679
32478
  "red",
31680
32479
  "orange",
@@ -32902,10 +33701,10 @@ function computeC4NodeDimensions(el, options) {
32902
33701
  const metaEntries = collectCardMetadata(el.metadata);
32903
33702
  if (metaEntries.length > 0) {
32904
33703
  height2 += DIVIDER_GAP;
32905
- height2 += metaEntries.length * META_LINE_HEIGHT5;
33704
+ height2 += metaEntries.length * META_LINE_HEIGHT4;
32906
33705
  const maxMetaWidth = Math.max(
32907
33706
  ...metaEntries.map(
32908
- (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE5) + CARD_H_PAD3 * 2
33707
+ (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE4) + CARD_H_PAD3 * 2
32909
33708
  )
32910
33709
  );
32911
33710
  if (maxMetaWidth > width) width = Math.min(MAX_NODE_WIDTH, maxMetaWidth);
@@ -34182,7 +34981,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34182
34981
  height: totalHeight
34183
34982
  };
34184
34983
  }
34185
- var import_dagre5, gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_FONT_SIZE5, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
34984
+ var import_dagre5, gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
34186
34985
  var init_layout8 = __esm({
34187
34986
  "src/c4/layout.ts"() {
34188
34987
  "use strict";
@@ -34201,8 +35000,8 @@ var init_layout8 = __esm({
34201
35000
  DESC_FONT_SIZE4 = 11;
34202
35001
  CARD_V_PAD3 = 14;
34203
35002
  CARD_H_PAD3 = 20;
34204
- META_LINE_HEIGHT5 = 16;
34205
- META_FONT_SIZE5 = 11;
35003
+ META_LINE_HEIGHT4 = 16;
35004
+ META_FONT_SIZE4 = 11;
34206
35005
  MARGIN6 = 40;
34207
35006
  BOUNDARY_PAD = 40;
34208
35007
  GROUP_BOUNDARY_PAD = 24;
@@ -34323,7 +35122,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34323
35122
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34324
35123
  const pathD = lineGenerator4(edge.points);
34325
35124
  if (pathD) {
34326
- const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH7).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
35125
+ const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
34327
35126
  if (dashed) {
34328
35127
  pathEl.attr("stroke-dasharray", "6 3");
34329
35128
  }
@@ -34389,7 +35188,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34389
35188
  palette.textOnFillLight,
34390
35189
  palette.textOnFillDark
34391
35190
  );
34392
- nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4).attr("ry", CARD_RADIUS4).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35191
+ nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS).attr("ry", CARD_RADIUS).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34393
35192
  let yPos = -h / 2 + CARD_V_PAD4;
34394
35193
  const typeLabel = `\xAB${node.type}\xBB`;
34395
35194
  nodeG.append("text").attr("x", 0).attr("y", yPos + TYPE_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", TYPE_FONT_SIZE).attr("font-style", "italic").text(typeLabel);
@@ -34433,7 +35232,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34433
35232
  }
34434
35233
  if (node.drillable) {
34435
35234
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
34436
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4);
35235
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS);
34437
35236
  nodeG.append("rect").attr("x", -w / 2).attr("y", h / 2 - DRILL_BAR_HEIGHT).attr("width", w).attr("height", DRILL_BAR_HEIGHT).attr("fill", solid ? onFillText : stroke2).attr("clip-path", `url(#${clipId})`).attr("class", "c4-drill-bar");
34438
35237
  }
34439
35238
  }
@@ -34493,14 +35292,14 @@ function drawCylinderCard(nodeG, w, h, fill2, stroke2, dashed) {
34493
35292
  `A ${w / 2} ${ry} 0 0 1 ${-w / 2} ${h / 2 - ry}`,
34494
35293
  "Z"
34495
35294
  ].join(" ");
34496
- const el = nodeG.append("path").attr("d", path).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35295
+ const el = nodeG.append("path").attr("d", path).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34497
35296
  if (dashed) {
34498
35297
  el.attr("stroke-dasharray", "6 3");
34499
35298
  }
34500
- nodeG.append("ellipse").attr("cx", 0).attr("cy", -h / 2 + ry).attr("rx", w / 2).attr("ry", ry).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35299
+ nodeG.append("ellipse").attr("cx", 0).attr("cy", -h / 2 + ry).attr("rx", w / 2).attr("ry", ry).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34501
35300
  }
34502
35301
  function drawCardRect(nodeG, w, h, fill2, stroke2, dashed) {
34503
- const el = nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4).attr("ry", CARD_RADIUS4).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35302
+ const el = nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS).attr("ry", CARD_RADIUS).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34504
35303
  if (dashed) {
34505
35304
  el.attr("stroke-dasharray", "6 3");
34506
35305
  }
@@ -34520,7 +35319,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
34520
35319
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34521
35320
  const pathD = lineGenerator4(edge.points);
34522
35321
  if (pathD) {
34523
- const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH7).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
35322
+ const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
34524
35323
  if (dashed) {
34525
35324
  pathEl.attr("stroke-dasharray", "6 3");
34526
35325
  }
@@ -35015,13 +35814,13 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35015
35814
  nodeG.append("line").attr("x1", -w / 2 + CARD_H_PAD4 / 2).attr("y1", yPos).attr("x2", w / 2 - CARD_H_PAD4 / 2).attr("y2", yPos).attr("stroke", solid ? onFillText : stroke2).attr("stroke-width", 0.5).attr("stroke-opacity", 0.4);
35016
35815
  yPos += DIVIDER_GAP2;
35017
35816
  const maxKeyWidth = Math.max(
35018
- ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE6))
35817
+ ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE2))
35019
35818
  );
35020
35819
  const valueX = -w / 2 + CARD_H_PAD4 + maxKeyWidth;
35021
35820
  for (const entry of metaEntries) {
35022
- nodeG.append("text").attr("x", -w / 2 + CARD_H_PAD4).attr("y", yPos + META_FONT_SIZE6 / 2).attr("text-anchor", "start").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", META_FONT_SIZE6).text(`${entry.key}:`);
35023
- nodeG.append("text").attr("x", valueX).attr("y", yPos + META_FONT_SIZE6 / 2).attr("text-anchor", "start").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", META_FONT_SIZE6).text(entry.value);
35024
- yPos += META_LINE_HEIGHT6;
35821
+ nodeG.append("text").attr("x", -w / 2 + CARD_H_PAD4).attr("y", yPos + META_FONT_SIZE2 / 2).attr("text-anchor", "start").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", META_FONT_SIZE2).text(`${entry.key}:`);
35822
+ nodeG.append("text").attr("x", valueX).attr("y", yPos + META_FONT_SIZE2 / 2).attr("text-anchor", "start").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", META_FONT_SIZE2).text(entry.value);
35823
+ yPos += META_LINE_HEIGHT2;
35025
35824
  }
35026
35825
  }
35027
35826
  } else {
@@ -35048,7 +35847,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35048
35847
  }
35049
35848
  if (node.drillable) {
35050
35849
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
35051
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4);
35850
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS);
35052
35851
  nodeG.append("rect").attr("x", -w / 2).attr("y", h / 2 - DRILL_BAR_HEIGHT).attr("width", w).attr("height", DRILL_BAR_HEIGHT).attr("fill", solid ? onFillText : stroke2).attr("clip-path", `url(#${clipId})`).attr("class", "c4-drill-bar");
35053
35852
  }
35054
35853
  }
@@ -35177,7 +35976,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
35177
35976
  document.body.removeChild(el);
35178
35977
  }
35179
35978
  }
35180
- var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE6, TECH_FONT_SIZE, EDGE_STROKE_WIDTH7, NODE_STROKE_WIDTH7, CARD_RADIUS4, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, META_FONT_SIZE6, META_LINE_HEIGHT6, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
35979
+ var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE6, TECH_FONT_SIZE, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
35181
35980
  var init_renderer9 = __esm({
35182
35981
  "src/c4/renderer.ts"() {
35183
35982
  "use strict";
@@ -35193,6 +35992,7 @@ var init_renderer9 = __esm({
35193
35992
  init_legend_constants();
35194
35993
  init_legend_integration();
35195
35994
  init_title_constants();
35995
+ init_visual_conventions();
35196
35996
  DIAGRAM_PADDING8 = 20;
35197
35997
  MAX_SCALE5 = 3;
35198
35998
  TITLE_HEIGHT6 = 30;
@@ -35202,16 +36002,11 @@ var init_renderer9 = __esm({
35202
36002
  DESC_LINE_HEIGHT6 = 16;
35203
36003
  EDGE_LABEL_FONT_SIZE6 = 11;
35204
36004
  TECH_FONT_SIZE = 10;
35205
- EDGE_STROKE_WIDTH7 = 1.5;
35206
- NODE_STROKE_WIDTH7 = 1.5;
35207
- CARD_RADIUS4 = 6;
35208
36005
  CARD_H_PAD4 = 20;
35209
36006
  CARD_V_PAD4 = 14;
35210
36007
  TYPE_LABEL_HEIGHT2 = 18;
35211
36008
  DIVIDER_GAP2 = 6;
35212
36009
  NAME_HEIGHT2 = 20;
35213
- META_FONT_SIZE6 = 11;
35214
- META_LINE_HEIGHT6 = 16;
35215
36010
  BOUNDARY_LABEL_FONT_SIZE = 12;
35216
36011
  BOUNDARY_STROKE_WIDTH = 1.5;
35217
36012
  BOUNDARY_RADIUS = 8;
@@ -35615,7 +36410,7 @@ function nodeFill6(palette, isDark, shape, nodeColor2, isEndTerminal, colorOff,
35615
36410
  function nodeStroke6(palette, shape, nodeColor2, isEndTerminal, colorOff) {
35616
36411
  return nodeColor2 ?? shapeDefaultColor(shape, palette, isEndTerminal, colorOff);
35617
36412
  }
35618
- function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36413
+ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35619
36414
  const w = node.width;
35620
36415
  const h = node.height;
35621
36416
  const rx = h / 2;
@@ -35627,7 +36422,7 @@ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeS
35627
36422
  nodeStroke6(palette, node.shape, node.color, isEnd, colorOff)
35628
36423
  ).attr("stroke-width", sNodeStrokeWidth);
35629
36424
  }
35630
- function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36425
+ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35631
36426
  const w = node.width;
35632
36427
  const h = node.height;
35633
36428
  g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr(
@@ -35646,7 +36441,7 @@ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWid
35646
36441
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35647
36442
  ).attr("stroke-width", sNodeStrokeWidth);
35648
36443
  }
35649
- function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36444
+ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35650
36445
  const w = node.width / 2;
35651
36446
  const h = node.height / 2;
35652
36447
  const points = [`${0},${-h}`, `${w},${0}`, `${0},${h}`, `${-w},${0}`].join(
@@ -35668,7 +36463,7 @@ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35668
36463
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35669
36464
  ).attr("stroke-width", sNodeStrokeWidth);
35670
36465
  }
35671
- function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sIoSkew = IO_SKEW) {
36466
+ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sIoSkew = IO_SKEW) {
35672
36467
  const w = node.width / 2;
35673
36468
  const h = node.height / 2;
35674
36469
  const sk = sIoSkew;
@@ -35694,7 +36489,7 @@ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth =
35694
36489
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35695
36490
  ).attr("stroke-width", sNodeStrokeWidth);
35696
36491
  }
35697
- function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sSubroutineInset = SUBROUTINE_INSET) {
36492
+ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sSubroutineInset = SUBROUTINE_INSET) {
35698
36493
  const w = node.width;
35699
36494
  const h = node.height;
35700
36495
  const s = nodeStroke6(palette, node.shape, node.color, void 0, colorOff);
@@ -35712,7 +36507,7 @@ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStroke
35712
36507
  g.append("line").attr("x1", -w / 2 + sSubroutineInset).attr("y1", -h / 2).attr("x2", -w / 2 + sSubroutineInset).attr("y2", h / 2).attr("stroke", innerStroke).attr("stroke-width", sNodeStrokeWidth);
35713
36508
  g.append("line").attr("x1", w / 2 - sSubroutineInset).attr("y1", -h / 2).attr("x2", w / 2 - sSubroutineInset).attr("y2", h / 2).attr("stroke", innerStroke).attr("stroke-width", sNodeStrokeWidth);
35714
36509
  }
35715
- function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sDocWaveHeight = DOC_WAVE_HEIGHT) {
36510
+ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sDocWaveHeight = DOC_WAVE_HEIGHT) {
35716
36511
  const w = node.width;
35717
36512
  const h = node.height;
35718
36513
  const waveH = sDocWaveHeight;
@@ -35743,7 +36538,7 @@ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35743
36538
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35744
36539
  ).attr("stroke-width", sNodeStrokeWidth);
35745
36540
  }
35746
- function renderNodeShape(g, node, palette, isDark, endTerminalIds, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sIoSkew = IO_SKEW, sSubroutineInset = SUBROUTINE_INSET, sDocWaveHeight = DOC_WAVE_HEIGHT) {
36541
+ function renderNodeShape(g, node, palette, isDark, endTerminalIds, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sIoSkew = IO_SKEW, sSubroutineInset = SUBROUTINE_INSET, sDocWaveHeight = DOC_WAVE_HEIGHT) {
35747
36542
  switch (node.shape) {
35748
36543
  case "terminal":
35749
36544
  renderTerminal(
@@ -35830,8 +36625,8 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
35830
36625
  const sTitleY = ctx.structural(TITLE_Y);
35831
36626
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
35832
36627
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE7);
35833
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH8);
35834
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH8);
36628
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
36629
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
35835
36630
  const sArrowheadW = ctx.structural(ARROWHEAD_W3);
35836
36631
  const sArrowheadH = ctx.structural(ARROWHEAD_H3);
35837
36632
  const sIoSkew = ctx.structural(IO_SKEW);
@@ -36055,7 +36850,7 @@ function renderFlowchartForExport(content, theme, palette) {
36055
36850
  document.body.removeChild(container);
36056
36851
  }
36057
36852
  }
36058
- var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, EDGE_STROKE_WIDTH8, NODE_STROKE_WIDTH8, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
36853
+ var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
36059
36854
  var init_flowchart_renderer = __esm({
36060
36855
  "src/graph/flowchart-renderer.ts"() {
36061
36856
  "use strict";
@@ -36071,12 +36866,11 @@ var init_flowchart_renderer = __esm({
36071
36866
  init_scaling();
36072
36867
  init_text_measure();
36073
36868
  init_note_box();
36869
+ init_visual_conventions();
36074
36870
  DIAGRAM_PADDING9 = 20;
36075
36871
  MAX_SCALE6 = 3;
36076
36872
  NODE_FONT_SIZE2 = 13;
36077
36873
  EDGE_LABEL_FONT_SIZE7 = 11;
36078
- EDGE_STROKE_WIDTH8 = 1.5;
36079
- NODE_STROKE_WIDTH8 = 1.5;
36080
36874
  ARROWHEAD_W3 = 10;
36081
36875
  ARROWHEAD_H3 = 7;
36082
36876
  IO_SKEW = 15;
@@ -37060,7 +37854,7 @@ function hasRoles(node) {
37060
37854
  }
37061
37855
  function computeNodeWidth2(node, expanded, options) {
37062
37856
  const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
37063
- const badgeWidth = badgeVal > 0 ? measureText(`${badgeVal}x`, META_FONT_SIZE7) + 2 * CHAR_WIDTH_RATIO * NODE_FONT_SIZE3 : 0;
37857
+ const badgeWidth = badgeVal > 0 ? measureText(`${badgeVal}x`, META_FONT_SIZE5) + 2 * CHAR_WIDTH_RATIO * NODE_FONT_SIZE3 : 0;
37064
37858
  const labelWidth2 = measureText(node.label, NODE_FONT_SIZE3) + badgeWidth + PADDING_X3;
37065
37859
  const allKeys = [];
37066
37860
  if (node.computedRps > 0) allKeys.push("RPS");
@@ -37107,7 +37901,7 @@ function computeNodeWidth2(node, expanded, options) {
37107
37901
  }
37108
37902
  if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
37109
37903
  const keyColWidth = Math.max(
37110
- ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE7))
37904
+ ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE5))
37111
37905
  );
37112
37906
  let maxRowWidth = 0;
37113
37907
  if (node.computedRps > 0) {
@@ -37120,7 +37914,7 @@ function computeNodeWidth2(node, expanded, options) {
37120
37914
  const rpsVal = effectiveCap > 0 && !node.isEdge ? `${formatRpsShort(node.computedRps)} / ${formatRpsShort(effectiveCap)}` : formatRps(node.computedRps);
37121
37915
  maxRowWidth = Math.max(
37122
37916
  maxRowWidth,
37123
- keyColWidth + measureText(rpsVal, META_FONT_SIZE7)
37917
+ keyColWidth + measureText(rpsVal, META_FONT_SIZE5)
37124
37918
  );
37125
37919
  }
37126
37920
  if (expanded) {
@@ -37137,7 +37931,7 @@ function computeNodeWidth2(node, expanded, options) {
37137
37931
  const valStr = p.key === "max-rps" || p.key === "ratelimit-rps" ? formatRpsShort(numVal) : p.key === "latency-ms" || p.key === "cb-latency-threshold-ms" || p.key === "duration-ms" || p.key === "cold-start-ms" ? formatMs(numVal) : PCT_KEYS.includes(p.key) ? `${numVal}%` : String(p.value);
37138
37932
  maxRowWidth = Math.max(
37139
37933
  maxRowWidth,
37140
- keyColWidth + measureText(valStr, META_FONT_SIZE7)
37934
+ keyColWidth + measureText(valStr, META_FONT_SIZE5)
37141
37935
  );
37142
37936
  }
37143
37937
  }
@@ -37148,7 +37942,7 @@ function computeNodeWidth2(node, expanded, options) {
37148
37942
  if (ms > 0) {
37149
37943
  maxRowWidth = Math.max(
37150
37944
  maxRowWidth,
37151
- keyColWidth + measureText(formatMs(ms), META_FONT_SIZE7)
37945
+ keyColWidth + measureText(formatMs(ms), META_FONT_SIZE5)
37152
37946
  );
37153
37947
  }
37154
37948
  }
@@ -37159,37 +37953,37 @@ function computeNodeWidth2(node, expanded, options) {
37159
37953
  const combinedVal = `${formatMs(perc.p90)} / ${formatMs(threshold)}`;
37160
37954
  maxRowWidth = Math.max(
37161
37955
  maxRowWidth,
37162
- keyColWidth + measureText(combinedVal, META_FONT_SIZE7)
37956
+ keyColWidth + measureText(combinedVal, META_FONT_SIZE5)
37163
37957
  );
37164
37958
  }
37165
37959
  }
37166
37960
  if (node.computedUptime < 1) {
37167
37961
  maxRowWidth = Math.max(
37168
37962
  maxRowWidth,
37169
- keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE7)
37963
+ keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE5)
37170
37964
  );
37171
37965
  }
37172
37966
  if (node.computedAvailability < 1) {
37173
37967
  maxRowWidth = Math.max(
37174
37968
  maxRowWidth,
37175
- keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE7)
37969
+ keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE5)
37176
37970
  );
37177
37971
  }
37178
37972
  if (node.computedCbState === "open") {
37179
37973
  maxRowWidth = Math.max(
37180
37974
  maxRowWidth,
37181
- measureText("CB: OPEN", META_FONT_SIZE7) + 8
37975
+ measureText("CB: OPEN", META_FONT_SIZE5) + 8
37182
37976
  );
37183
37977
  }
37184
37978
  }
37185
- const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE7;
37979
+ const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE5;
37186
37980
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
37187
37981
  let descWidth = 0;
37188
37982
  for (const dl of descLines) {
37189
- const truncated = truncateText(dl, META_FONT_SIZE7, DESC_MAX_WIDTH2);
37983
+ const truncated = truncateText(dl, META_FONT_SIZE5, DESC_MAX_WIDTH2);
37190
37984
  descWidth = Math.max(
37191
37985
  descWidth,
37192
- measureText(truncated, META_FONT_SIZE7) + PADDING_X3
37986
+ measureText(truncated, META_FONT_SIZE5) + PADDING_X3
37193
37987
  );
37194
37988
  }
37195
37989
  return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
@@ -37199,17 +37993,17 @@ function computeNodeHeight2(node, expanded, options) {
37199
37993
  const computedCount = countComputedRows(node, expanded);
37200
37994
  const hasRps = node.computedRps > 0;
37201
37995
  const descLineCount = expanded && node.description && !node.isEdge ? node.description.length : 0;
37202
- const descH = descLineCount * META_LINE_HEIGHT7;
37996
+ const descH = descLineCount * META_LINE_HEIGHT5;
37203
37997
  if (propCount === 0 && computedCount === 0 && !hasRps)
37204
37998
  return NODE_HEADER_HEIGHT + descH + NODE_PAD_BOTTOM;
37205
37999
  let h = NODE_HEADER_HEIGHT + descH + NODE_SEPARATOR_GAP;
37206
38000
  const computedSectionCount = (hasRps ? 1 : 0) + computedCount;
37207
- h += computedSectionCount * META_LINE_HEIGHT7;
38001
+ h += computedSectionCount * META_LINE_HEIGHT5;
37208
38002
  if (computedSectionCount > 0 && propCount > 0) h += NODE_SEPARATOR_GAP;
37209
- h += propCount * META_LINE_HEIGHT7;
38003
+ h += propCount * META_LINE_HEIGHT5;
37210
38004
  if (hasRoles(node)) h += ROLE_DOT_ROW;
37211
38005
  h += NODE_PAD_BOTTOM;
37212
- if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT5;
38006
+ if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT3;
37213
38007
  return h;
37214
38008
  }
37215
38009
  function formatRps(rps) {
@@ -37544,7 +38338,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
37544
38338
  height: totalHeight
37545
38339
  };
37546
38340
  }
37547
- var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT7, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT5, NODE_FONT_SIZE3, META_FONT_SIZE7, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
38341
+ var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
37548
38342
  var init_layout10 = __esm({
37549
38343
  "src/infra/layout.ts"() {
37550
38344
  "use strict";
@@ -37552,13 +38346,13 @@ var init_layout10 = __esm({
37552
38346
  init_text_measure();
37553
38347
  MIN_NODE_WIDTH2 = 140;
37554
38348
  NODE_HEADER_HEIGHT = 28;
37555
- META_LINE_HEIGHT7 = 14;
38349
+ META_LINE_HEIGHT5 = 14;
37556
38350
  NODE_SEPARATOR_GAP = 4;
37557
38351
  NODE_PAD_BOTTOM = 10;
37558
38352
  ROLE_DOT_ROW = 12;
37559
- COLLAPSE_BAR_HEIGHT5 = 6;
38353
+ COLLAPSE_BAR_HEIGHT3 = 6;
37560
38354
  NODE_FONT_SIZE3 = 13;
37561
- META_FONT_SIZE7 = 10;
38355
+ META_FONT_SIZE5 = 10;
37562
38356
  EDGE_LABEL_FONT_SIZE8 = 11;
37563
38357
  PADDING_X3 = 24;
37564
38358
  GROUP_PADDING2 = 20;
@@ -37662,19 +38456,19 @@ __export(renderer_exports10, {
37662
38456
  function buildScaledConstants(ctx) {
37663
38457
  return {
37664
38458
  sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
37665
- sMetaFontSize: ctx.text(META_FONT_SIZE8),
37666
- sMetaLineHeight: ctx.structural(META_LINE_HEIGHT8),
38459
+ sMetaFontSize: ctx.text(META_FONT_SIZE6),
38460
+ sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
37667
38461
  sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE9),
37668
38462
  sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
37669
38463
  sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
37670
- sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH9),
37671
- sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH9),
38464
+ sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
38465
+ sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH),
37672
38466
  sOverloadStrokeWidth: ctx.structural(OVERLOAD_STROKE_WIDTH),
37673
38467
  sRoleDotRadius: ctx.structural(ROLE_DOT_RADIUS),
37674
38468
  sNodeSeparatorGap: ctx.structural(NODE_SEPARATOR_GAP2),
37675
38469
  sNodePadBottom: ctx.structural(NODE_PAD_BOTTOM2),
37676
- sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT6),
37677
- sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET2),
38470
+ sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT),
38471
+ sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET),
37678
38472
  sParticleR: ctx.structural(PARTICLE_R),
37679
38473
  sRejectParticleR: ctx.structural(PARTICLE_R),
37680
38474
  sRejectDropDistance: ctx.structural(REJECT_DROP_DISTANCE),
@@ -37998,7 +38792,7 @@ function isWarning(node) {
37998
38792
  return cap > 0 && node.computedRps / cap > 0.7;
37999
38793
  }
38000
38794
  function truncateDesc(text) {
38001
- return truncateText(text, META_FONT_SIZE8, DESC_MAX_WIDTH);
38795
+ return truncateText(text, META_FONT_SIZE6, DESC_MAX_WIDTH);
38002
38796
  }
38003
38797
  function sloLatencyColor(p90, slo) {
38004
38798
  const t = slo.latencyP90 ?? 0;
@@ -38477,7 +39271,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38477
39271
  if (!isNodeCollapsed) {
38478
39272
  const expanded = expandedNodeIds?.has(node.id) ?? false;
38479
39273
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
38480
- const descH = descLines.length * META_LINE_HEIGHT8;
39274
+ const descH = descLines.length * META_LINE_HEIGHT6;
38481
39275
  for (let di = 0; di < descLines.length; di++) {
38482
39276
  const rawLine = descLines[di];
38483
39277
  const processed = preprocessDescriptionLine(rawLine);
@@ -38485,7 +39279,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38485
39279
  const isTruncated = descTruncated !== processed;
38486
39280
  const textEl = g.append("text").attr("x", node.x).attr(
38487
39281
  "y",
38488
- y + NODE_HEADER_HEIGHT2 + di * META_LINE_HEIGHT8 + META_LINE_HEIGHT8 / 2 + sc.sMetaFontSize * 0.35
39282
+ y + NODE_HEADER_HEIGHT2 + di * META_LINE_HEIGHT6 + META_LINE_HEIGHT6 / 2 + sc.sMetaFontSize * 0.35
38489
39283
  ).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sMetaFontSize).attr("fill", textFill);
38490
39284
  renderInlineText(textEl, descTruncated, palette, sc.sMetaFontSize);
38491
39285
  if (isTruncated) textEl.append("title").text(rawLine);
@@ -39003,7 +39797,7 @@ function parseAndLayoutInfra(content) {
39003
39797
  const layout = layoutInfra(computed);
39004
39798
  return { parsed, computed, layout };
39005
39799
  }
39006
- var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE8, META_LINE_HEIGHT8, EDGE_LABEL_FONT_SIZE9, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, EDGE_STROKE_WIDTH9, NODE_STROKE_WIDTH9, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, COLLAPSE_BAR_HEIGHT6, COLLAPSE_BAR_INSET2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
39800
+ var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE9, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
39007
39801
  var init_renderer10 = __esm({
39008
39802
  "src/infra/renderer.ts"() {
39009
39803
  "use strict";
@@ -39024,21 +39818,18 @@ var init_renderer10 = __esm({
39024
39818
  init_legend_layout();
39025
39819
  init_title_constants();
39026
39820
  init_scaling();
39821
+ init_visual_conventions();
39027
39822
  NODE_FONT_SIZE4 = 13;
39028
- META_FONT_SIZE8 = 10;
39029
- META_LINE_HEIGHT8 = 14;
39823
+ META_FONT_SIZE6 = 10;
39824
+ META_LINE_HEIGHT6 = 14;
39030
39825
  EDGE_LABEL_FONT_SIZE9 = 11;
39031
39826
  GROUP_LABEL_FONT_SIZE2 = 14;
39032
39827
  NODE_BORDER_RADIUS = 8;
39033
- EDGE_STROKE_WIDTH9 = 1.5;
39034
- NODE_STROKE_WIDTH9 = 1.5;
39035
39828
  OVERLOAD_STROKE_WIDTH = 3;
39036
39829
  ROLE_DOT_RADIUS = 3;
39037
39830
  NODE_HEADER_HEIGHT2 = 28;
39038
39831
  NODE_SEPARATOR_GAP2 = 4;
39039
39832
  NODE_PAD_BOTTOM2 = 10;
39040
- COLLAPSE_BAR_HEIGHT6 = 6;
39041
- COLLAPSE_BAR_INSET2 = 0;
39042
39833
  LEGEND_FIXED_GAP3 = 16;
39043
39834
  SPEED_BADGE_H_PAD = 5;
39044
39835
  SPEED_BADGE_V_PAD = 3;
@@ -39077,7 +39868,7 @@ var init_renderer10 = __esm({
39077
39868
  partitions: "partitions"
39078
39869
  };
39079
39870
  DESC_MAX_CHARS = 120;
39080
- DESC_MAX_WIDTH = DESC_MAX_CHARS * CHAR_WIDTH_RATIO * META_FONT_SIZE8;
39871
+ DESC_MAX_WIDTH = DESC_MAX_CHARS * CHAR_WIDTH_RATIO * META_FONT_SIZE6;
39081
39872
  RPS_FORMAT_KEYS = /* @__PURE__ */ new Set(["max-rps", "ratelimit-rps"]);
39082
39873
  MS_FORMAT_KEYS = /* @__PURE__ */ new Set([
39083
39874
  "latency-ms",
@@ -40766,7 +41557,7 @@ __export(renderer_exports11, {
40766
41557
  renderPertForExport: () => renderPertForExport
40767
41558
  });
40768
41559
  function analysisBlockChrome(palette, isDark) {
40769
- const surfaceBg = isDark ? palette.surface : palette.bg;
41560
+ const surfaceBg = themeBaseBg(palette, isDark);
40770
41561
  return {
40771
41562
  fill: mix(palette.surface, palette.bg, 40),
40772
41563
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -40864,18 +41655,18 @@ function renderPert(container, resolved, layout, palette, isDark, options = {})
40864
41655
  const sLegendPillHeight = ctx.structural(LEGEND_PILL_HEIGHT);
40865
41656
  const sLegendBlockHeight = showTagLegend ? sLegendTopGap + sLegendPillHeight + sLegendBottomGap : 0;
40866
41657
  const sNodeRadius = ctx.structural(NODE_RADIUS2);
40867
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH10);
41658
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
40868
41659
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE5);
40869
41660
  const sNodeCellFontSize = ctx.text(NODE_CELL_FONT_SIZE2);
40870
41661
  const sNodeTopRowHeight = ctx.structural(NODE_TOP_ROW_HEIGHT);
40871
41662
  const sNodeBottomRowHeight = ctx.structural(NODE_BOTTOM_ROW_HEIGHT);
40872
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH10);
41663
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
40873
41664
  const sArrowheadW = ctx.structural(ARROWHEAD_W4);
40874
41665
  const sArrowheadH = ctx.structural(ARROWHEAD_H4);
40875
- const sContainerRadius = ctx.structural(CONTAINER_RADIUS3);
40876
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE4);
40877
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT3);
40878
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT7);
41666
+ const sContainerRadius = ctx.structural(CONTAINER_RADIUS);
41667
+ const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE);
41668
+ const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT);
41669
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
40879
41670
  const sPinIconW = ctx.structural(PIN_ICON_W);
40880
41671
  const sPinIconH = ctx.structural(PIN_ICON_H);
40881
41672
  const scaledWidth = layout.width + sDiagramPad * 2;
@@ -41442,7 +42233,7 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41442
42233
  palette.textOnFillDark
41443
42234
  );
41444
42235
  const sNR = sc.nodeRadius ?? NODE_RADIUS2;
41445
- const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT7;
42236
+ const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT;
41446
42237
  drawTextbookCard(g, {
41447
42238
  width: grp.width,
41448
42239
  height: grp.height,
@@ -41476,10 +42267,10 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41476
42267
  g.append("rect").attr("class", "pert-collapse-bar").attr("x", grp.x).attr("y", grp.y + grp.height - sCBH).attr("width", grp.width).attr("height", sCBH).attr("fill", cardBaseColor).attr("clip-path", `url(#${clipId})`);
41477
42268
  continue;
41478
42269
  }
41479
- const sCR = sc.containerRadius ?? CONTAINER_RADIUS3;
41480
- const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE4;
41481
- const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT3;
41482
- const sNSW = sc.nodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42270
+ const sCR = sc.containerRadius ?? CONTAINER_RADIUS;
42271
+ const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE;
42272
+ const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT;
42273
+ const sNSW = sc.nodeStrokeWidth ?? NODE_STROKE_WIDTH;
41483
42274
  g.append("rect").attr("x", grp.x).attr("y", grp.y).attr("width", grp.width).attr("height", grp.height).attr("rx", sCR).attr("ry", sCR).attr("fill", containerFill3).attr("stroke", containerStroke3).attr("stroke-opacity", 0.35).attr("stroke-width", sNSW);
41484
42275
  g.append("text").attr("x", grp.x + grp.width / 2).attr("y", grp.y + sCHH / 2 + sCLFS / 2 - 2).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("fill", palette.text).attr("font-size", sCLFS).attr("font-weight", "bold").text(label);
41485
42276
  }
@@ -41532,7 +42323,7 @@ function renderEdges2(root, resolved, layout, palette, collapsedSet, sc = {}) {
41532
42323
  }
41533
42324
  const path = lineGenerator6(e.points);
41534
42325
  if (!path) continue;
41535
- const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH10;
42326
+ const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH;
41536
42327
  const sELFS = sc.edgeLabelFontSize ?? 10;
41537
42328
  layer.append("path").attr("class", "pert-edge").attr("d", path).attr("fill", "none").attr("stroke", bandColor(band, palette, palette.textMuted)).attr("stroke-width", sESW).attr("marker-end", `url(#${bandArrow(band)})`).attr("data-source", e.source).attr("data-target", e.target).attr("data-critical", String(isCritical)).attr("data-critical-path", String(isCritical)).attr("data-criticality-band", band ?? "");
41538
42329
  const parsedEdge = edgeByKey.get(`${e.source}->${e.target}`);
@@ -41714,7 +42505,7 @@ function computeDurationEmphasis(activities) {
41714
42505
  function drawTextbookCard(g, a) {
41715
42506
  const { width: w, height: h, x, y } = a;
41716
42507
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41717
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42508
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41718
42509
  const sTRH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41719
42510
  const sBRH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41720
42511
  const sNFS = a.sNodeFontSize ?? NODE_FONT_SIZE5;
@@ -41813,7 +42604,7 @@ function drawTextbookCard(g, a) {
41813
42604
  function drawMilestonePill(g, a) {
41814
42605
  const { width: w, height: h, x, y } = a;
41815
42606
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41816
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42607
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41817
42608
  const topRowH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41818
42609
  const botRowH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41819
42610
  const sNCFS = a.sNodeCellFontSize ?? NODE_CELL_FONT_SIZE2;
@@ -41946,7 +42737,7 @@ function renderCaptionBlock(svg, bullets, args) {
41946
42737
  palette.textOnFillDark
41947
42738
  );
41948
42739
  const block = svg.append("g").attr("class", "pert-caption-block").attr("data-pert-caption", "");
41949
- block.append("rect").attr("class", "pert-caption-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH10);
42740
+ block.append("rect").attr("class", "pert-caption-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH);
41950
42741
  block.append("text").attr("class", "pert-caption-header").attr("x", x + width / 2).attr("y", y + CAPTION_BOX_PADDING_Y + CAPTION_FONT_SIZE).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", CAPTION_FONT_SIZE).attr("font-weight", "700").text("Summary");
41951
42742
  const textX = x + CAPTION_BOX_PADDING_X;
41952
42743
  const firstBaselineY = y + CAPTION_BOX_PADDING_Y + CAPTION_HEADER_BAND_HEIGHT + CAPTION_FONT_SIZE;
@@ -42063,7 +42854,7 @@ function renderTornadoBlock(svg, rows, args) {
42063
42854
  palette.textOnFillDark
42064
42855
  );
42065
42856
  const block = svg.append("g").attr("class", "pert-tornado-block").attr("data-pert-tornado", "");
42066
- block.append("rect").attr("class", "pert-tornado-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH10);
42857
+ block.append("rect").attr("class", "pert-tornado-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH);
42067
42858
  block.append("text").attr("class", "pert-tornado-header").attr("x", x + width / 2).attr("y", y + CAPTION_BOX_PADDING_Y + CAPTION_FONT_SIZE).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", CAPTION_FONT_SIZE).attr("font-weight", "700").text("Activity Risk");
42068
42859
  const fmt = (v) => {
42069
42860
  const r = Math.round(v * 100) / 100;
@@ -42219,7 +43010,7 @@ function renderScurveBlock(svg, data, args) {
42219
43010
  palette.textOnFillDark
42220
43011
  );
42221
43012
  const block = svg.append("g").attr("class", "pert-scurve-block").attr("data-pert-scurve", "");
42222
- block.append("rect").attr("class", "pert-scurve-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH10);
43013
+ block.append("rect").attr("class", "pert-scurve-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH);
42223
43014
  const hasTitle = typeof title === "string" && title.length > 0;
42224
43015
  if (hasTitle) {
42225
43016
  const titleText = title.replace(/\.$/, "");
@@ -42403,7 +43194,7 @@ function formatScurveDate(iso) {
42403
43194
  if (month < 0 || month > 11 || isNaN(day)) return iso;
42404
43195
  return `${SCURVE_MONTH_NAMES[month]} ${day}`;
42405
43196
  }
42406
- var d3Selection17, d3Shape9, DIAGRAM_PADDING11, NODE_FONT_SIZE5, NODE_CELL_FONT_SIZE2, NODE_RADIUS2, NODE_STROKE_WIDTH10, NODE_TOP_ROW_HEIGHT, NODE_BOTTOM_ROW_HEIGHT, EDGE_STROKE_WIDTH10, ARROWHEAD_W4, ARROWHEAD_H4, CONTAINER_RADIUS3, CONTAINER_LABEL_FONT_SIZE4, CONTAINER_HEADER_HEIGHT3, COLLAPSE_BAR_HEIGHT7, DURATION_FADE_OPACITY, PIN_ICON_W, PIN_ICON_H, LEGEND_PILL_HEIGHT, LEGEND_TOP_GAP, LEGEND_BOTTOM_GAP, FIELD_LEGEND_HEADER_BAND_HEIGHT, FIELD_LEGEND_CELL_VPAD, FIELD_LEGEND_LABEL_FONT_SIZE, FIELD_LEGEND_DESC_FONT_SIZE, FIELD_LEGEND_DESC_LINE_HEIGHT, FIELD_LEGEND_LABEL_DESC_GAP, FIELD_LEGEND_CELLS, lineGenerator6, FIELD_LEGEND_MIN_W, SUB_BULLET_INDENT, CAPTION_HEADER_BAND_HEIGHT, TORNADO_TOP_N, TORNADO_ROW_HEIGHT, TORNADO_NAME_COL_W, TORNADO_BAR_FONT_SIZE, TORNADO_BAR_HEIGHT, SUMMARY_MIN_W, SUMMARY_MAX_W, ANALYSIS_GAP, COL1_VSTACK_GAP, TORNADO_MIN_W, SCURVE_MIN_W, SCURVE_BOX_HEIGHT, SCURVE_PLOT_PADDING_X, SCURVE_PLOT_PADDING_RIGHT, SCURVE_PLOT_PADDING_BOTTOM, SCURVE_TICK_FONT_SIZE, SCURVE_PERCENTILE_RADIUS, DAYS_PER_UNIT, SCURVE_MONTH_NAMES, UNIT_TO_DAYS_LOCAL2;
43197
+ var d3Selection17, d3Shape9, DIAGRAM_PADDING11, NODE_FONT_SIZE5, NODE_CELL_FONT_SIZE2, NODE_RADIUS2, NODE_TOP_ROW_HEIGHT, NODE_BOTTOM_ROW_HEIGHT, ARROWHEAD_W4, ARROWHEAD_H4, DURATION_FADE_OPACITY, PIN_ICON_W, PIN_ICON_H, LEGEND_PILL_HEIGHT, LEGEND_TOP_GAP, LEGEND_BOTTOM_GAP, FIELD_LEGEND_HEADER_BAND_HEIGHT, FIELD_LEGEND_CELL_VPAD, FIELD_LEGEND_LABEL_FONT_SIZE, FIELD_LEGEND_DESC_FONT_SIZE, FIELD_LEGEND_DESC_LINE_HEIGHT, FIELD_LEGEND_LABEL_DESC_GAP, FIELD_LEGEND_CELLS, lineGenerator6, FIELD_LEGEND_MIN_W, SUB_BULLET_INDENT, CAPTION_HEADER_BAND_HEIGHT, TORNADO_TOP_N, TORNADO_ROW_HEIGHT, TORNADO_NAME_COL_W, TORNADO_BAR_FONT_SIZE, TORNADO_BAR_HEIGHT, SUMMARY_MIN_W, SUMMARY_MAX_W, ANALYSIS_GAP, COL1_VSTACK_GAP, TORNADO_MIN_W, SCURVE_MIN_W, SCURVE_BOX_HEIGHT, SCURVE_PLOT_PADDING_X, SCURVE_PLOT_PADDING_RIGHT, SCURVE_PLOT_PADDING_BOTTOM, SCURVE_TICK_FONT_SIZE, SCURVE_PERCENTILE_RADIUS, DAYS_PER_UNIT, SCURVE_MONTH_NAMES, UNIT_TO_DAYS_LOCAL2;
42407
43198
  var init_renderer11 = __esm({
42408
43199
  "src/pert/renderer.ts"() {
42409
43200
  "use strict";
@@ -42421,20 +43212,15 @@ var init_renderer11 = __esm({
42421
43212
  init_analyzer();
42422
43213
  init_layout11();
42423
43214
  init_internal();
43215
+ init_visual_conventions();
42424
43216
  DIAGRAM_PADDING11 = 20;
42425
43217
  NODE_FONT_SIZE5 = 13;
42426
43218
  NODE_CELL_FONT_SIZE2 = 11;
42427
43219
  NODE_RADIUS2 = 6;
42428
- NODE_STROKE_WIDTH10 = 1.5;
42429
43220
  NODE_TOP_ROW_HEIGHT = 26;
42430
43221
  NODE_BOTTOM_ROW_HEIGHT = 26;
42431
- EDGE_STROKE_WIDTH10 = 1.5;
42432
43222
  ARROWHEAD_W4 = 10;
42433
43223
  ARROWHEAD_H4 = 7;
42434
- CONTAINER_RADIUS3 = 8;
42435
- CONTAINER_LABEL_FONT_SIZE4 = 13;
42436
- CONTAINER_HEADER_HEIGHT3 = 28;
42437
- COLLAPSE_BAR_HEIGHT7 = 6;
42438
43224
  DURATION_FADE_OPACITY = 0.55;
42439
43225
  PIN_ICON_W = 13;
42440
43226
  PIN_ICON_H = 13;
@@ -45425,8 +46211,8 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45425
46211
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
45426
46212
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE10);
45427
46213
  const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
45428
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH11);
45429
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH11);
46214
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
46215
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
45430
46216
  const sArrowheadW = ctx.structural(ARROWHEAD_W5);
45431
46217
  const sArrowheadH = ctx.structural(ARROWHEAD_H5);
45432
46218
  const sPseudostateRadius = ctx.structural(PSEUDOSTATE_RADIUS);
@@ -45483,7 +46269,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45483
46269
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45484
46270
  const gw = group.width + sGroupExtraPadding * 2;
45485
46271
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45486
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46272
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45487
46273
  const strokeColor = group.color ?? palette.textMuted;
45488
46274
  const groupWrapper = contentG.append("g").attr("class", "st-group-wrapper").attr("data-line-number", String(group.lineNumber)).attr("data-group-id", group.id).attr("data-group-toggle", group.id).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", `Collapse group ${group.label}`).style("cursor", "pointer");
45489
46275
  groupWrapper.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "st-group");
@@ -45703,7 +46489,7 @@ function renderStateForExport(content, theme, palette) {
45703
46489
  document.body.removeChild(container);
45704
46490
  }
45705
46491
  }
45706
- var d3Selection19, DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE3, EDGE_STROKE_WIDTH11, NODE_STROKE_WIDTH11, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
46492
+ var d3Selection19, DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE3, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
45707
46493
  var init_state_renderer = __esm({
45708
46494
  "src/graph/state-renderer.ts"() {
45709
46495
  "use strict";
@@ -45719,13 +46505,12 @@ var init_state_renderer = __esm({
45719
46505
  init_scaling();
45720
46506
  init_text_measure();
45721
46507
  init_note_box();
46508
+ init_visual_conventions();
45722
46509
  DIAGRAM_PADDING12 = 20;
45723
46510
  MAX_SCALE7 = 3;
45724
46511
  NODE_FONT_SIZE6 = 13;
45725
46512
  EDGE_LABEL_FONT_SIZE10 = 11;
45726
46513
  GROUP_LABEL_FONT_SIZE3 = 11;
45727
- EDGE_STROKE_WIDTH11 = 1.5;
45728
- NODE_STROKE_WIDTH11 = 1.5;
45729
46514
  ARROWHEAD_W5 = 10;
45730
46515
  ARROWHEAD_H5 = 7;
45731
46516
  PSEUDOSTATE_RADIUS = 10;
@@ -47438,14 +48223,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47438
48223
  const panelY = PADDING2;
47439
48224
  const textX = panelX + CARD_PADDING_X3;
47440
48225
  const clipId = "persona-clip";
47441
- defs.append("clipPath").attr("id", clipId).append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5);
48226
+ defs.append("clipPath").attr("id", clipId).append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS);
47442
48227
  const personaFill = shapeFill(palette, personaColor, isDark, { solid });
47443
48228
  const onPersonaText = contrastText(
47444
48229
  personaFill,
47445
48230
  palette.textOnFillLight,
47446
48231
  palette.textOnFillDark
47447
48232
  );
47448
- personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5).attr("fill", personaFill);
48233
+ personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", personaFill);
47449
48234
  if (descLines.length > 0) {
47450
48235
  personaG.append("line").attr("x1", panelX + 1).attr("x2", panelX + panelWidth - silhouetteZone).attr("y1", panelY + titleRowH).attr("y2", panelY + titleRowH).attr("stroke", solid ? onPersonaText : personaColor).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
47451
48236
  }
@@ -47453,7 +48238,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47453
48238
  const silY = panelY + panelHeight / 2 - 6;
47454
48239
  const silClip = personaG.append("g").attr("clip-path", `url(#${clipId})`);
47455
48240
  renderPersonaSilhouette(silClip, silX, silY, personaColor, palette, 1.2);
47456
- personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5).attr("fill", "none").attr("stroke", personaColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48241
+ personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", "none").attr("stroke", personaColor).attr("stroke-width", CARD_STROKE_WIDTH2);
47457
48242
  personaG.append("text").attr("x", textX).attr("y", panelY + CARD_PADDING_Y3 + FONT_SIZE_STEP).attr("font-size", FONT_SIZE_STEP).attr("font-weight", "500").attr("fill", onPersonaText).text(parsed.persona.name);
47458
48243
  for (let li = 0; li < descLines.length; li++) {
47459
48244
  const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
@@ -47693,7 +48478,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47693
48478
  { solid }
47694
48479
  );
47695
48480
  const rowStroke = stepColor ?? palette.textMuted;
47696
- itemG.append("rect").attr("x", listX).attr("y", itemY).attr("width", cardW).attr("height", COLLAPSED_CARD_H).attr("rx", CARD_RADIUS5).attr("fill", rowFill).attr("stroke", rowStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
48481
+ itemG.append("rect").attr("x", listX).attr("y", itemY).attr("width", cardW).attr("height", COLLAPSED_CARD_H).attr("rx", CARD_RADIUS).attr("fill", rowFill).attr("stroke", rowStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
47697
48482
  const faceCx = listX + CARD_PADDING_X3 + COLLAPSED_FACE_R;
47698
48483
  const faceCy = itemY + COLLAPSED_CARD_H / 2;
47699
48484
  if (step.score !== void 0) {
@@ -47856,7 +48641,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47856
48641
  const scoreColor = scoreToColor(score, palette);
47857
48642
  const tintedBg = mix(scoreColor, palette.surface, 20);
47858
48643
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
47859
- g.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", CARD_RADIUS5).attr("fill", tintedBg).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48644
+ g.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", CARD_RADIUS).attr("fill", tintedBg).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
47860
48645
  g.append("line").attr("x1", fcx).attr("y1", by + bh).attr("x2", fcx).attr("y2", fcy - FACE_RADIUS - 1).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
47861
48646
  const centerX = bx + bw / 2;
47862
48647
  for (let i = 0; i < lines.length; i++) {
@@ -48013,7 +48798,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
48013
48798
  palette.textOnFillLight,
48014
48799
  palette.textOnFillDark
48015
48800
  );
48016
- stepG.append("rect").attr("x", cx).attr("y", cy).attr("width", sl.width).attr("height", sl.height).attr("rx", CARD_RADIUS5).attr("fill", cardFill).attr("stroke", cardStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
48801
+ stepG.append("rect").attr("x", cx).attr("y", cy).attr("width", sl.width).attr("height", sl.height).attr("rx", CARD_RADIUS).attr("fill", cardFill).attr("stroke", cardStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
48017
48802
  const titleMaxW = sl.width - CARD_PADDING_X3 * 2;
48018
48803
  const titleLines = wrapTextToWidth(sl.step.title, FONT_SIZE_STEP, titleMaxW);
48019
48804
  for (let i = 0; i < titleLines.length; i++) {
@@ -48072,7 +48857,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
48072
48857
  const stripFill = shapeFill(palette, stripColor, isDark, {
48073
48858
  ...solid !== void 0 && { solid }
48074
48859
  });
48075
- stepG.append("rect").attr("x", cx).attr("y", stripY).attr("width", sl.width).attr("height", TAG_STRIP_HEIGHT).attr("rx", CARD_RADIUS5).attr("fill", stripFill).attr("stroke", stripColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48860
+ stepG.append("rect").attr("x", cx).attr("y", stripY).attr("width", sl.width).attr("height", TAG_STRIP_HEIGHT).attr("rx", CARD_RADIUS).attr("fill", stripFill).attr("stroke", stripColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48076
48861
  const stripTextColor = contrastText(
48077
48862
  stripFill,
48078
48863
  palette.textOnFillLight,
@@ -48188,7 +48973,7 @@ function renderJourneyMapForExport(content, theme, palette) {
48188
48973
  }
48189
48974
  return svgEl.outerHTML;
48190
48975
  }
48191
- var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_RADIUS5, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_HEIGHT3, CARD_STROKE_WIDTH2, CARD_META_LINE_HEIGHT3, CARD_GAP_INTERNAL, COLUMN_RADIUS2, COLUMN_HEADER_HEIGHT2, COLUMN_PADDING3, FONT_SIZE_TITLE, FONT_SIZE_PHASE, FONT_SIZE_STEP, FONT_SIZE_META, GRID_LINE_OPACITY, CURVE_STROKE_WIDTH, FACE_RADIUS, DIM_HOVER, TITLE_LINE_HEIGHT, EMOTION_LABEL_MAX_WIDTH, EMOTION_LABEL_FONT_SIZE, ICON_THUMBS_DOWN, ICON_THUMBS_UP, ICON_THOUGHT;
48976
+ var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_HEIGHT3, CARD_STROKE_WIDTH2, CARD_META_LINE_HEIGHT3, CARD_GAP_INTERNAL, COLUMN_RADIUS2, COLUMN_HEADER_HEIGHT2, COLUMN_PADDING3, FONT_SIZE_TITLE, FONT_SIZE_PHASE, FONT_SIZE_STEP, FONT_SIZE_META, GRID_LINE_OPACITY, CURVE_STROKE_WIDTH, FACE_RADIUS, DIM_HOVER, TITLE_LINE_HEIGHT, EMOTION_LABEL_MAX_WIDTH, EMOTION_LABEL_FONT_SIZE, ICON_THUMBS_DOWN, ICON_THUMBS_UP, ICON_THOUGHT;
48192
48977
  var init_renderer14 = __esm({
48193
48978
  "src/journey-map/renderer.ts"() {
48194
48979
  "use strict";
@@ -48203,9 +48988,9 @@ var init_renderer14 = __esm({
48203
48988
  init_tag_groups();
48204
48989
  init_scaling();
48205
48990
  init_text_measure();
48991
+ init_visual_conventions();
48206
48992
  DIAGRAM_PADDING13 = 20;
48207
48993
  PADDING2 = DIAGRAM_PADDING13;
48208
- CARD_RADIUS5 = 6;
48209
48994
  CARD_PADDING_X3 = 10;
48210
48995
  CARD_PADDING_Y3 = 6;
48211
48996
  CARD_HEADER_HEIGHT3 = 24;
@@ -48282,7 +49067,7 @@ function computeCycleLayout(parsed, options) {
48282
49067
  const hasDesc = !hideDescriptions && node.description.length > 0;
48283
49068
  const labelWidth2 = Math.max(
48284
49069
  MIN_NODE_WIDTH4,
48285
- measureText(node.label, LABEL_FONT_SIZE5) + NODE_PAD_X * 2
49070
+ measureText(node.label, LABEL_FONT_SIZE4) + NODE_PAD_X * 2
48286
49071
  );
48287
49072
  if (circleNodes) {
48288
49073
  return computeCircleNodeDims(node, hasDesc);
@@ -48499,7 +49284,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48499
49284
  let bestScore = Infinity;
48500
49285
  for (let w = minW; w <= MAX_NODE_WIDTH3; w += DESC_WIDTH_STEP) {
48501
49286
  const wrapped2 = wrapDescForWidth(description, w);
48502
- const h = HEADER_HEIGHT5 + wrapped2.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y;
49287
+ const h = HEADER_HEIGHT4 + wrapped2.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y;
48503
49288
  const ratio = w / h;
48504
49289
  const score = Math.abs(Math.log(ratio / DESC_TARGET_RATIO));
48505
49290
  if (score < bestScore) {
@@ -48511,7 +49296,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48511
49296
  const wrapped = wrapDescForWidth(description, minW);
48512
49297
  return {
48513
49298
  width: minW,
48514
- height: HEADER_HEIGHT5 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
49299
+ height: HEADER_HEIGHT4 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
48515
49300
  wrappedDesc: wrapped
48516
49301
  };
48517
49302
  }
@@ -48524,7 +49309,7 @@ function wrapDescForWidth(description, nodeWidth) {
48524
49309
  );
48525
49310
  }
48526
49311
  function renderedDescNodeHeight(numLines, scale) {
48527
- const headerH = HEADER_HEIGHT5 * scale;
49312
+ const headerH = HEADER_HEIGHT4 * scale;
48528
49313
  const descFont = Math.max(
48529
49314
  RENDERER_DESC_FONT_MIN,
48530
49315
  Math.round(RENDERER_DESC_FONT * scale)
@@ -48859,7 +49644,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
48859
49644
  const midAngle = srcExitAngle + dir * visibleSweep / 2;
48860
49645
  return { path, midAngle };
48861
49646
  }
48862
- var MIN_ARC_ANGLE, LABEL_FONT_SIZE5, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT5, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
49647
+ var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT4, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
48863
49648
  var init_layout14 = __esm({
48864
49649
  "src/cycle/layout.ts"() {
48865
49650
  "use strict";
@@ -48867,7 +49652,7 @@ var init_layout14 = __esm({
48867
49652
  init_wrapped_desc();
48868
49653
  init_text_measure();
48869
49654
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
48870
- LABEL_FONT_SIZE5 = 13;
49655
+ LABEL_FONT_SIZE4 = 13;
48871
49656
  CIRCLE_LABEL_FONT_SIZE = 16;
48872
49657
  DESC_FONT_SIZE6 = 11;
48873
49658
  EDGE_LABEL_FONT_SIZE11 = 11;
@@ -48877,7 +49662,7 @@ var init_layout14 = __esm({
48877
49662
  DESC_WIDTH_STEP = 20;
48878
49663
  DESC_TARGET_RATIO = 1.6;
48879
49664
  PLAIN_NODE_HEIGHT = 50;
48880
- HEADER_HEIGHT5 = 36;
49665
+ HEADER_HEIGHT4 = 36;
48881
49666
  DESC_LINE_HEIGHT7 = 16;
48882
49667
  DESC_PAD_Y = 14;
48883
49668
  NODE_PAD_X = 20;
@@ -49457,6 +50242,16 @@ __export(resolver_exports, {
49457
50242
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49458
50243
  resolveMap: () => resolveMap
49459
50244
  });
50245
+ function containerOvershoot(span, usOriented) {
50246
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50247
+ return Math.max(
50248
+ CONTAINER_OVERSHOOT_MIN,
50249
+ Math.min(
50250
+ CONTAINER_OVERSHOOT_MAX,
50251
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50252
+ )
50253
+ );
50254
+ }
49460
50255
  function bboxArea(b) {
49461
50256
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49462
50257
  }
@@ -49953,7 +50748,7 @@ function resolveMap(parsed, data) {
49953
50748
  const containerUnion = unionExtent(containerBoxes, points);
49954
50749
  if (containerUnion)
49955
50750
  extent3 = pad(
49956
- clampContainerToCluster(containerUnion, points),
50751
+ clampContainerToCluster(containerUnion, points, usOriented),
49957
50752
  PAD_FRACTION
49958
50753
  );
49959
50754
  }
@@ -50037,17 +50832,21 @@ function mostCommonCountry(regions, poiCountries) {
50037
50832
  }
50038
50833
  return best;
50039
50834
  }
50040
- function clampContainerToCluster(container, points) {
50835
+ function clampContainerToCluster(container, points, usOriented) {
50041
50836
  const poi = unionExtent([], points);
50042
50837
  if (!poi) return container;
50043
50838
  let [[west, south], [east, north]] = container;
50044
50839
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50045
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50046
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50047
- if (east <= 180 && pEast <= 180) {
50048
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50049
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50050
- }
50840
+ const over = containerOvershoot(
50841
+ Math.max(pEast - pWest, pNorth - pSouth),
50842
+ usOriented
50843
+ );
50844
+ south = Math.max(south, pSouth - over);
50845
+ north = Math.min(north, pNorth + over);
50846
+ const wOver = pWest - over;
50847
+ const eOver = pEast + over;
50848
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
50849
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
50051
50850
  return [
50052
50851
  [west, south],
50053
50852
  [east, north]
@@ -50065,7 +50864,7 @@ function firstError(diags) {
50065
50864
  const e = diags.find((d) => d.severity === "error");
50066
50865
  return e ? formatDgmoError(e) : null;
50067
50866
  }
50068
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_DEG, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50867
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_MAX, CONTAINER_OVERSHOOT_MIN, CONTAINER_OVERSHOOT_DECAY, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50069
50868
  var init_resolver2 = __esm({
50070
50869
  "src/map/resolver.ts"() {
50071
50870
  "use strict";
@@ -50078,7 +50877,9 @@ var init_resolver2 = __esm({
50078
50877
  WORLD_LAT_SOUTH = -58;
50079
50878
  WORLD_LAT_NORTH = 78;
50080
50879
  POI_ZOOM_FLOOR_DEG = 7;
50081
- CONTAINER_OVERSHOOT_DEG = 8;
50880
+ CONTAINER_OVERSHOOT_MAX = 8;
50881
+ CONTAINER_OVERSHOOT_MIN = 3;
50882
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50082
50883
  US_NATIONAL_LON_SPAN = 48;
50083
50884
  CONUS_BBOX = [
50084
50885
  [-125, 25],
@@ -50244,12 +51045,12 @@ function tierBand(maxSpanDeg) {
50244
51045
  }
50245
51046
  function labelBudget(width, height, band) {
50246
51047
  const bandCap = {
50247
- world: 7,
50248
- continental: 6,
50249
- regional: 5,
50250
- local: 4
51048
+ world: 10,
51049
+ continental: 9,
51050
+ regional: 7,
51051
+ local: 6
50251
51052
  };
50252
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51053
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50253
51054
  return Math.max(0, Math.min(area2, bandCap[band]));
50254
51055
  }
50255
51056
  function waterEligible(tier, kind, band) {
@@ -50303,6 +51104,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50303
51104
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50304
51105
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50305
51106
  }
51107
+ function rectDist2(px, py, x0, y0, x1, y1) {
51108
+ const dx = Math.max(x0 - px, 0, px - x1);
51109
+ const dy = Math.max(y0 - py, 0, py - y1);
51110
+ return dx * dx + dy * dy;
51111
+ }
50306
51112
  function rectFits(r, width, height) {
50307
51113
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50308
51114
  }
@@ -50321,6 +51127,7 @@ function placeContextLabels(args) {
50321
51127
  palette,
50322
51128
  project,
50323
51129
  collides,
51130
+ contentPoints,
50324
51131
  overLand
50325
51132
  } = args;
50326
51133
  void projection;
@@ -50391,8 +51198,17 @@ function placeContextLabels(args) {
50391
51198
  const [x0, y0, x1, y1] = c.bbox;
50392
51199
  const w = x1 - x0;
50393
51200
  const h = y1 - y0;
50394
- return { c, w, h, area: w * h };
50395
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51201
+ let dist = Infinity;
51202
+ if (contentPoints?.length) {
51203
+ for (const p of contentPoints) {
51204
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51205
+ if (d < dist) dist = d;
51206
+ }
51207
+ }
51208
+ return { c, w, h, area: w * h, dist };
51209
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51210
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51211
+ );
50396
51212
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50397
51213
  let ci = 0;
50398
51214
  for (const r of ranked) {
@@ -50409,7 +51225,7 @@ function placeContextLabels(args) {
50409
51225
  );
50410
51226
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50411
51227
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50412
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51228
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50413
51229
  const text = c.name;
50414
51230
  const tw = labelWidth(text, 0, fontSize);
50415
51231
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50422,6 +51238,9 @@ function placeContextLabels(args) {
50422
51238
  letterSpacing: 0,
50423
51239
  color,
50424
51240
  fontSize,
51241
+ // Multi-position dodging: carry the ordered interior positions through to the
51242
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51243
+ ...c.positions ? { positions: c.positions } : {},
50425
51244
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50426
51245
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50427
51246
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50436,39 +51255,53 @@ function placeContextLabels(args) {
50436
51255
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50437
51256
  const waterCap = budget - Math.min(2, countryCount);
50438
51257
  let waterPlaced = 0;
50439
- for (const cand of candidates) {
50440
- if (placed.length >= budget) break;
50441
- if (cand.italic && waterPlaced >= waterCap) continue;
51258
+ const gateAt = (cx, cy, cand) => {
50442
51259
  const rect = rectAround(
50443
- cand.cx,
50444
- cand.cy,
51260
+ cx,
51261
+ cy,
50445
51262
  cand.lines,
50446
51263
  cand.letterSpacing,
50447
51264
  cand.fontSize
50448
51265
  );
50449
- if (!rectFits(rect, width, height)) continue;
51266
+ if (!rectFits(rect, width, height)) return null;
50450
51267
  if (cand.italic && overLand) {
50451
51268
  const inset = 2;
50452
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51269
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50453
51270
  const touchesLand = cand.lines.some((line11, li) => {
50454
51271
  const lw = labelWidth(line11, cand.letterSpacing);
50455
- const x0 = cand.cx - lw / 2 + inset;
50456
- const x1 = cand.cx + lw / 2 - inset;
50457
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51272
+ const x0 = cx - lw / 2 + inset;
51273
+ const x1 = cx + lw / 2 - inset;
51274
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
50458
51275
  const base = top + li * LINE_HEIGHT;
50459
51276
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50460
51277
  (y) => xs.some((x) => overLand(x, y))
50461
51278
  );
50462
51279
  });
50463
- if (touchesLand) continue;
51280
+ if (touchesLand) return null;
50464
51281
  }
50465
- if (collides(rect)) continue;
50466
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50467
- placedRects.push(rect);
51282
+ if (collides(rect)) return null;
51283
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51284
+ return null;
51285
+ return rect;
51286
+ };
51287
+ for (const cand of candidates) {
51288
+ if (placed.length >= budget) break;
51289
+ if (cand.italic && waterPlaced >= waterCap) continue;
51290
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51291
+ let chosen = null;
51292
+ for (const [px, py] of positions) {
51293
+ const rect = gateAt(px, py, cand);
51294
+ if (rect) {
51295
+ chosen = { x: px, y: py, rect };
51296
+ break;
51297
+ }
51298
+ }
51299
+ if (!chosen) continue;
51300
+ placedRects.push(chosen.rect);
50468
51301
  if (cand.italic) waterPlaced++;
50469
51302
  placed.push({
50470
- x: cand.cx,
50471
- y: cand.cy,
51303
+ x: chosen.x,
51304
+ y: chosen.y,
50472
51305
  text: cand.text,
50473
51306
  anchor: "middle",
50474
51307
  color: cand.color,
@@ -50486,7 +51319,7 @@ function placeContextLabels(args) {
50486
51319
  }
50487
51320
  return placed;
50488
51321
  }
50489
- var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, KIND_ORDER;
51322
+ var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, MAX_COUNTRY_POSITIONS, COUNTRY_POS_GRID, COUNTRY_POS_TOPN_MARGIN, KIND_ORDER;
50490
51323
  var init_context_labels = __esm({
50491
51324
  "src/map/context-labels.ts"() {
50492
51325
  "use strict";
@@ -50504,6 +51337,9 @@ var init_context_labels = __esm({
50504
51337
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50505
51338
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50506
51339
  COUNTRY_FADE_MAX = 45;
51340
+ MAX_COUNTRY_POSITIONS = 4;
51341
+ COUNTRY_POS_GRID = 5;
51342
+ COUNTRY_POS_TOPN_MARGIN = 3;
50507
51343
  KIND_ORDER = {
50508
51344
  ocean: 0,
50509
51345
  sea: 1,
@@ -50586,6 +51422,84 @@ function decodeLayer(topo) {
50586
51422
  decodeCache.set(topo, out);
50587
51423
  return out;
50588
51424
  }
51425
+ function countryLabelPositions(args) {
51426
+ const { geometry, bounds, project, width, height, curated } = args;
51427
+ const w0 = bounds[0][0];
51428
+ const s0 = bounds[0][1];
51429
+ const e0 = bounds[1][0];
51430
+ const n0 = bounds[1][1];
51431
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51432
+ return mkCurated(curated, project);
51433
+ }
51434
+ const N = COUNTRY_POS_GRID;
51435
+ const onLand = [];
51436
+ const kept = [];
51437
+ for (let i = 0; i < N; i++) {
51438
+ onLand[i] = [];
51439
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51440
+ for (let j = 0; j < N; j++) {
51441
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51442
+ const land = pointInGeometry(geometry, lon, lat);
51443
+ onLand[i][j] = land;
51444
+ if (!land) continue;
51445
+ const p = project(lon, lat);
51446
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51447
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51448
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51449
+ }
51450
+ }
51451
+ if (!kept.length) return mkCurated(curated, project);
51452
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51453
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51454
+ const interiorness = (c) => {
51455
+ let n = 0;
51456
+ for (let di = -1; di <= 1; di++)
51457
+ for (let dj = -1; dj <= 1; dj++) {
51458
+ if (di === 0 && dj === 0) continue;
51459
+ const ni = c.i + di;
51460
+ const nj = c.j + dj;
51461
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51462
+ }
51463
+ return n;
51464
+ };
51465
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51466
+ const pool = [...kept];
51467
+ pool.sort((a, b) => {
51468
+ const d = interiorness(b) - interiorness(a);
51469
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51470
+ });
51471
+ const ordered = [pool.shift()];
51472
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51473
+ let bestIdx = 0;
51474
+ let bestMin = -1;
51475
+ for (let k = 0; k < pool.length; k++) {
51476
+ const c = pool[k];
51477
+ let minD = Infinity;
51478
+ for (const o of ordered) {
51479
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51480
+ if (d < minD) minD = d;
51481
+ }
51482
+ if (minD > bestMin) {
51483
+ bestMin = minD;
51484
+ bestIdx = k;
51485
+ }
51486
+ }
51487
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51488
+ }
51489
+ const grid = ordered.map((c) => ({
51490
+ lonLat: [c.lon, c.lat],
51491
+ screen: [c.sx, c.sy]
51492
+ }));
51493
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51494
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51495
+ return out;
51496
+ }
51497
+ function mkCurated(curated, project) {
51498
+ if (!curated) return [];
51499
+ const p = project(curated[0], curated[1]);
51500
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51501
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51502
+ }
50589
51503
  function projectionFor(family, extent3) {
50590
51504
  switch (family) {
50591
51505
  case "albers-usa":
@@ -51413,6 +52327,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51413
52327
  if (p[1] < minY) minY = p[1];
51414
52328
  if (p[1] > maxY) maxY = p[1];
51415
52329
  }
52330
+ r.bbox = [minX, minY, maxX, maxY];
52331
+ r.rings = rings;
51416
52332
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51417
52333
  });
51418
52334
  const fillAt = (x, y) => {
@@ -52603,6 +53519,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52603
53519
  for (const box of insets)
52604
53520
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52605
53521
  const countryCandidates = [];
53522
+ const rawCountries = [];
52606
53523
  for (const f of worldLayer.values()) {
52607
53524
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52608
53525
  if (!iso || regionById.has(iso)) continue;
@@ -52619,11 +53536,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52619
53536
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52620
53537
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52621
53538
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52622
- countryCandidates.push({
53539
+ rawCountries.push({
53540
+ f,
53541
+ iso,
52623
53542
  name: f.properties?.name ?? iso,
52624
53543
  bbox: [x0, y0, x1, y1],
52625
53544
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52626
- curatedAnchor: !!anchorLngLat
53545
+ curatedLngLat: anchorLngLat ?? null,
53546
+ area: (x1 - x0) * (y1 - y0)
53547
+ });
53548
+ }
53549
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53550
+ const cBudget = labelBudget(width, height, cBand);
53551
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53552
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53553
+ const rankOrder = rawCountries.map((r, idx) => {
53554
+ let dist = Infinity;
53555
+ const [x0, y0, x1, y1] = r.bbox;
53556
+ for (const [px, py] of contentPoints) {
53557
+ const dx = Math.max(x0 - px, 0, px - x1);
53558
+ const dy = Math.max(y0 - py, 0, py - y1);
53559
+ const d = dx * dx + dy * dy;
53560
+ if (d < dist) dist = d;
53561
+ }
53562
+ return { idx, area: r.area, dist };
53563
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53564
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53565
+ ).slice(0, topN);
53566
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53567
+ for (let i = 0; i < rawCountries.length; i++) {
53568
+ const r = rawCountries[i];
53569
+ let anchor = r.anchor;
53570
+ let positions;
53571
+ if (genIdx.has(i) && anchor) {
53572
+ const gb = (0, import_d3_geo2.geoBounds)(r.f);
53573
+ const gen = countryLabelPositions({
53574
+ geometry: r.f.geometry,
53575
+ bounds: gb,
53576
+ project,
53577
+ width,
53578
+ height,
53579
+ curated: r.curatedLngLat
53580
+ });
53581
+ if (gen.length) {
53582
+ positions = gen.map((p) => p.screen);
53583
+ anchor = positions[0];
53584
+ }
53585
+ }
53586
+ countryCandidates.push({
53587
+ name: r.name,
53588
+ bbox: r.bbox,
53589
+ anchor,
53590
+ curatedAnchor: !!r.curatedLngLat,
53591
+ ...positions ? { positions } : {}
52627
53592
  });
52628
53593
  }
52629
53594
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52657,6 +53622,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52657
53622
  palette,
52658
53623
  project,
52659
53624
  collides,
53625
+ contentPoints,
52660
53626
  // Water labels must stay over open water — `fillAt` returns the ocean
52661
53627
  // backdrop colour off-land and a region fill on-land (lakes/states count
52662
53628
  // as land here, which is the safe side for an ocean name).
@@ -52910,7 +53876,7 @@ function ringToCoastPaths(ring, frame) {
52910
53876
  function coastlineOuterRings(regions, minExtent, frame) {
52911
53877
  const paths = [];
52912
53878
  for (const r of regions) {
52913
- const rings = parsePathRings(r.d);
53879
+ const rings = r.rings ?? parsePathRings(r.d);
52914
53880
  for (let i = 0; i < rings.length; i++) {
52915
53881
  const ring = rings[i];
52916
53882
  if (ring.length < 3) continue;
@@ -52974,6 +53940,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52974
53940
  const svg = d3Selection23.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY).style("background", layout.background);
52975
53941
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", layout.background);
52976
53942
  const defs = svg.append("defs");
53943
+ const uid = mapInstanceCounter++;
53944
+ const nid = (base) => `${base}__m${uid}`;
52977
53945
  const arrowSize = (w) => Math.min(15, 7 + w * 0.95);
52978
53946
  const haloColor = palette.bg;
52979
53947
  const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
@@ -53006,8 +53974,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53006
53974
  for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
53007
53975
  if (layout.relief.length && layout.reliefHatch) {
53008
53976
  const h = layout.reliefHatch;
53009
- const rangeClipId = "dgmo-relief-clip";
53010
- const landClipId = "dgmo-relief-land";
53977
+ const rangeClipId = nid("dgmo-relief-clip");
53978
+ const landClipId = nid("dgmo-relief-land");
53011
53979
  const rangeClip = defs.append("clipPath").attr("id", rangeClipId);
53012
53980
  for (const s of layout.relief) rangeClip.append("path").attr("d", s.d);
53013
53981
  const landClip = defs.append("clipPath").attr("id", landClipId);
@@ -53020,7 +53988,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53020
53988
  }
53021
53989
  if (layout.coastlineStyle) {
53022
53990
  const cs = layout.coastlineStyle;
53023
- const maskId = "dgmo-map-water-mask";
53991
+ const maskId = nid("dgmo-map-water-mask");
53024
53992
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
53025
53993
  mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
53026
53994
  const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
@@ -53038,7 +54006,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53038
54006
  appendWaterLines(
53039
54007
  gWater,
53040
54008
  defs,
53041
- "dgmo-map-coast",
54009
+ nid("dgmo-map-coast"),
53042
54010
  // Pass the canvas frame so edges collinear with it (the antimeridian on a
53043
54011
  // world map, regional clipExtent cuts) don't get ringed as fake coast —
53044
54012
  // land runs cleanly to the render-area edge.
@@ -53075,7 +54043,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53075
54043
  (l) => l.poiId !== void 0 && !l.hidden
53076
54044
  );
53077
54045
  if (poiLabels.length) {
53078
- const patchBlurId = "dgmo-map-label-patch-blur";
54046
+ const patchBlurId = nid("dgmo-map-label-patch-blur");
53079
54047
  defs.append("filter").attr("id", patchBlurId).attr("x", "-50%").attr("y", "-50%").attr("width", "200%").attr("height", "200%").append("feGaussianBlur").attr("in", "SourceGraphic").attr("stdDeviation", 2.5);
53080
54048
  const PAD = 8;
53081
54049
  const buildPatch = (labels, maskId, decoCluster) => {
@@ -53103,14 +54071,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53103
54071
  if (decoCluster !== void 0)
53104
54072
  gPatch.attr("data-cluster-deco", decoCluster);
53105
54073
  for (const r of layout.regions) {
53106
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53107
- for (const ring of parsePathRings(r.d))
53108
- for (const [px, py] of ring) {
53109
- if (px < minX) minX = px;
53110
- if (px > maxX) maxX = px;
53111
- if (py < minY) minY = py;
53112
- if (py > maxY) maxY = py;
53113
- }
54074
+ let minX, minY, maxX, maxY;
54075
+ if (r.bbox) {
54076
+ [minX, minY, maxX, maxY] = r.bbox;
54077
+ } else {
54078
+ minX = Infinity;
54079
+ minY = Infinity;
54080
+ maxX = -Infinity;
54081
+ maxY = -Infinity;
54082
+ for (const ring of parsePathRings(r.d))
54083
+ for (const [px, py] of ring) {
54084
+ if (px < minX) minX = px;
54085
+ if (px > maxX) maxX = px;
54086
+ if (py < minY) minY = py;
54087
+ if (py > maxY) maxY = py;
54088
+ }
54089
+ }
53114
54090
  const hit = blobRects.some(
53115
54091
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53116
54092
  );
@@ -53120,7 +54096,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53120
54096
  if (clusterUi) {
53121
54097
  buildPatch(
53122
54098
  poiLabels.filter((l) => l.clusterMember === void 0),
53123
- "dgmo-map-label-patch"
54099
+ nid("dgmo-map-label-patch")
53124
54100
  );
53125
54101
  const byCluster = /* @__PURE__ */ new Map();
53126
54102
  for (const l of poiLabels) {
@@ -53131,9 +54107,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53131
54107
  }
53132
54108
  let ci = 0;
53133
54109
  for (const [cid, labs] of byCluster)
53134
- buildPatch(labs, `dgmo-map-label-patch-c${ci++}`, cid);
54110
+ buildPatch(labs, nid(`dgmo-map-label-patch-c${ci++}`), cid);
53135
54111
  } else {
53136
- buildPatch(poiLabels, "dgmo-map-label-patch");
54112
+ buildPatch(poiLabels, nid("dgmo-map-label-patch"));
53137
54113
  }
53138
54114
  }
53139
54115
  if (layout.insets.length) {
@@ -53142,7 +54118,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53142
54118
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
53143
54119
  insetG.append("path").attr("d", d).attr("fill", layout.background).attr("stroke", mix(palette.text, palette.bg, 55)).attr("stroke-width", 1).attr("stroke-linejoin", "round");
53144
54120
  if (box.contextLand) {
53145
- const clipId = `dgmo-map-inset-clip-${bi}`;
54121
+ const clipId = nid(`dgmo-map-inset-clip-${bi}`);
53146
54122
  defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
53147
54123
  insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
53148
54124
  }
@@ -53150,7 +54126,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53150
54126
  for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
53151
54127
  if (layout.coastlineStyle) {
53152
54128
  const cs = layout.coastlineStyle;
53153
- const maskId = "dgmo-map-inset-water-mask";
54129
+ const maskId = nid("dgmo-map-inset-water-mask");
53154
54130
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
53155
54131
  const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
53156
54132
  for (const box of layout.insets) {
@@ -53159,7 +54135,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53159
54135
  }
53160
54136
  layout.insets.forEach((box, bi) => {
53161
54137
  if (box.contextLand)
53162
- mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
54138
+ mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#${nid(`dgmo-map-inset-clip-${bi}`)})`);
53163
54139
  });
53164
54140
  for (const r of layout.insetRegions)
53165
54141
  if (r.id !== "lake")
@@ -53167,7 +54143,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53167
54143
  for (const r of layout.insetRegions)
53168
54144
  if (r.id === "lake")
53169
54145
  mask.append("path").attr("d", r.d).attr("fill", "white");
53170
- const clipId = "dgmo-map-inset-water-clip";
54146
+ const clipId = nid("dgmo-map-inset-water-clip");
53171
54147
  const clip = defs.append("clipPath").attr("id", clipId);
53172
54148
  for (const box of layout.insets) {
53173
54149
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
@@ -53177,7 +54153,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53177
54153
  appendWaterLines(
53178
54154
  gInsetWater,
53179
54155
  defs,
53180
- "dgmo-map-inset-coast",
54156
+ nid("dgmo-map-inset-coast"),
53181
54157
  coastlineOuterRings(layout.insetRegions, cs.minExtent),
53182
54158
  cs,
53183
54159
  layout.background
@@ -53206,7 +54182,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53206
54182
  }
53207
54183
  wireSync(p, leg.lineNumber);
53208
54184
  if (leg.arrow) {
53209
- const id = `dgmo-map-arrow-${i}`;
54185
+ const id = nid(`dgmo-map-arrow-${i}`);
53210
54186
  const s = arrowSize(leg.width);
53211
54187
  defs.append("marker").attr("id", id).attr("viewBox", "0 0 10 10").attr("refX", 10).attr("refY", 5).attr("markerUnits", "userSpaceOnUse").attr("markerWidth", s).attr("markerHeight", s).attr("orient", "auto-start-reverse").append("path").attr("d", "M0,0L10,5L0,10z").attr("fill", leg.color);
53212
54188
  p.attr("marker-end", `url(#${id})`);
@@ -53402,7 +54378,7 @@ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic
53402
54378
  }
53403
54379
  return t;
53404
54380
  }
53405
- var d3Selection23, LABEL_FONT;
54381
+ var d3Selection23, LABEL_FONT, mapInstanceCounter;
53406
54382
  var init_renderer16 = __esm({
53407
54383
  "src/map/renderer.ts"() {
53408
54384
  "use strict";
@@ -53415,6 +54391,7 @@ var init_renderer16 = __esm({
53415
54391
  init_legend_band();
53416
54392
  init_layout15();
53417
54393
  LABEL_FONT = 11;
54394
+ mapInstanceCounter = 0;
53418
54395
  }
53419
54396
  });
53420
54397
 
@@ -54337,7 +55314,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54337
55314
  const tasksAll = [...allTasks(parsed)];
54338
55315
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54339
55316
  const solid = parsed.options["solid-fill"] === "on";
54340
- const surfaceBg = isDark ? palette.surface : palette.bg;
55317
+ const surfaceBg = themeBaseBg(palette, isDark);
54341
55318
  const roleCount = Math.max(1, parsed.roles.length);
54342
55319
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54343
55320
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54346,7 +55323,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54346
55323
  const sHMargin = ctx.aesthetic(H_MARGIN);
54347
55324
  const sVMargin = ctx.aesthetic(V_MARGIN3);
54348
55325
  const sTitleAreaHeight = ctx.structural(TITLE_AREA_HEIGHT4);
54349
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT6);
55326
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT5);
54350
55327
  const sRowHeight = ctx.structural(ROW_HEIGHT);
54351
55328
  const sPhaseHeight = ctx.structural(PHASE_HEIGHT);
54352
55329
  const sTaskLabelMin = ctx.structural(TASK_LABEL_MIN);
@@ -54371,7 +55348,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54371
55348
  const sLegendLetterFont = ctx.text(LEGEND_LETTER_FONT);
54372
55349
  const sViolationLineHeight = ctx.structural(VIOLATION_LINE_HEIGHT);
54373
55350
  const sStackTopGap = ctx.structural(STACK_TOP_GAP);
54374
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH12);
55351
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
54375
55352
  const sNodeRadius = ctx.structural(NODE_RADIUS3);
54376
55353
  const innerWidth = Math.max(0, width - 2 * sHMargin);
54377
55354
  let roleColW = Math.max(
@@ -54515,10 +55492,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54515
55492
  const cx = roleX(i) + sColumnInset;
54516
55493
  const cw = roleColW - 2 * sColumnInset;
54517
55494
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54518
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55495
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54519
55496
  const headerFill = mix(
54520
55497
  roleColor,
54521
- isDark ? palette.surface : palette.bg,
55498
+ themeBaseBg(palette, isDark),
54522
55499
  30
54523
55500
  );
54524
55501
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -54917,7 +55894,7 @@ function parseQuotedSegments(message) {
54917
55894
  out.push({ text: message.slice(last), bold: false });
54918
55895
  return out;
54919
55896
  }
54920
- var d3Selection26, MARKER_LABELS, TITLE_AREA_HEIGHT4, H_MARGIN, V_MARGIN3, LEGEND_HEIGHT2, LEGEND_CHIP_GAP, LEGEND_CHIP_LABEL_MIN, LEGEND_LETTER_CHIP_W, TITLE_LEGEND_GAP, LEGEND_LABEL_FONT, LEGEND_LETTER_FONT, HEADER_HEIGHT6, ROW_HEIGHT, PHASE_HEIGHT, TASK_LABEL_MIN, TASK_LABEL_MAX, ROLE_COL_MIN, ROLE_COL_MAX, CELL_PAD, VIOLATION_LINE_HEIGHT, STACK_TOP_GAP, DESC_FONT, COLUMN_RADIUS3, COLUMN_INSET, COLUMN_BOTTOM_PAD, MARKER_FONT, MARKER_FONT_WEIGHT, LABEL_FONT2, ROLE_HEADER_FONT, PHASE_FONT, TINT_PCT, NODE_STROKE_WIDTH12, NODE_RADIUS3, AUTO_ACCENTS, SLICE_GAP, NAME_LINE_HEIGHT, DESC_LINE_HEIGHT9;
55897
+ var d3Selection26, MARKER_LABELS, TITLE_AREA_HEIGHT4, H_MARGIN, V_MARGIN3, LEGEND_HEIGHT2, LEGEND_CHIP_GAP, LEGEND_CHIP_LABEL_MIN, LEGEND_LETTER_CHIP_W, TITLE_LEGEND_GAP, LEGEND_LABEL_FONT, LEGEND_LETTER_FONT, HEADER_HEIGHT5, ROW_HEIGHT, PHASE_HEIGHT, TASK_LABEL_MIN, TASK_LABEL_MAX, ROLE_COL_MIN, ROLE_COL_MAX, CELL_PAD, VIOLATION_LINE_HEIGHT, STACK_TOP_GAP, DESC_FONT, COLUMN_RADIUS3, COLUMN_INSET, COLUMN_BOTTOM_PAD, MARKER_FONT, MARKER_FONT_WEIGHT, LABEL_FONT2, ROLE_HEADER_FONT, PHASE_FONT, TINT_PCT, NODE_RADIUS3, AUTO_ACCENTS, SLICE_GAP, NAME_LINE_HEIGHT, DESC_LINE_HEIGHT9;
54921
55898
  var init_renderer19 = __esm({
54922
55899
  "src/raci/renderer.ts"() {
54923
55900
  "use strict";
@@ -54929,6 +55906,7 @@ var init_renderer19 = __esm({
54929
55906
  init_variants();
54930
55907
  init_scaling();
54931
55908
  init_text_measure();
55909
+ init_visual_conventions();
54932
55910
  MARKER_LABELS = {
54933
55911
  raci: { R: "Responsible", A: "Accountable", C: "Consulted", I: "Informed" },
54934
55912
  rasci: {
@@ -54950,7 +55928,7 @@ var init_renderer19 = __esm({
54950
55928
  TITLE_LEGEND_GAP = 16;
54951
55929
  LEGEND_LABEL_FONT = 12;
54952
55930
  LEGEND_LETTER_FONT = 14;
54953
- HEADER_HEIGHT6 = 36;
55931
+ HEADER_HEIGHT5 = 36;
54954
55932
  ROW_HEIGHT = 36;
54955
55933
  PHASE_HEIGHT = 40;
54956
55934
  TASK_LABEL_MIN = 200;
@@ -54970,7 +55948,6 @@ var init_renderer19 = __esm({
54970
55948
  ROLE_HEADER_FONT = 12;
54971
55949
  PHASE_FONT = 13;
54972
55950
  TINT_PCT = 25;
54973
- NODE_STROKE_WIDTH12 = 1.5;
54974
55951
  NODE_RADIUS3 = 6;
54975
55952
  AUTO_ACCENTS = [
54976
55953
  "blue",
@@ -55570,11 +56547,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55570
56547
  const lines = splitParticipantLabel(
55571
56548
  p.label,
55572
56549
  labelTextWidth(PARTICIPANT_BOX_WIDTH),
55573
- LABEL_FONT_SIZE6
56550
+ LABEL_FONT_SIZE5
55574
56551
  );
55575
56552
  if (lines.length === 0) continue;
55576
56553
  const widest = Math.max(
55577
- ...lines.map((l) => measureText(l, LABEL_FONT_SIZE6))
56554
+ ...lines.map((l) => measureText(l, LABEL_FONT_SIZE5))
55578
56555
  );
55579
56556
  const labelWidth2 = widest + 10;
55580
56557
  uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
@@ -55608,7 +56585,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55608
56585
  const sSelfCallWidth = ctx.structural(SELF_CALL_WIDTH);
55609
56586
  const sNoteTextWidthMax = sNoteMaxW - sNotePadH * 2 - sNoteFold;
55610
56587
  const sNoteLaneMax = sGap - sActivationWidth - sNoteGap;
55611
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE6);
56588
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE5);
55612
56589
  const sLabelTextWidth = labelTextWidth(sBoxW);
55613
56590
  const participantIndexMap = /* @__PURE__ */ new Map();
55614
56591
  participants.forEach((p, i) => participantIndexMap.set(p.id, i));
@@ -56245,7 +57222,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56245
57222
  const groupTagColor = getTagColor(groupTagValue || void 0);
56246
57223
  const fillColor = groupTagColor ? mix(
56247
57224
  groupTagColor,
56248
- isDark ? palette.surface : palette.bg,
57225
+ themeBaseBg(palette, isDark),
56249
57226
  isDark ? 15 : 20
56250
57227
  ) : isDark ? palette.surface : palette.bg;
56251
57228
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56299,7 +57276,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56299
57276
  participantG.append("title").text("Click to expand");
56300
57277
  const pFill = effectiveTagColor ? mix(
56301
57278
  effectiveTagColor,
56302
- isDark ? palette.surface : palette.bg,
57279
+ themeBaseBg(palette, isDark),
56303
57280
  isDark ? 30 : 40
56304
57281
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56305
57282
  const pStroke = effectiveTagColor || palette.border;
@@ -56506,7 +57483,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56506
57483
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56507
57484
  const actTagColor = getTagColor(actTagValue);
56508
57485
  const actBaseColor = actTagColor || palette.primary;
56509
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57486
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
56510
57487
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56511
57488
  const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
56512
57489
  if (tagKey && actTagValue) {
@@ -56745,7 +57722,7 @@ function buildNoteMessageMap(elements) {
56745
57722
  walk(elements);
56746
57723
  return map;
56747
57724
  }
56748
- function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr, solid, boxW = W, boxH = H, labelTextW = labelTextWidth(W), labelFontSize2 = LABEL_FONT_SIZE6) {
57725
+ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr, solid, boxW = W, boxH = H, labelTextW = labelTextWidth(W), labelFontSize2 = LABEL_FONT_SIZE5) {
56749
57726
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
56750
57727
  if (tagAttr) {
56751
57728
  g.attr(`data-tag-${tagAttr.key}`, tagAttr.value);
@@ -56793,7 +57770,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
56793
57770
  });
56794
57771
  }
56795
57772
  }
56796
- var d3Selection27, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, LABEL_FONT_SIZE6, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W2, NOTE_FOLD2, NOTE_PAD_H2, NOTE_PAD_V2, NOTE_FONT_SIZE2, NOTE_LINE_H2, NOTE_GAP2, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, ACTOR_LABEL_CLEARANCE, labelTextWidth, fill, stroke, SW, W, H;
57773
+ var d3Selection27, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, LABEL_FONT_SIZE5, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W2, NOTE_FOLD2, NOTE_PAD_H2, NOTE_PAD_V2, NOTE_FONT_SIZE2, NOTE_LINE_H2, NOTE_GAP2, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, ACTOR_LABEL_CLEARANCE, labelTextWidth, fill, stroke, SW, W, H;
56797
57774
  var init_renderer20 = __esm({
56798
57775
  "src/sequence/renderer.ts"() {
56799
57776
  "use strict";
@@ -56814,7 +57791,7 @@ var init_renderer20 = __esm({
56814
57791
  PARTICIPANT_GAP = 160;
56815
57792
  PARTICIPANT_BOX_WIDTH = 120;
56816
57793
  PARTICIPANT_BOX_HEIGHT = 50;
56817
- LABEL_FONT_SIZE6 = 13;
57794
+ LABEL_FONT_SIZE5 = 13;
56818
57795
  TOP_MARGIN = 20;
56819
57796
  TITLE_HEIGHT8 = 30;
56820
57797
  PARTICIPANT_Y_OFFSET = 10;
@@ -58229,7 +59206,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58229
59206
  const textColor = palette.text;
58230
59207
  const mutedColor = palette.border;
58231
59208
  const bgColor = palette.bg;
58232
- const bg = isDark ? palette.surface : palette.bg;
59209
+ const bg = themeBaseBg(palette, isDark);
58233
59210
  const colors = getSeriesColors(palette);
58234
59211
  const groupColorMap = /* @__PURE__ */ new Map();
58235
59212
  timelineGroups.forEach((grp, i) => {
@@ -58644,9 +59621,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58644
59621
  drawLegend2();
58645
59622
  }
58646
59623
  }
58647
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59624
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58648
59625
  const {
58649
59626
  width,
59627
+ height,
58650
59628
  tooltip,
58651
59629
  solid,
58652
59630
  textColor,
@@ -58698,9 +59676,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58698
59676
  const rowH = ctx.structural(28);
58699
59677
  const innerHeight = rowH * sorted.length;
58700
59678
  const usedHeight = margin.top + innerHeight + margin.bottom;
59679
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59680
+ const svgHeight = fitToContainer ? height : usedHeight;
58701
59681
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58702
- 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);
58703
- if (ctx.isBelowFloor) {
59682
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", svgHeight).attr("viewBox", `0 0 ${width} ${usedHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
59683
+ if (ctx.isBelowFloor && !fitToContainer) {
58704
59684
  svg.attr("width", "100%");
58705
59685
  }
58706
59686
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60374,7 +61354,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60374
61354
  onClickItem
60375
61355
  );
60376
61356
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60377
- const bg = isDark ? palette.surface : palette.bg;
61357
+ const bg = themeBaseBg(palette, isDark);
60378
61358
  const getQuadrantColor = (label, defaultIdx) => {
60379
61359
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60380
61360
  };
@@ -60711,18 +61691,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60711
61691
  palette,
60712
61692
  viewState,
60713
61693
  options,
60714
- exportMode
61694
+ exportMode,
61695
+ isDark: theme === "dark"
60715
61696
  };
60716
61697
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
60717
61698
  return (handler ?? exportVisualization)(ctx);
60718
61699
  }
61700
+ function ctxTagOverride(ctx) {
61701
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61702
+ }
60719
61703
  async function exportOrg(ctx) {
60720
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61704
+ const { content, theme, palette, viewState, exportMode } = ctx;
60721
61705
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
60722
61706
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
60723
61707
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
60724
61708
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
60725
- const isDark = theme === "dark";
61709
+ const isDark = ctx.isDark;
60726
61710
  const effectivePalette = await resolveExportPalette(theme, palette);
60727
61711
  const orgParsed = parseOrg2(content, effectivePalette);
60728
61712
  if (orgParsed.error) return "";
@@ -60730,7 +61714,7 @@ async function exportOrg(ctx) {
60730
61714
  const activeTagGroup = resolveActiveTagGroup(
60731
61715
  orgParsed.tagGroups,
60732
61716
  orgParsed.options["active-tag"],
60733
- viewState?.tag ?? options?.tagGroup
61717
+ ctxTagOverride(ctx)
60734
61718
  );
60735
61719
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60736
61720
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60763,12 +61747,12 @@ async function exportOrg(ctx) {
60763
61747
  return finalizeSvgExport(container, theme, effectivePalette);
60764
61748
  }
60765
61749
  async function exportSitemap(ctx) {
60766
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61750
+ const { content, theme, palette, viewState, exportMode } = ctx;
60767
61751
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
60768
61752
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
60769
61753
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
60770
61754
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
60771
- const isDark = theme === "dark";
61755
+ const isDark = ctx.isDark;
60772
61756
  const effectivePalette = await resolveExportPalette(theme, palette);
60773
61757
  const sitemapParsed = parseSitemap2(content, effectivePalette);
60774
61758
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -60776,7 +61760,7 @@ async function exportSitemap(ctx) {
60776
61760
  const activeTagGroup = resolveActiveTagGroup(
60777
61761
  sitemapParsed.tagGroups,
60778
61762
  sitemapParsed.options["active-tag"],
60779
- viewState?.tag ?? options?.tagGroup
61763
+ ctxTagOverride(ctx)
60780
61764
  );
60781
61765
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60782
61766
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60807,7 +61791,7 @@ async function exportSitemap(ctx) {
60807
61791
  return finalizeSvgExport(container, theme, effectivePalette);
60808
61792
  }
60809
61793
  async function exportKanban(ctx) {
60810
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61794
+ const { content, theme, palette, viewState, exportMode } = ctx;
60811
61795
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
60812
61796
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
60813
61797
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -60819,11 +61803,11 @@ async function exportKanban(ctx) {
60819
61803
  document.body.appendChild(container);
60820
61804
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
60821
61805
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
60822
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61806
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
60823
61807
  activeTagGroup: resolveActiveTagGroup(
60824
61808
  kanbanParsed.tagGroups,
60825
61809
  kanbanParsed.options["active-tag"],
60826
- viewState?.tag ?? options?.tagGroup
61810
+ ctxTagOverride(ctx)
60827
61811
  ),
60828
61812
  currentSwimlaneGroup: viewState?.swim ?? null,
60829
61813
  ...kanbanCollapsedLanes !== void 0 && {
@@ -60856,7 +61840,7 @@ async function exportClass(ctx) {
60856
61840
  classParsed,
60857
61841
  classLayout,
60858
61842
  effectivePalette,
60859
- theme === "dark",
61843
+ ctx.isDark,
60860
61844
  void 0,
60861
61845
  { width: exportWidth, height: exportHeight },
60862
61846
  void 0,
@@ -60865,7 +61849,7 @@ async function exportClass(ctx) {
60865
61849
  return finalizeSvgExport(container, theme, effectivePalette);
60866
61850
  }
60867
61851
  async function exportEr(ctx) {
60868
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61852
+ const { content, theme, palette, viewState, exportMode } = ctx;
60869
61853
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
60870
61854
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
60871
61855
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -60883,13 +61867,13 @@ async function exportEr(ctx) {
60883
61867
  erParsed,
60884
61868
  erLayout,
60885
61869
  effectivePalette,
60886
- theme === "dark",
61870
+ ctx.isDark,
60887
61871
  void 0,
60888
61872
  { width: exportWidth, height: exportHeight },
60889
61873
  resolveActiveTagGroup(
60890
61874
  erParsed.tagGroups,
60891
61875
  erParsed.options["active-tag"],
60892
- viewState?.tag ?? options?.tagGroup
61876
+ ctxTagOverride(ctx)
60893
61877
  ),
60894
61878
  viewState?.sem,
60895
61879
  exportMode
@@ -60897,7 +61881,7 @@ async function exportEr(ctx) {
60897
61881
  return finalizeSvgExport(container, theme, effectivePalette);
60898
61882
  }
60899
61883
  async function exportBoxesAndLines(ctx) {
60900
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61884
+ const { content, theme, palette, viewState, exportMode } = ctx;
60901
61885
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
60902
61886
  const effectivePalette = await resolveExportPalette(theme, palette);
60903
61887
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -60917,13 +61901,13 @@ async function exportBoxesAndLines(ctx) {
60917
61901
  const exportWidth = blLayout.width + PADDING3 * 2;
60918
61902
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
60919
61903
  const container = createExportContainer(exportWidth, exportHeight);
60920
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61904
+ const blActiveTagGroup = ctxTagOverride(ctx);
60921
61905
  renderBoxesAndLinesForExport2(
60922
61906
  container,
60923
61907
  blParsed,
60924
61908
  blLayout,
60925
61909
  effectivePalette,
60926
- theme === "dark",
61910
+ ctx.isDark,
60927
61911
  {
60928
61912
  exportDims: { width: exportWidth, height: exportHeight },
60929
61913
  ...blActiveTagGroup !== void 0 && {
@@ -60938,12 +61922,12 @@ async function exportBoxesAndLines(ctx) {
60938
61922
  return finalizeSvgExport(container, theme, effectivePalette);
60939
61923
  }
60940
61924
  async function exportMindmap(ctx) {
60941
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61925
+ const { content, theme, palette, viewState, exportMode } = ctx;
60942
61926
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
60943
61927
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
60944
61928
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
60945
61929
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
60946
- const isDark = theme === "dark";
61930
+ const isDark = ctx.isDark;
60947
61931
  const effectivePalette = await resolveExportPalette(theme, palette);
60948
61932
  const mmParsed = parseMindmap2(content, effectivePalette);
60949
61933
  if (mmParsed.error) return "";
@@ -60951,7 +61935,7 @@ async function exportMindmap(ctx) {
60951
61935
  const activeTagGroup = resolveActiveTagGroup(
60952
61936
  mmParsed.tagGroups,
60953
61937
  mmParsed.options["active-tag"],
60954
- viewState?.tag ?? options?.tagGroup
61938
+ ctxTagOverride(ctx)
60955
61939
  );
60956
61940
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
60957
61941
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61001,7 +61985,7 @@ async function exportWireframe(ctx) {
61001
61985
  wireframeParsed,
61002
61986
  wireframeLayout,
61003
61987
  effectivePalette,
61004
- theme === "dark",
61988
+ ctx.isDark,
61005
61989
  void 0,
61006
61990
  { width: exportWidth, height: exportHeight },
61007
61991
  theme
@@ -61009,7 +61993,7 @@ async function exportWireframe(ctx) {
61009
61993
  return finalizeSvgExport(container, theme, effectivePalette);
61010
61994
  }
61011
61995
  async function exportC4(ctx) {
61012
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61996
+ const { content, theme, palette, viewState, exportMode } = ctx;
61013
61997
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61014
61998
  const {
61015
61999
  layoutC4Context: layoutC4Context2,
@@ -61021,9 +62005,9 @@ async function exportC4(ctx) {
61021
62005
  const effectivePalette = await resolveExportPalette(theme, palette);
61022
62006
  const c4Parsed = parseC42(content, effectivePalette);
61023
62007
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61024
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61025
- const c4System = options?.c4System ?? viewState?.c4s;
61026
- const c4Container = options?.c4Container ?? viewState?.c4c;
62008
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62009
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62010
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61027
62011
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61028
62012
  if (c4Layout.nodes.length === 0) return "";
61029
62013
  const PADDING3 = 20;
@@ -61037,13 +62021,13 @@ async function exportC4(ctx) {
61037
62021
  c4Parsed,
61038
62022
  c4Layout,
61039
62023
  effectivePalette,
61040
- theme === "dark",
62024
+ ctx.isDark,
61041
62025
  void 0,
61042
62026
  { width: exportWidth, height: exportHeight },
61043
62027
  resolveActiveTagGroup(
61044
62028
  c4Parsed.tagGroups,
61045
62029
  c4Parsed.options["active-tag"],
61046
- viewState?.tag ?? options?.tagGroup
62030
+ ctxTagOverride(ctx)
61047
62031
  ),
61048
62032
  exportMode
61049
62033
  );
@@ -61064,14 +62048,14 @@ async function exportFlowchart(ctx) {
61064
62048
  fcParsed,
61065
62049
  layout,
61066
62050
  effectivePalette,
61067
- theme === "dark",
62051
+ ctx.isDark,
61068
62052
  void 0,
61069
62053
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61070
62054
  );
61071
62055
  return finalizeSvgExport(container, theme, effectivePalette);
61072
62056
  }
61073
62057
  async function exportInfra(ctx) {
61074
- const { content, theme, palette, viewState, options } = ctx;
62058
+ const { content, theme, palette, viewState } = ctx;
61075
62059
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61076
62060
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61077
62061
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61084,7 +62068,7 @@ async function exportInfra(ctx) {
61084
62068
  const activeTagGroup = resolveActiveTagGroup(
61085
62069
  infraParsed.tagGroups,
61086
62070
  infraParsed.options["active-tag"],
61087
- viewState?.tag ?? options?.tagGroup
62071
+ ctxTagOverride(ctx)
61088
62072
  );
61089
62073
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61090
62074
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61102,7 +62086,7 @@ async function exportInfra(ctx) {
61102
62086
  container,
61103
62087
  infraLayout,
61104
62088
  effectivePalette,
61105
- theme === "dark",
62089
+ ctx.isDark,
61106
62090
  showInfraTitle ? infraParsed.title : null,
61107
62091
  showInfraTitle ? infraParsed.titleLineNumber : null,
61108
62092
  infraTagGroups,
@@ -61149,7 +62133,7 @@ async function exportPert(ctx) {
61149
62133
  pertResolved,
61150
62134
  pertLayout,
61151
62135
  effectivePalette,
61152
- theme === "dark",
62136
+ ctx.isDark,
61153
62137
  {
61154
62138
  title: pertParsed.title,
61155
62139
  exportDims: { width: exportW, height: exportH },
@@ -61162,7 +62146,7 @@ async function exportPert(ctx) {
61162
62146
  return finalizeSvgExport(container, theme, effectivePalette);
61163
62147
  }
61164
62148
  async function exportGantt(ctx) {
61165
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62149
+ const { content, theme, palette, viewState, exportMode } = ctx;
61166
62150
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61167
62151
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61168
62152
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61180,7 +62164,7 @@ async function exportGantt(ctx) {
61180
62164
  container,
61181
62165
  resolved,
61182
62166
  effectivePalette,
61183
- theme === "dark",
62167
+ ctx.isDark,
61184
62168
  {
61185
62169
  ...ganttCollapsedGroups !== void 0 && {
61186
62170
  collapsedGroups: ganttCollapsedGroups
@@ -61194,7 +62178,7 @@ async function exportGantt(ctx) {
61194
62178
  currentActiveGroup: resolveActiveTagGroup(
61195
62179
  resolved.tagGroups,
61196
62180
  resolved.options.activeTag ?? void 0,
61197
- viewState?.tag ?? options?.tagGroup
62181
+ ctxTagOverride(ctx)
61198
62182
  ),
61199
62183
  exportMode
61200
62184
  },
@@ -61217,7 +62201,7 @@ async function exportState(ctx) {
61217
62201
  stateParsed,
61218
62202
  layout,
61219
62203
  effectivePalette,
61220
- theme === "dark",
62204
+ ctx.isDark,
61221
62205
  void 0,
61222
62206
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61223
62207
  );
@@ -61237,7 +62221,7 @@ async function exportTechRadar(ctx) {
61237
62221
  container,
61238
62222
  radarParsed,
61239
62223
  effectivePalette,
61240
- theme === "dark",
62224
+ ctx.isDark,
61241
62225
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61242
62226
  viewState,
61243
62227
  exportMode
@@ -61254,13 +62238,13 @@ async function exportJourneyMap(ctx) {
61254
62238
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61255
62239
  return "";
61256
62240
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61257
- isDark: theme === "dark"
62241
+ isDark: ctx.isDark
61258
62242
  });
61259
62243
  const container = createExportContainer(
61260
62244
  jmLayout.totalWidth,
61261
62245
  jmLayout.totalHeight
61262
62246
  );
61263
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62247
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61264
62248
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61265
62249
  exportMode
61266
62250
  });
@@ -61278,7 +62262,7 @@ async function exportCycle(ctx) {
61278
62262
  container,
61279
62263
  cycleParsed,
61280
62264
  effectivePalette,
61281
- theme === "dark",
62265
+ ctx.isDark,
61282
62266
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61283
62267
  viewState,
61284
62268
  exportMode
@@ -61315,7 +62299,7 @@ async function exportMap(ctx) {
61315
62299
  mapResolved,
61316
62300
  mapData,
61317
62301
  effectivePalette,
61318
- theme === "dark",
62302
+ ctx.isDark,
61319
62303
  dims
61320
62304
  );
61321
62305
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61332,7 +62316,7 @@ async function exportPyramid(ctx) {
61332
62316
  container,
61333
62317
  pyramidParsed,
61334
62318
  effectivePalette,
61335
- theme === "dark",
62319
+ ctx.isDark,
61336
62320
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61337
62321
  );
61338
62322
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61349,7 +62333,7 @@ async function exportRing(ctx) {
61349
62333
  container,
61350
62334
  ringParsed,
61351
62335
  effectivePalette,
61352
- theme === "dark",
62336
+ ctx.isDark,
61353
62337
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61354
62338
  );
61355
62339
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61366,7 +62350,7 @@ async function exportRaci(ctx) {
61366
62350
  container,
61367
62351
  raciParsed,
61368
62352
  effectivePalette,
61369
- theme === "dark",
62353
+ ctx.isDark,
61370
62354
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61371
62355
  );
61372
62356
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61389,7 +62373,7 @@ async function exportSlope(ctx) {
61389
62373
  container,
61390
62374
  parsed,
61391
62375
  effectivePalette,
61392
- theme === "dark",
62376
+ ctx.isDark,
61393
62377
  void 0,
61394
62378
  dims
61395
62379
  );
@@ -61405,14 +62389,14 @@ async function exportArc(ctx) {
61405
62389
  container,
61406
62390
  parsed,
61407
62391
  effectivePalette,
61408
- theme === "dark",
62392
+ ctx.isDark,
61409
62393
  void 0,
61410
62394
  dims
61411
62395
  );
61412
62396
  return finalizeSvgExport(container, theme, effectivePalette);
61413
62397
  }
61414
62398
  async function exportTimeline(ctx) {
61415
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62399
+ const { content, theme, palette, viewState, exportMode } = ctx;
61416
62400
  const parsed = parseTimeline(content, palette);
61417
62401
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61418
62402
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61421,13 +62405,13 @@ async function exportTimeline(ctx) {
61421
62405
  container,
61422
62406
  parsed,
61423
62407
  effectivePalette,
61424
- theme === "dark",
62408
+ ctx.isDark,
61425
62409
  void 0,
61426
62410
  dims,
61427
62411
  resolveActiveTagGroup(
61428
62412
  parsed.timelineTagGroups,
61429
62413
  parsed.timelineActiveTag,
61430
- viewState?.tag ?? options?.tagGroup
62414
+ ctxTagOverride(ctx)
61431
62415
  ),
61432
62416
  viewState?.swim,
61433
62417
  void 0,
@@ -61446,7 +62430,7 @@ async function exportWordcloud(ctx) {
61446
62430
  container,
61447
62431
  parsed,
61448
62432
  effectivePalette,
61449
- theme === "dark",
62433
+ ctx.isDark,
61450
62434
  dims
61451
62435
  );
61452
62436
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61461,7 +62445,7 @@ async function exportVenn(ctx) {
61461
62445
  container,
61462
62446
  parsed,
61463
62447
  effectivePalette,
61464
- theme === "dark",
62448
+ ctx.isDark,
61465
62449
  void 0,
61466
62450
  dims
61467
62451
  );
@@ -61477,14 +62461,14 @@ async function exportQuadrant(ctx) {
61477
62461
  container,
61478
62462
  parsed,
61479
62463
  effectivePalette,
61480
- theme === "dark",
62464
+ ctx.isDark,
61481
62465
  void 0,
61482
62466
  dims
61483
62467
  );
61484
62468
  return finalizeSvgExport(container, theme, effectivePalette);
61485
62469
  }
61486
62470
  async function exportVisualization(ctx) {
61487
- const { content, theme, palette, viewState, options } = ctx;
62471
+ const { content, theme, palette, viewState } = ctx;
61488
62472
  const parsed = parseVisualization(content, palette);
61489
62473
  if (parsed.type !== "sequence") {
61490
62474
  if (parsed.error) {
@@ -61495,7 +62479,7 @@ async function exportVisualization(ctx) {
61495
62479
  }
61496
62480
  }
61497
62481
  const effectivePalette = await resolveExportPalette(theme, palette);
61498
- const isDark = theme === "dark";
62482
+ const isDark = ctx.isDark;
61499
62483
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61500
62484
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61501
62485
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61503,7 +62487,7 @@ async function exportVisualization(ctx) {
61503
62487
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61504
62488
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61505
62489
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61506
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62490
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61507
62491
  renderSequenceDiagram2(
61508
62492
  container,
61509
62493
  seqParsed,
@@ -61749,16 +62733,13 @@ var themes = {
61749
62733
 
61750
62734
  // src/dimensions.ts
61751
62735
  init_dgmo_router();
61752
- init_scaling();
61753
62736
  init_chart_type_registry();
61754
62737
  function getMinDimensions(content) {
61755
62738
  const { chartType } = parseDgmo(content);
61756
62739
  if (!chartType) return { width: 300, height: 200 };
61757
- const counts = extractContentCounts(content, chartType);
61758
- return computeMinDimensions(chartType, counts);
61759
- }
61760
- function extractContentCounts(content, chartType) {
61761
- return REGISTRY_BY_ID.get(chartType)?.measure?.(content) ?? {};
62740
+ const descriptor = REGISTRY_BY_ID.get(chartType);
62741
+ const counts = descriptor?.measure?.(content) ?? {};
62742
+ return descriptor?.minDims?.(counts) ?? { width: 300, height: 200 };
61762
62743
  }
61763
62744
 
61764
62745
  // src/utils/svg-embed.ts