@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.js CHANGED
@@ -38,6 +38,14 @@ function makeDgmoError(line11, message, severity = "error", code) {
38
38
  function formatDgmoError(err) {
39
39
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
40
40
  }
41
+ function makeFail(result) {
42
+ return (line11, message) => {
43
+ const diag = makeDgmoError(line11, message);
44
+ result.diagnostics.push(diag);
45
+ result.error = formatDgmoError(diag);
46
+ return result;
47
+ };
48
+ }
41
49
  function levenshtein(a, b) {
42
50
  const m = a.length;
43
51
  const n = b.length;
@@ -304,21 +312,99 @@ function resolveColor(color, palette) {
304
312
  }
305
313
  return colorNames[lower] ?? null;
306
314
  }
315
+ function nearestNamedColor(input) {
316
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
317
+ if (cssHex) input = cssHex;
318
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
319
+ if (!m) return null;
320
+ let h = m[1].toLowerCase();
321
+ if (h.length === 3)
322
+ h = h.split("").map((c) => c + c).join("");
323
+ const r = parseInt(h.slice(0, 2), 16) / 255;
324
+ const g = parseInt(h.slice(2, 4), 16) / 255;
325
+ const b = parseInt(h.slice(4, 6), 16) / 255;
326
+ const max = Math.max(r, g, b);
327
+ const min = Math.min(r, g, b);
328
+ const delta = max - min;
329
+ const l = (max + min) / 2;
330
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
331
+ if (s < 0.15) {
332
+ if (l < 0.2) return "black";
333
+ if (l > 0.85) return "white";
334
+ return "gray";
335
+ }
336
+ let hue;
337
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
338
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
339
+ else hue = 60 * ((r - g) / delta + 4);
340
+ if (hue < 0) hue += 360;
341
+ const anchors = [
342
+ ["red", 0],
343
+ ["orange", 30],
344
+ ["yellow", 55],
345
+ ["green", 120],
346
+ ["teal", 170],
347
+ ["cyan", 190],
348
+ ["blue", 225],
349
+ ["purple", 285],
350
+ ["red", 360]
351
+ ];
352
+ let best = "red";
353
+ let bestD = Infinity;
354
+ for (const [name, deg] of anchors) {
355
+ const d = Math.abs(hue - deg);
356
+ if (d < bestD) {
357
+ bestD = d;
358
+ best = name;
359
+ }
360
+ }
361
+ return best;
362
+ }
363
+ function isInvalidColorToken(token) {
364
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
365
+ const lower = token.toLowerCase();
366
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
367
+ }
368
+ function invalidColorDiagnostic(token, line11) {
369
+ if (!isInvalidColorToken(token)) return null;
370
+ const nearest = nearestNamedColor(token);
371
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
372
+ return makeDgmoError(
373
+ line11,
374
+ `Color "${token}" is not a valid DGMO color \u2014 DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${near}`,
375
+ "warning",
376
+ INVALID_COLOR_CODE
377
+ );
378
+ }
307
379
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
308
380
  const resolved = resolveColor(color, palette);
309
381
  if (resolved !== null) return resolved;
382
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
383
+ const nearest = nearestNamedColor(color);
384
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
385
+ diagnostics.push(
386
+ makeDgmoError(
387
+ line11,
388
+ `Color "${color}" is not supported \u2014 DGMO does not accept hex or CSS color values. Use a named palette color: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${near}`,
389
+ "error",
390
+ INVALID_COLOR_CODE
391
+ )
392
+ );
393
+ return void 0;
394
+ }
310
395
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
311
396
  const suggestion = hint ? ` ${hint}` : "";
312
397
  diagnostics.push(
313
398
  makeDgmoError(
314
399
  line11,
315
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
316
- "warning"
400
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
401
+ "warning",
402
+ INVALID_COLOR_CODE
317
403
  )
318
404
  );
319
405
  return void 0;
320
406
  }
321
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
407
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
322
408
  var init_colors = __esm({
323
409
  "src/colors.ts"() {
324
410
  "use strict";
@@ -376,6 +462,102 @@ var init_colors = __esm({
376
462
  "black",
377
463
  "white"
378
464
  ]);
465
+ CATEGORICAL_COLOR_ORDER = Object.freeze([
466
+ "red",
467
+ "green",
468
+ "blue",
469
+ "yellow",
470
+ "teal",
471
+ "purple",
472
+ "orange",
473
+ "cyan"
474
+ ]);
475
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
476
+ INVALID_CSS_COLOR_HEX = Object.freeze({
477
+ pink: "#ffc0cb",
478
+ hotpink: "#ff69b4",
479
+ deeppink: "#ff1493",
480
+ lightpink: "#ffb6c1",
481
+ palevioletred: "#db7093",
482
+ crimson: "#dc143c",
483
+ scarlet: "#ff2400",
484
+ firebrick: "#b22222",
485
+ darkred: "#8b0000",
486
+ maroon: "#800000",
487
+ salmon: "#fa8072",
488
+ lightsalmon: "#ffa07a",
489
+ darksalmon: "#e9967a",
490
+ coral: "#ff7f50",
491
+ lightcoral: "#f08080",
492
+ tomato: "#ff6347",
493
+ orangered: "#ff4500",
494
+ darkorange: "#ff8c00",
495
+ gold: "#ffd700",
496
+ goldenrod: "#daa520",
497
+ darkgoldenrod: "#b8860b",
498
+ khaki: "#f0e68c",
499
+ darkkhaki: "#bdb76b",
500
+ amber: "#ffbf00",
501
+ lavender: "#e6e6fa",
502
+ violet: "#ee82ee",
503
+ magenta: "#ff00ff",
504
+ fuchsia: "#ff00ff",
505
+ orchid: "#da70d6",
506
+ plum: "#dda0dd",
507
+ indigo: "#4b0082",
508
+ navy: "#000080",
509
+ midnightblue: "#191970",
510
+ darkblue: "#00008b",
511
+ mediumblue: "#0000cd",
512
+ royalblue: "#4169e1",
513
+ cornflowerblue: "#6495ed",
514
+ dodgerblue: "#1e90ff",
515
+ deepskyblue: "#00bfff",
516
+ skyblue: "#87ceeb",
517
+ lightskyblue: "#87cefa",
518
+ lightblue: "#add8e6",
519
+ powderblue: "#b0e0e6",
520
+ steelblue: "#4682b4",
521
+ slateblue: "#6a5acd",
522
+ cadetblue: "#5f9ea0",
523
+ turquoise: "#40e0d0",
524
+ aqua: "#00ffff",
525
+ aquamarine: "#7fffd4",
526
+ lime: "#00ff00",
527
+ limegreen: "#32cd32",
528
+ lightgreen: "#90ee90",
529
+ palegreen: "#98fb98",
530
+ seagreen: "#2e8b57",
531
+ mediumseagreen: "#3cb371",
532
+ forestgreen: "#228b22",
533
+ darkgreen: "#006400",
534
+ olive: "#808000",
535
+ olivedrab: "#6b8e23",
536
+ darkolivegreen: "#556b2f",
537
+ chartreuse: "#7fff00",
538
+ lawngreen: "#7cfc00",
539
+ springgreen: "#00ff7f",
540
+ greenyellow: "#adff2f",
541
+ brown: "#a52a2a",
542
+ sienna: "#a0522d",
543
+ chocolate: "#d2691e",
544
+ peru: "#cd853f",
545
+ tan: "#d2b48c",
546
+ beige: "#f5f5dc",
547
+ wheat: "#f5deb3",
548
+ ivory: "#fffff0",
549
+ silver: "#c0c0c0",
550
+ lightgray: "#d3d3d3",
551
+ lightgrey: "#d3d3d3",
552
+ darkgray: "#a9a9a9",
553
+ darkgrey: "#a9a9a9",
554
+ dimgray: "#696969",
555
+ dimgrey: "#696969",
556
+ slategray: "#708090",
557
+ slategrey: "#708090",
558
+ gainsboro: "#dcdcdc",
559
+ grey: "#808080"
560
+ });
379
561
  seriesColors = [
380
562
  nord.nord10,
381
563
  // blue
@@ -550,7 +732,13 @@ function extractColor(label, palette, diagnostics, line11) {
550
732
  );
551
733
  if (lastSpaceIdx < 0) return { label };
552
734
  const trailing = label.substring(lastSpaceIdx + 1);
553
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
735
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
736
+ if (diagnostics && line11 !== void 0) {
737
+ const diag = invalidColorDiagnostic(trailing, line11);
738
+ if (diag) diagnostics.push(diag);
739
+ }
740
+ return { label };
741
+ }
554
742
  let color;
555
743
  if (diagnostics && line11 !== void 0) {
556
744
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1174,6 +1362,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1174
1362
  }
1175
1363
  }
1176
1364
  }
1365
+ function cascadeTagMetadata(roots, tagGroups) {
1366
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1367
+ if (keys.length === 0) return;
1368
+ const walk = (node, inherited) => {
1369
+ const childInherited = { ...inherited };
1370
+ for (const key of keys) {
1371
+ const own = node.metadata[key];
1372
+ if (own) {
1373
+ childInherited[key] = own;
1374
+ } else if (inherited[key]) {
1375
+ node.metadata[key] = inherited[key];
1376
+ }
1377
+ }
1378
+ for (const child of node.children) walk(child, childInherited);
1379
+ };
1380
+ for (const root of roots) walk(root, {});
1381
+ }
1177
1382
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1178
1383
  const defaults = [];
1179
1384
  for (const group of tagGroups) {
@@ -1229,11 +1434,7 @@ var init_tag_groups = __esm({
1229
1434
  init_diagnostics();
1230
1435
  init_colors();
1231
1436
  AUTO_TAG_COLOR_SENTINEL = "";
1232
- autoTagColorCycle = Object.freeze(
1233
- RECOGNIZED_COLOR_NAMES.filter(
1234
- (n) => n !== "gray" && n !== "black" && n !== "white"
1235
- )
1236
- );
1437
+ autoTagColorCycle = CATEGORICAL_COLOR_ORDER;
1237
1438
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
1238
1439
  VALID_TAG_IDENT_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
1239
1440
  }
@@ -1716,7 +1917,12 @@ function parseVisualizationFull(content, palette) {
1716
1917
  }
1717
1918
  if (currentTimelineTagGroup && indent > 0) {
1718
1919
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1719
- const { label, color } = extractColor(entryText, palette);
1920
+ const { label, color } = extractColor(
1921
+ entryText,
1922
+ palette,
1923
+ result.diagnostics,
1924
+ lineNumber
1925
+ );
1720
1926
  if (color) {
1721
1927
  if (isDefault) {
1722
1928
  currentTimelineTagGroup.defaultValue = label;
@@ -3017,13 +3223,16 @@ function contrastText(bg, lightText, darkText) {
3017
3223
  }
3018
3224
  return lightText;
3019
3225
  }
3226
+ function themeBaseBg(palette, isDark) {
3227
+ return isDark ? palette.surface : palette.bg;
3228
+ }
3020
3229
  function shapeFill(palette, intent, isDark, opts) {
3021
3230
  if (opts?.solid) return intent;
3022
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3231
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3023
3232
  }
3024
3233
  function getSeriesColors(palette) {
3025
3234
  const c = palette.colors;
3026
- return [c.blue, c.green, c.yellow, c.orange, c.purple, c.red, c.teal, c.cyan];
3235
+ return CATEGORICAL_COLOR_ORDER.map((name) => c[name]);
3027
3236
  }
3028
3237
  function getSegmentColors(palette, count) {
3029
3238
  if (count <= 0) return [];
@@ -3050,7 +3259,7 @@ function getSegmentColors(palette, count) {
3050
3259
  }
3051
3260
  function politicalTints(palette, count, isDark) {
3052
3261
  if (count <= 0) return [];
3053
- const base = isDark ? palette.surface : palette.bg;
3262
+ const base = themeBaseBg(palette, isDark);
3054
3263
  const c = palette.colors;
3055
3264
  const swatches = [
3056
3265
  .../* @__PURE__ */ new Set([
@@ -3076,6 +3285,7 @@ var POLITICAL_TINT_BANDS;
3076
3285
  var init_color_utils = __esm({
3077
3286
  "src/palettes/color-utils.ts"() {
3078
3287
  "use strict";
3288
+ init_colors();
3079
3289
  POLITICAL_TINT_BANDS = {
3080
3290
  light: [32, 48, 64, 80],
3081
3291
  dark: [44, 58, 72, 86]
@@ -3859,6 +4069,7 @@ __export(palettes_exports, {
3859
4069
  shade: () => shade,
3860
4070
  shapeFill: () => shapeFill,
3861
4071
  slatePalette: () => slatePalette,
4072
+ themeBaseBg: () => themeBaseBg,
3862
4073
  tidewaterPalette: () => tidewaterPalette,
3863
4074
  tint: () => tint,
3864
4075
  tokyoNightPalette: () => tokyoNightPalette
@@ -4073,81 +4284,7 @@ var init_text_measure = __esm({
4073
4284
  });
4074
4285
 
4075
4286
  // src/utils/scaling.ts
4076
- function computeMinDimensions(chartType, counts) {
4077
- switch (chartType) {
4078
- case "sequence":
4079
- return {
4080
- width: Math.max((counts.participants ?? 2) * 80, 320),
4081
- height: Math.max((counts.messages ?? 1) * 20 + 120, 200)
4082
- };
4083
- case "raci":
4084
- return {
4085
- width: Math.max((counts.roles ?? 2) * 50 + 180, 300),
4086
- height: Math.max((counts.tasks ?? 1) * 28 + 80, 200)
4087
- };
4088
- case "mindmap":
4089
- return {
4090
- width: Math.max((counts.nodes ?? 3) * 30, 300),
4091
- height: Math.max((counts.depth ?? 2) * 60, 200)
4092
- };
4093
- case "tech-radar":
4094
- return { width: 360, height: 400 };
4095
- case "heatmap":
4096
- return {
4097
- width: Math.max((counts.columns ?? 3) * 40, 300),
4098
- height: Math.max((counts.rows ?? 3) * 30 + 60, 200)
4099
- };
4100
- case "arc":
4101
- return {
4102
- width: 300,
4103
- height: Math.max((counts.nodes ?? 3) * 20 + 120, 200)
4104
- };
4105
- case "org":
4106
- return {
4107
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4108
- height: Math.max((counts.depth ?? 2) * 80, 200)
4109
- };
4110
- case "gantt":
4111
- return {
4112
- width: 400,
4113
- height: Math.max((counts.tasks ?? 3) * 24 + 80, 200)
4114
- };
4115
- case "kanban":
4116
- return {
4117
- width: Math.max((counts.columns ?? 3) * 120, 360),
4118
- height: 300
4119
- };
4120
- case "er":
4121
- return {
4122
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4123
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4124
- };
4125
- case "class":
4126
- return {
4127
- width: Math.max((counts.nodes ?? 2) * 140, 300),
4128
- height: Math.max((counts.nodes ?? 2) * 80, 200)
4129
- };
4130
- case "flowchart":
4131
- case "state":
4132
- return {
4133
- width: Math.max((counts.nodes ?? 3) * 60, 300),
4134
- height: Math.max((counts.nodes ?? 3) * 50, 200)
4135
- };
4136
- case "pert":
4137
- return {
4138
- width: Math.max((counts.tasks ?? 3) * 80, 340),
4139
- height: Math.max((counts.tasks ?? 3) * 40 + 80, 200)
4140
- };
4141
- case "infra":
4142
- return {
4143
- width: Math.max((counts.nodes ?? 3) * 80, 300),
4144
- height: Math.max((counts.nodes ?? 3) * 60, 200)
4145
- };
4146
- default:
4147
- return { ...DEFAULT_MIN };
4148
- }
4149
- }
4150
- var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext, DEFAULT_MIN;
4287
+ var DEFAULT_MIN_SCALE_FACTOR, TEXT_FLOOR, ScaleContext;
4151
4288
  var init_scaling = __esm({
4152
4289
  "src/utils/scaling.ts"() {
4153
4290
  "use strict";
@@ -4166,6 +4303,30 @@ var init_scaling = __esm({
4166
4303
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4167
4304
  return new _ScaleContext(clamped, minScaleFactor);
4168
4305
  }
4306
+ /**
4307
+ * Fit content into a bounding box, scaling by whichever dimension is more
4308
+ * constraining (the smaller of the width- and height-fit ratios) so the
4309
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4310
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4311
+ * never shrunk past the readability floor).
4312
+ */
4313
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4314
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4315
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4316
+ const raw = Math.min(wRaw, hRaw);
4317
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4318
+ return new _ScaleContext(clamped, minScaleFactor);
4319
+ }
4320
+ /**
4321
+ * Build a context from an explicit raw factor (clamped to
4322
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4323
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4324
+ * can still overflow — re-measure the laid-out result and tighten.
4325
+ */
4326
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4327
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4328
+ return new _ScaleContext(clamped, minScaleFactor);
4329
+ }
4169
4330
  static identity() {
4170
4331
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4171
4332
  }
@@ -4182,7 +4343,6 @@ var init_scaling = __esm({
4182
4343
  return Math.max(fontSize * this.factor, floor);
4183
4344
  }
4184
4345
  };
4185
- DEFAULT_MIN = { width: 300, height: 200 };
4186
4346
  }
4187
4347
  });
4188
4348
 
@@ -5867,12 +6027,7 @@ function parseSequenceDgmo(content, palette) {
5867
6027
  const trimmed = token.trim();
5868
6028
  return nameAliasMap.get(trimmed) ?? trimmed;
5869
6029
  };
5870
- const fail = (line11, message) => {
5871
- const diag = makeDgmoError(line11, message);
5872
- result.diagnostics.push(diag);
5873
- result.error = formatDgmoError(diag);
5874
- return result;
5875
- };
6030
+ const fail = makeFail(result);
5876
6031
  const pushError = (line11, message) => {
5877
6032
  const diag = makeDgmoError(line11, message);
5878
6033
  result.diagnostics.push(diag);
@@ -5887,6 +6042,7 @@ function parseSequenceDgmo(content, palette) {
5887
6042
  const lines = content.split("\n");
5888
6043
  let hasExplicitChart = false;
5889
6044
  let contentStarted = false;
6045
+ let bodyStarted = false;
5890
6046
  let firstLineIndex = -1;
5891
6047
  for (let fi = 0; fi < lines.length; fi++) {
5892
6048
  const fl = lines[fi].trim();
@@ -6198,6 +6354,7 @@ function parseSequenceDgmo(content, palette) {
6198
6354
  );
6199
6355
  }
6200
6356
  contentStarted = true;
6357
+ bodyStarted = true;
6201
6358
  const section = {
6202
6359
  kind: "section",
6203
6360
  // Capture group 1 guaranteed present after successful match.
@@ -6413,7 +6570,7 @@ function parseSequenceDgmo(content, palette) {
6413
6570
  alias: bareAlias
6414
6571
  } = splitPipe(trimmed, lineNumber);
6415
6572
  const inGroup = activeGroup && measureIndent(raw) > 0;
6416
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6573
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6417
6574
  contentStarted = true;
6418
6575
  const id = bareCore;
6419
6576
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6460,6 +6617,7 @@ function parseSequenceDgmo(content, palette) {
6460
6617
  }
6461
6618
  if (labeledArrow) {
6462
6619
  contentStarted = true;
6620
+ bodyStarted = true;
6463
6621
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6464
6622
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6465
6623
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6524,6 +6682,7 @@ function parseSequenceDgmo(content, palette) {
6524
6682
  const bareCall = bareCallSync || bareCallAsync;
6525
6683
  if (bareCall) {
6526
6684
  contentStarted = true;
6685
+ bodyStarted = true;
6527
6686
  const from = bareCall[1];
6528
6687
  const to = bareCall[2];
6529
6688
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6545,6 +6704,7 @@ function parseSequenceDgmo(content, palette) {
6545
6704
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6546
6705
  if (ifMatch) {
6547
6706
  contentStarted = true;
6707
+ bodyStarted = true;
6548
6708
  const block = {
6549
6709
  kind: "block",
6550
6710
  type: "if",
@@ -6561,6 +6721,7 @@ function parseSequenceDgmo(content, palette) {
6561
6721
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6562
6722
  if (loopMatch) {
6563
6723
  contentStarted = true;
6724
+ bodyStarted = true;
6564
6725
  const block = {
6565
6726
  kind: "block",
6566
6727
  type: "loop",
@@ -6577,6 +6738,7 @@ function parseSequenceDgmo(content, palette) {
6577
6738
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6578
6739
  if (parallelMatch) {
6579
6740
  contentStarted = true;
6741
+ bodyStarted = true;
6580
6742
  const block = {
6581
6743
  kind: "block",
6582
6744
  type: "parallel",
@@ -6673,6 +6835,7 @@ function parseSequenceDgmo(content, palette) {
6673
6835
  lineNumber,
6674
6836
  endLineNumber: lineNumber
6675
6837
  };
6838
+ bodyStarted = true;
6676
6839
  currentContainer().push(note);
6677
6840
  continue;
6678
6841
  }
@@ -6697,6 +6860,7 @@ function parseSequenceDgmo(content, palette) {
6697
6860
  endLineNumber: i + 1
6698
6861
  // i has advanced past the body lines (1-based)
6699
6862
  };
6863
+ bodyStarted = true;
6700
6864
  currentContainer().push(note);
6701
6865
  continue;
6702
6866
  }
@@ -7541,6 +7705,15 @@ function parseNodeRef(text) {
7541
7705
  }
7542
7706
  return null;
7543
7707
  }
7708
+ function parseNodeRefLoose(text) {
7709
+ const t = text.trim();
7710
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7711
+ const m = t.match(shapeRe);
7712
+ if (!m) return null;
7713
+ const ref = parseNodeRef(m[1]);
7714
+ if (!ref) return null;
7715
+ return { ref, trailing: m[2].trim() };
7716
+ }
7544
7717
  function splitArrows(line11) {
7545
7718
  const segments = [];
7546
7719
  const arrowPositions = [];
@@ -7636,6 +7809,7 @@ function parseFlowchart(content, palette) {
7636
7809
  const notes = [];
7637
7810
  let contentStarted = false;
7638
7811
  let firstLineParsed = false;
7812
+ let prevLineLastNodeId = null;
7639
7813
  const nameAliasMap = /* @__PURE__ */ new Map();
7640
7814
  function peelAlias2(seg) {
7641
7815
  const trimmed = seg.trim();
@@ -7643,6 +7817,19 @@ function parseFlowchart(content, palette) {
7643
7817
  if (!m) return { seg: trimmed };
7644
7818
  return { seg: m[1].trim(), alias: m[2] };
7645
7819
  }
7820
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7821
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7822
+ if (suffixWarnedLines.has(lineNumber)) return;
7823
+ suffixWarnedLines.add(lineNumber);
7824
+ result.diagnostics.push(
7825
+ makeDgmoError(
7826
+ lineNumber,
7827
+ `Ignored unsupported text after a node shape: "${trailing}". Flowcharts have no tag groups or node metadata; node colors are assigned automatically by shape (e.g. terminals green/red, decisions yellow). Remove the suffix.`,
7828
+ "warning",
7829
+ "W_FLOWCHART_NODE_SUFFIX"
7830
+ )
7831
+ );
7832
+ }
7646
7833
  function getOrCreateNode(ref, lineNumber) {
7647
7834
  const key = ref.id;
7648
7835
  const existing = nodeMap.get(key);
@@ -7701,6 +7888,8 @@ function parseFlowchart(content, palette) {
7701
7888
  indentStack[indentStack.length - 1].nodeId
7702
7889
  ) : null;
7703
7890
  const segments = splitArrows(trimmed);
7891
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7892
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7704
7893
  if (segments.length === 1) {
7705
7894
  const peeled = peelAlias2(segments[0]);
7706
7895
  const ref = parseNodeRef(peeled.seg);
@@ -7710,6 +7899,13 @@ function parseFlowchart(content, palette) {
7710
7899
  indentStack.push({ nodeId: node.id, indent });
7711
7900
  return node.id;
7712
7901
  }
7902
+ const loose = parseNodeRefLoose(peeled.seg);
7903
+ if (loose) {
7904
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7905
+ const node = getOrCreateNode(loose.ref, lineNumber);
7906
+ indentStack.push({ nodeId: node.id, indent });
7907
+ return node.id;
7908
+ }
7713
7909
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7714
7910
  if (aliasResolved !== void 0) {
7715
7911
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7745,16 +7941,23 @@ function parseFlowchart(content, palette) {
7745
7941
  }
7746
7942
  }
7747
7943
  }
7944
+ if (!ref) {
7945
+ const loose = parseNodeRefLoose(peeled.seg);
7946
+ if (loose) {
7947
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7948
+ ref = loose.ref;
7949
+ }
7950
+ }
7748
7951
  if (!ref) continue;
7749
7952
  const node = getOrCreateNode(ref, lineNumber);
7750
7953
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7751
7954
  if (pendingArrow !== null) {
7752
- const sourceId = lastNodeId ?? implicitSourceId;
7955
+ const sourceId = lastNodeId ?? effectiveSource;
7753
7956
  if (sourceId) {
7754
7957
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7755
7958
  }
7756
7959
  pendingArrow = null;
7757
- } else if (lastNodeId === null && implicitSourceId === null) {
7960
+ } else if (lastNodeId === null && effectiveSource === null) {
7758
7961
  }
7759
7962
  lastNodeId = node.id;
7760
7963
  }
@@ -7836,7 +8039,8 @@ function parseFlowchart(content, palette) {
7836
8039
  continue;
7837
8040
  }
7838
8041
  }
7839
- processContentLine(trimmed, lineNumber, indent);
8042
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8043
+ if (lastId) prevLineLastNodeId = lastId;
7840
8044
  }
7841
8045
  if (result.nodes.length === 0 && !result.error) {
7842
8046
  const diag = makeDgmoError(
@@ -8919,7 +9123,12 @@ function parseERDiagram(content, palette) {
8919
9123
  }
8920
9124
  if (currentTagGroup && !contentStarted && indent > 0) {
8921
9125
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8922
- const { label, color } = extractColor(cleanEntry, palette);
9126
+ const { label, color } = extractColor(
9127
+ cleanEntry,
9128
+ palette,
9129
+ result.diagnostics,
9130
+ lineNumber
9131
+ );
8923
9132
  if (isDefault) {
8924
9133
  currentTagGroup.defaultValue = label;
8925
9134
  } else if (currentTagGroup.entries.length === 0) {
@@ -9237,12 +9446,7 @@ function parseOrg(content, palette) {
9237
9446
  diagnostics: [],
9238
9447
  error: null
9239
9448
  };
9240
- const fail = (line11, message) => {
9241
- const diag = makeDgmoError(line11, message);
9242
- result.diagnostics.push(diag);
9243
- result.error = formatDgmoError(diag);
9244
- return result;
9245
- };
9449
+ const fail = makeFail(result);
9246
9450
  const pushError = (line11, message) => {
9247
9451
  const diag = makeDgmoError(line11, message);
9248
9452
  result.diagnostics.push(diag);
@@ -9351,7 +9555,12 @@ function parseOrg(content, palette) {
9351
9555
  const indent2 = measureIndent(line11);
9352
9556
  if (indent2 > 0) {
9353
9557
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9354
- const { label, color } = extractColor(cleanEntry, palette);
9558
+ const { label, color } = extractColor(
9559
+ cleanEntry,
9560
+ palette,
9561
+ result.diagnostics,
9562
+ lineNumber
9563
+ );
9355
9564
  if (isDefault) {
9356
9565
  currentTagGroup.defaultValue = label;
9357
9566
  } else if (currentTagGroup.entries.length === 0) {
@@ -9637,12 +9846,7 @@ function parseSitemap(content, palette) {
9637
9846
  diagnostics: [],
9638
9847
  error: null
9639
9848
  };
9640
- const fail = (line11, message) => {
9641
- const diag = makeDgmoError(line11, message);
9642
- result.diagnostics.push(diag);
9643
- result.error = formatDgmoError(diag);
9644
- return result;
9645
- };
9849
+ const fail = makeFail(result);
9646
9850
  const pushError = (line11, message) => {
9647
9851
  const diag = makeDgmoError(line11, message);
9648
9852
  result.diagnostics.push(diag);
@@ -9743,7 +9947,12 @@ function parseSitemap(content, palette) {
9743
9947
  const indent2 = measureIndent(line11);
9744
9948
  if (indent2 > 0) {
9745
9949
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9746
- const { label, color } = extractColor(cleanEntry, palette);
9950
+ const { label, color } = extractColor(
9951
+ cleanEntry,
9952
+ palette,
9953
+ result.diagnostics,
9954
+ lineNumber
9955
+ );
9747
9956
  currentTagGroup.entries.push({
9748
9957
  value: label,
9749
9958
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11458,7 +11667,7 @@ var init_chart_types = __esm({
11458
11667
  },
11459
11668
  {
11460
11669
  id: "sequence",
11461
- description: "Message / interaction flows",
11670
+ description: "Message request and response interaction flows",
11462
11671
  fallback: true
11463
11672
  },
11464
11673
  {
@@ -11549,7 +11758,7 @@ var init_chart_types = __esm({
11549
11758
  },
11550
11759
  {
11551
11760
  id: "slope",
11552
- description: "Change between two periods"
11761
+ description: "Change between 2 time periods"
11553
11762
  },
11554
11763
  {
11555
11764
  id: "sankey",
@@ -11578,7 +11787,7 @@ var init_chart_types = __esm({
11578
11787
  // ── Tier 4 — General-purpose data charts ──────────────────
11579
11788
  {
11580
11789
  id: "bar",
11581
- description: "Categorical comparisons",
11790
+ description: "Categorical comparisons for 3 - 5 figures",
11582
11791
  fallback: true
11583
11792
  },
11584
11793
  {
@@ -11840,7 +12049,9 @@ function parseChart(content, palette) {
11840
12049
  if (dataValues) {
11841
12050
  const { label: rawLabel, color: pointColor } = extractColor(
11842
12051
  dataValues.label,
11843
- palette
12052
+ palette,
12053
+ result.diagnostics,
12054
+ lineNumber
11844
12055
  );
11845
12056
  const [first, ...rest] = dataValues.values;
11846
12057
  result.data.push({
@@ -11905,6 +12116,12 @@ function parseChart(content, palette) {
11905
12116
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11906
12117
  );
11907
12118
  }
12119
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12120
+ warn(
12121
+ result.seriesLineNumber ?? 1,
12122
+ `Plain "bar" shows only the first series ("${result.seriesNames[0]}"); the other ${result.seriesNames.length - 1} are dropped at render. Use "bar-stacked" for stacked bars or "multi-line" to plot every series.`
12123
+ );
12124
+ }
11908
12125
  if (!result.error && result.seriesNames) {
11909
12126
  const expectedCount = result.seriesNames.length;
11910
12127
  for (const dp of result.data) {
@@ -12175,7 +12392,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12175
12392
  if (!dataRow || dataRow.values.length < 2) return null;
12176
12393
  const { label: rawLabel, color: pointColor } = extractColor(
12177
12394
  dataRow.label,
12178
- palette
12395
+ palette,
12396
+ diagnostics,
12397
+ lineNumber
12179
12398
  );
12180
12399
  return {
12181
12400
  name: rawLabel,
@@ -12313,11 +12532,15 @@ function parseExtendedChartFull(content, palette) {
12313
12532
  const targetResolved = resolveSlot(rawTarget);
12314
12533
  const { label: source, color: sourceColor } = extractColor(
12315
12534
  sourceResolved,
12316
- palette
12535
+ palette,
12536
+ result.diagnostics,
12537
+ lineNumber
12317
12538
  );
12318
12539
  const { label: target, color: targetColor } = extractColor(
12319
12540
  targetResolved,
12320
- palette
12541
+ palette,
12542
+ result.diagnostics,
12543
+ lineNumber
12321
12544
  );
12322
12545
  if (sourceColor || targetColor) {
12323
12546
  if (!result.nodeColors) result.nodeColors = {};
@@ -12369,7 +12592,9 @@ function parseExtendedChartFull(content, palette) {
12369
12592
  const targetResolved = resolveSlot(dataRow2.label);
12370
12593
  const { label: target, color: targetColor } = extractColor(
12371
12594
  targetResolved,
12372
- palette
12595
+ palette,
12596
+ result.diagnostics,
12597
+ lineNumber
12373
12598
  );
12374
12599
  if (targetColor) {
12375
12600
  if (!result.nodeColors) result.nodeColors = {};
@@ -12402,7 +12627,9 @@ function parseExtendedChartFull(content, palette) {
12402
12627
  const trimmedResolved = resolveSlot(trimmed);
12403
12628
  const { label: nodeName, color: nodeColor2 } = extractColor(
12404
12629
  trimmedResolved,
12405
- palette
12630
+ palette,
12631
+ result.diagnostics,
12632
+ lineNumber
12406
12633
  );
12407
12634
  if (nodeColor2) {
12408
12635
  if (!result.nodeColors) result.nodeColors = {};
@@ -12492,8 +12719,11 @@ function parseExtendedChartFull(content, palette) {
12492
12719
  min: parseFloat(rangeMatch[1]),
12493
12720
  max: parseFloat(rangeMatch[2])
12494
12721
  };
12722
+ continue;
12723
+ }
12724
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12725
+ continue;
12495
12726
  }
12496
- continue;
12497
12727
  }
12498
12728
  }
12499
12729
  if (firstToken === "no-name") {
@@ -12553,7 +12783,9 @@ function parseExtendedChartFull(content, palette) {
12553
12783
  if (colonIndex >= 0) {
12554
12784
  const { label: fnName, color: fnColor } = extractColor(
12555
12785
  trimmed.substring(0, colonIndex).trim(),
12556
- palette
12786
+ palette,
12787
+ result.diagnostics,
12788
+ lineNumber
12557
12789
  );
12558
12790
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12559
12791
  if (!result.functions) result.functions = [];
@@ -12601,7 +12833,9 @@ function parseExtendedChartFull(content, palette) {
12601
12833
  if (dataRow?.values.length === 1) {
12602
12834
  const { label: rawLabel, color: pointColor } = extractColor(
12603
12835
  dataRow.label,
12604
- palette
12836
+ palette,
12837
+ result.diagnostics,
12838
+ lineNumber
12605
12839
  );
12606
12840
  result.data.push({
12607
12841
  label: rawLabel,
@@ -12700,11 +12934,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12700
12934
  const sc = ctx ?? ScaleContext.identity();
12701
12935
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12702
12936
  if (parsed.type === "sankey") {
12703
- const bg = isDark ? palette.surface : palette.bg;
12937
+ const bg = themeBaseBg(palette, isDark);
12704
12938
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12705
12939
  }
12706
12940
  if (parsed.type === "chord") {
12707
- const bg = isDark ? palette.surface : palette.bg;
12941
+ const bg = themeBaseBg(palette, isDark);
12708
12942
  return buildChordOption(
12709
12943
  parsed,
12710
12944
  palette,
@@ -12745,7 +12979,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12745
12979
  );
12746
12980
  }
12747
12981
  if (parsed.type === "funnel") {
12748
- const bg = isDark ? palette.surface : palette.bg;
12982
+ const bg = themeBaseBg(palette, isDark);
12749
12983
  return buildFunnelOption(
12750
12984
  parsed,
12751
12985
  palette,
@@ -13341,7 +13575,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13341
13575
  const gridLeft = parsed.ylabel ? 12 : 3;
13342
13576
  const gridRight = 4;
13343
13577
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13344
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13578
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13579
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13345
13580
  let graphic;
13346
13581
  if (showLabels && points.length > 0) {
13347
13582
  const labelPoints = [];
@@ -13481,7 +13716,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13481
13716
  }
13482
13717
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13483
13718
  const sc = ctx ?? ScaleContext.identity();
13484
- const bg = isDark ? palette.surface : palette.bg;
13719
+ const bg = themeBaseBg(palette, isDark);
13485
13720
  const heatmapRows = parsed.heatmapRows ?? [];
13486
13721
  const columns = parsed.columns ?? [];
13487
13722
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13800,7 +14035,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13800
14035
  colors,
13801
14036
  titleConfig
13802
14037
  } = buildChartCommons(parsed, palette, isDark, sc);
13803
- const bg = isDark ? palette.surface : palette.bg;
14038
+ const bg = themeBaseBg(palette, isDark);
13804
14039
  switch (parsed.type) {
13805
14040
  case "bar":
13806
14041
  return buildBarOption(
@@ -14748,12 +14983,7 @@ function parseKanban(content, palette) {
14748
14983
  diagnostics: [],
14749
14984
  error: null
14750
14985
  };
14751
- const fail = (line11, message) => {
14752
- const diag = makeDgmoError(line11, message);
14753
- result.diagnostics.push(diag);
14754
- result.error = formatDgmoError(diag);
14755
- return result;
14756
- };
14986
+ const fail = makeFail(result);
14757
14987
  const warn = (line11, message) => {
14758
14988
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14759
14989
  };
@@ -14856,7 +15086,12 @@ function parseKanban(content, palette) {
14856
15086
  const indent2 = measureIndent(line11);
14857
15087
  if (indent2 > 0) {
14858
15088
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14859
- const { label, color } = extractColor(cleanEntry, palette);
15089
+ const { label, color } = extractColor(
15090
+ cleanEntry,
15091
+ palette,
15092
+ result.diagnostics,
15093
+ lineNumber
15094
+ );
14860
15095
  if (isDefault) {
14861
15096
  currentTagGroup.defaultValue = label;
14862
15097
  } else if (currentTagGroup.entries.length === 0) {
@@ -15200,12 +15435,7 @@ function parseC4(content, palette) {
15200
15435
  if (!result.error && severity === "error")
15201
15436
  result.error = formatDgmoError(diag);
15202
15437
  };
15203
- const fail = (line11, message) => {
15204
- const diag = makeDgmoError(line11, message);
15205
- result.diagnostics.push(diag);
15206
- result.error = formatDgmoError(diag);
15207
- return result;
15208
- };
15438
+ const fail = makeFail(result);
15209
15439
  if (!content?.trim()) {
15210
15440
  return fail(0, "No content provided");
15211
15441
  }
@@ -15306,7 +15536,12 @@ function parseC4(content, palette) {
15306
15536
  const indent2 = measureIndent(line11);
15307
15537
  if (indent2 > 0) {
15308
15538
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15309
- const { label, color } = extractColor(cleanEntry, palette);
15539
+ const { label, color } = extractColor(
15540
+ cleanEntry,
15541
+ palette,
15542
+ result.diagnostics,
15543
+ lineNumber
15544
+ );
15310
15545
  if (isDefault) {
15311
15546
  currentTagGroup.defaultValue = label;
15312
15547
  } else if (currentTagGroup.entries.length === 0) {
@@ -16971,8 +17206,66 @@ function parseInfra(content) {
16971
17206
  }
16972
17207
  }
16973
17208
  validateTagGroupNames(result.tagGroups, warn, setError);
17209
+ checkReachability(result);
16974
17210
  return result;
16975
17211
  }
17212
+ function checkReachability(result) {
17213
+ if (result.nodes.length === 0) return;
17214
+ const entries = result.nodes.filter((n) => n.isEdge);
17215
+ if (entries.length === 0) {
17216
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17217
+ result.diagnostics.push(
17218
+ makeDgmoError(
17219
+ line11,
17220
+ `Infra diagram has no 'internet' or 'edge' entry point \u2014 an infra diagram traces request traffic from an entry inward, so without one nothing carries traffic. Add an 'internet' or 'edge' node and route from it.`,
17221
+ "warning",
17222
+ "W_INFRA_NO_ENTRY"
17223
+ )
17224
+ );
17225
+ return;
17226
+ }
17227
+ const groupChildMap = /* @__PURE__ */ new Map();
17228
+ for (const node of result.nodes) {
17229
+ if (node.groupId) {
17230
+ const list = groupChildMap.get(node.groupId) ?? [];
17231
+ list.push(node.id);
17232
+ groupChildMap.set(node.groupId, list);
17233
+ }
17234
+ }
17235
+ const outbound = /* @__PURE__ */ new Map();
17236
+ for (const edge of result.edges) {
17237
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17238
+ const list = outbound.get(edge.sourceId) ?? [];
17239
+ list.push(...targets);
17240
+ outbound.set(edge.sourceId, list);
17241
+ }
17242
+ const reachable = /* @__PURE__ */ new Set();
17243
+ const queue = [];
17244
+ for (const entry of entries) {
17245
+ reachable.add(entry.id);
17246
+ queue.push(entry.id);
17247
+ }
17248
+ while (queue.length > 0) {
17249
+ const current = queue.shift();
17250
+ for (const next of outbound.get(current) ?? []) {
17251
+ if (!reachable.has(next)) {
17252
+ reachable.add(next);
17253
+ queue.push(next);
17254
+ }
17255
+ }
17256
+ }
17257
+ for (const node of result.nodes) {
17258
+ if (node.isEdge || reachable.has(node.id)) continue;
17259
+ result.diagnostics.push(
17260
+ makeDgmoError(
17261
+ node.lineNumber,
17262
+ `'${node.label}' is unreachable from an 'internet'/'edge' entry \u2014 no request traffic flows to it, so it's dead on an infra diagram. Connect it downstream of an entry, or remove it.`,
17263
+ "warning",
17264
+ "W_INFRA_UNREACHABLE"
17265
+ )
17266
+ );
17267
+ }
17268
+ }
16976
17269
  function stripNodeDecorations(name) {
16977
17270
  let s = name.trim();
16978
17271
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17262,7 +17555,12 @@ function parseGantt(content, palette) {
17262
17555
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17263
17556
  if (eraEntryMatch) {
17264
17557
  const eraLabelRaw = eraEntryMatch[3].trim();
17265
- const eraExtracted = extractColor(eraLabelRaw, palette);
17558
+ const eraExtracted = extractColor(
17559
+ eraLabelRaw,
17560
+ palette,
17561
+ diagnostics,
17562
+ lineNumber
17563
+ );
17266
17564
  result.eras.push({
17267
17565
  startDate: eraEntryMatch[1],
17268
17566
  endDate: eraEntryMatch[2],
@@ -17284,7 +17582,12 @@ function parseGantt(content, palette) {
17284
17582
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17285
17583
  if (markerEntryMatch) {
17286
17584
  const markerLabelRaw = markerEntryMatch[2].trim();
17287
- const markerExtracted = extractColor(markerLabelRaw, palette);
17585
+ const markerExtracted = extractColor(
17586
+ markerLabelRaw,
17587
+ palette,
17588
+ diagnostics,
17589
+ lineNumber
17590
+ );
17288
17591
  result.markers.push({
17289
17592
  date: markerEntryMatch[1],
17290
17593
  label: markerExtracted.label,
@@ -17305,7 +17608,12 @@ function parseGantt(content, palette) {
17305
17608
  } else {
17306
17609
  if (COMMENT_RE.test(line11)) continue;
17307
17610
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17308
- const extracted = extractColor(cleanEntry, palette);
17611
+ const extracted = extractColor(
17612
+ cleanEntry,
17613
+ palette,
17614
+ diagnostics,
17615
+ lineNumber
17616
+ );
17309
17617
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17310
17618
  const isFirstEntry = currentTagGroup.entries.length === 0;
17311
17619
  currentTagGroup.entries.push({
@@ -17500,7 +17808,12 @@ function parseGantt(content, palette) {
17500
17808
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17501
17809
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17502
17810
  const eraLabelRaw = eraOffsetMatch[3].trim();
17503
- const eraExtracted = extractColor(eraLabelRaw, palette);
17811
+ const eraExtracted = extractColor(
17812
+ eraLabelRaw,
17813
+ palette,
17814
+ diagnostics,
17815
+ lineNumber
17816
+ );
17504
17817
  result.eras.push({
17505
17818
  startDate: "",
17506
17819
  endDate: "",
@@ -17516,7 +17829,12 @@ function parseGantt(content, palette) {
17516
17829
  if (markerOffsetMatch) {
17517
17830
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17518
17831
  const markerLabelRaw = markerOffsetMatch[2].trim();
17519
- const markerExtracted = extractColor(markerLabelRaw, palette);
17832
+ const markerExtracted = extractColor(
17833
+ markerLabelRaw,
17834
+ palette,
17835
+ diagnostics,
17836
+ lineNumber
17837
+ );
17520
17838
  result.markers.push({
17521
17839
  date: "",
17522
17840
  label: markerExtracted.label,
@@ -17590,7 +17908,12 @@ function parseGantt(content, palette) {
17590
17908
  const eraMatch = line11.match(ERA_RE);
17591
17909
  if (eraMatch) {
17592
17910
  const eraLabelRaw = eraMatch[3].trim();
17593
- const eraExtracted = extractColor(eraLabelRaw, palette);
17911
+ const eraExtracted = extractColor(
17912
+ eraLabelRaw,
17913
+ palette,
17914
+ diagnostics,
17915
+ lineNumber
17916
+ );
17594
17917
  result.eras.push({
17595
17918
  startDate: eraMatch[1],
17596
17919
  endDate: eraMatch[2],
@@ -17608,7 +17931,12 @@ function parseGantt(content, palette) {
17608
17931
  const markerMatch = line11.match(MARKER_RE);
17609
17932
  if (markerMatch) {
17610
17933
  const markerLabelRaw = markerMatch[2].trim();
17611
- const markerExtracted = extractColor(markerLabelRaw, palette);
17934
+ const markerExtracted = extractColor(
17935
+ markerLabelRaw,
17936
+ palette,
17937
+ diagnostics,
17938
+ lineNumber
17939
+ );
17612
17940
  result.markers.push({
17613
17941
  date: markerMatch[1],
17614
17942
  label: markerExtracted.label,
@@ -19005,6 +19333,7 @@ function parseBoxesAndLines(content, palette) {
19005
19333
  const nodes = [];
19006
19334
  const edges = [];
19007
19335
  const groups = [];
19336
+ const nodePositions = /* @__PURE__ */ new Map();
19008
19337
  const result = {
19009
19338
  type: "boxes-and-lines",
19010
19339
  title: null,
@@ -19038,6 +19367,8 @@ function parseBoxesAndLines(content, palette) {
19038
19367
  }
19039
19368
  const groupStack = [];
19040
19369
  let contentStarted = false;
19370
+ let inLayoutBlock = false;
19371
+ const LAYOUT_ENTRY_RE = /^(.+?):\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/;
19041
19372
  let currentTagGroup = null;
19042
19373
  const metaAliasMap = /* @__PURE__ */ new Map();
19043
19374
  const nameAliasMap = /* @__PURE__ */ new Map();
@@ -19077,7 +19408,7 @@ function parseBoxesAndLines(content, palette) {
19077
19408
  const trimmed = raw.trim();
19078
19409
  const indent = measureIndent2(raw);
19079
19410
  if (!trimmed || trimmed.startsWith("//")) continue;
19080
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19411
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
19081
19412
  result.diagnostics.push(
19082
19413
  makeDgmoError(
19083
19414
  lineNum,
@@ -19206,7 +19537,12 @@ function parseBoxesAndLines(content, palette) {
19206
19537
  if (tagBlockMatch.inlineValues) {
19207
19538
  for (const rawVal of tagBlockMatch.inlineValues) {
19208
19539
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19209
- const { label, color } = extractColor(cleanVal);
19540
+ const { label, color } = extractColor(
19541
+ cleanVal,
19542
+ palette,
19543
+ result.diagnostics,
19544
+ lineNum
19545
+ );
19210
19546
  newTagGroup.entries.push({
19211
19547
  value: label,
19212
19548
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19223,7 +19559,12 @@ function parseBoxesAndLines(content, palette) {
19223
19559
  }
19224
19560
  if (currentTagGroup && !contentStarted && indent > 0) {
19225
19561
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19226
- const { label, color } = extractColor(cleanEntry);
19562
+ const { label, color } = extractColor(
19563
+ cleanEntry,
19564
+ palette,
19565
+ result.diagnostics,
19566
+ lineNum
19567
+ );
19227
19568
  currentTagGroup.entries.push({
19228
19569
  value: label,
19229
19570
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19239,6 +19580,42 @@ function parseBoxesAndLines(content, palette) {
19239
19580
  if (currentTagGroup && indent === 0) {
19240
19581
  currentTagGroup = null;
19241
19582
  }
19583
+ if (!inLayoutBlock && indent === 0 && trimmed === "layout") {
19584
+ let isBlock = false;
19585
+ for (let j = i + 1; j < lines.length; j++) {
19586
+ const peek = lines[j];
19587
+ if (!peek.trim()) continue;
19588
+ isBlock = measureIndent2(peek) > 0 && LAYOUT_ENTRY_RE.test(peek.trim());
19589
+ break;
19590
+ }
19591
+ if (isBlock) {
19592
+ flushDescription();
19593
+ closeGroupsToIndent(0);
19594
+ inLayoutBlock = true;
19595
+ continue;
19596
+ }
19597
+ }
19598
+ if (inLayoutBlock) {
19599
+ if (indent > 0) {
19600
+ const lm = trimmed.match(LAYOUT_ENTRY_RE);
19601
+ if (lm) {
19602
+ nodePositions.set(lm[1].trim(), {
19603
+ x: Number(lm[2]),
19604
+ y: Number(lm[3])
19605
+ });
19606
+ } else {
19607
+ result.diagnostics.push(
19608
+ makeDgmoError(
19609
+ lineNum,
19610
+ `Invalid layout entry "${trimmed}" \u2014 expected "<node-id>: <x>, <y>"`,
19611
+ "warning"
19612
+ )
19613
+ );
19614
+ }
19615
+ continue;
19616
+ }
19617
+ inLayoutBlock = false;
19618
+ }
19242
19619
  if (descState !== null) {
19243
19620
  if (indent > descState.indent) {
19244
19621
  if (trimmed.includes("->") || trimmed.includes("<->")) {
@@ -19491,6 +19868,22 @@ function parseBoxesAndLines(content, palette) {
19491
19868
  );
19492
19869
  }
19493
19870
  finalizeAutoTagColors(result.tagGroups);
19871
+ if (nodePositions.size > 0) {
19872
+ const nodeLabelSet = new Set(result.nodes.map((n) => n.label));
19873
+ for (const id of nodePositions.keys()) {
19874
+ if (!nodeLabelSet.has(id)) {
19875
+ pushWarning(0, `layout entry for unknown node "${id}" (ignored)`);
19876
+ }
19877
+ }
19878
+ const unpositioned = result.nodes.filter((n) => !nodePositions.has(n.label)).map((n) => n.label);
19879
+ if (unpositioned.length > 0) {
19880
+ pushWarning(
19881
+ 0,
19882
+ `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`
19883
+ );
19884
+ }
19885
+ result.nodePositions = nodePositions;
19886
+ }
19494
19887
  if (result.tagGroups.length > 0) {
19495
19888
  injectDefaultTagMetadata(result.nodes, result.tagGroups);
19496
19889
  validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
@@ -19769,7 +20162,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19769
20162
  metadata
19770
20163
  };
19771
20164
  }
19772
- var MAX_GROUP_DEPTH;
20165
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19773
20166
  var init_parser18 = __esm({
19774
20167
  "src/boxes-and-lines/parser.ts"() {
19775
20168
  "use strict";
@@ -19781,6 +20174,8 @@ var init_parser18 = __esm({
19781
20174
  init_reserved_key_registry();
19782
20175
  init_notes();
19783
20176
  MAX_GROUP_DEPTH = 2;
20177
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20178
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19784
20179
  }
19785
20180
  });
19786
20181
 
@@ -19800,12 +20195,7 @@ function parseMindmap(content, palette) {
19800
20195
  diagnostics: [],
19801
20196
  error: null
19802
20197
  };
19803
- const fail = (line11, message) => {
19804
- const diag = makeDgmoError(line11, message);
19805
- result.diagnostics.push(diag);
19806
- result.error = formatDgmoError(diag);
19807
- return result;
19808
- };
20198
+ const fail = makeFail(result);
19809
20199
  const pushError = (line11, message) => {
19810
20200
  const diag = makeDgmoError(line11, message);
19811
20201
  result.diagnostics.push(diag);
@@ -19918,7 +20308,12 @@ function parseMindmap(content, palette) {
19918
20308
  const indent2 = measureIndent(line11);
19919
20309
  if (indent2 > 0) {
19920
20310
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19921
- const { label, color } = extractColor(cleanEntry, palette);
20311
+ const { label, color } = extractColor(
20312
+ cleanEntry,
20313
+ palette,
20314
+ result.diagnostics,
20315
+ lineNumber
20316
+ );
19922
20317
  if (isDefault) {
19923
20318
  currentTagGroup.defaultValue = label;
19924
20319
  } else if (currentTagGroup.entries.length === 0) {
@@ -19995,6 +20390,7 @@ function parseMindmap(content, palette) {
19995
20390
  collectAll(result.roots);
19996
20391
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
19997
20392
  validateTagGroupNames(result.tagGroups, pushWarning);
20393
+ cascadeTagMetadata(result.roots, result.tagGroups);
19998
20394
  }
19999
20395
  if (result.roots.length === 0 && !result.error) {
20000
20396
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20601,7 +20997,12 @@ function parseWireframe(content) {
20601
20997
  }
20602
20998
  if (indent > 0 && currentTagGroup) {
20603
20999
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20604
- const { label, color } = extractColor(cleanEntry);
21000
+ const { label, color } = extractColor(
21001
+ cleanEntry,
21002
+ void 0,
21003
+ diagnostics,
21004
+ lineNumber
21005
+ );
20605
21006
  currentTagGroup.entries.push({
20606
21007
  value: label,
20607
21008
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20849,12 +21250,7 @@ function parseTechRadar(content) {
20849
21250
  diagnostics: [],
20850
21251
  error: null
20851
21252
  };
20852
- const fail = (line11, message) => {
20853
- const diag = makeDgmoError(line11, message);
20854
- result.diagnostics.push(diag);
20855
- result.error = formatDgmoError(diag);
20856
- return result;
20857
- };
21253
+ const fail = makeFail(result);
20858
21254
  const warn = (line11, message) => {
20859
21255
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20860
21256
  };
@@ -21222,12 +21618,7 @@ function parseCycle(content) {
21222
21618
  let state = "top";
21223
21619
  let currentNode = null;
21224
21620
  let currentEdge = null;
21225
- const fail = (line11, message) => {
21226
- const diag = makeDgmoError(line11, message);
21227
- result.diagnostics.push(diag);
21228
- result.error = formatDgmoError(diag);
21229
- return result;
21230
- };
21621
+ const fail = makeFail(result);
21231
21622
  const warn = (line11, message, severity = "warning") => {
21232
21623
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21233
21624
  };
@@ -21504,12 +21895,7 @@ function parseJourneyMap(content, palette) {
21504
21895
  diagnostics: [],
21505
21896
  error: null
21506
21897
  };
21507
- const fail = (line11, message) => {
21508
- const diag = makeDgmoError(line11, message);
21509
- result.diagnostics.push(diag);
21510
- result.error = formatDgmoError(diag);
21511
- return result;
21512
- };
21898
+ const fail = makeFail(result);
21513
21899
  const warn = (line11, message) => {
21514
21900
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21515
21901
  };
@@ -21591,7 +21977,18 @@ function parseJourneyMap(content, palette) {
21591
21977
  }
21592
21978
  }
21593
21979
  } else {
21594
- personaName = afterKeyword;
21980
+ const colorMatch = afterKeyword.match(/^(.+?)\s+color:\s*(\S+)$/i);
21981
+ if (colorMatch) {
21982
+ personaName = colorMatch[1].trim();
21983
+ personaColor = resolveColorWithDiagnostic(
21984
+ colorMatch[2],
21985
+ lineNumber,
21986
+ result.diagnostics,
21987
+ palette
21988
+ ) ?? void 0;
21989
+ } else {
21990
+ personaName = afterKeyword;
21991
+ }
21595
21992
  }
21596
21993
  if (!personaName) {
21597
21994
  return fail(lineNumber, "persona requires a name");
@@ -21647,7 +22044,12 @@ function parseJourneyMap(content, palette) {
21647
22044
  if (currentTagGroup && !contentStarted) {
21648
22045
  if (indent > 0) {
21649
22046
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21650
- const { label, color } = extractColor(cleanEntry, palette);
22047
+ const { label, color } = extractColor(
22048
+ cleanEntry,
22049
+ palette,
22050
+ result.diagnostics,
22051
+ lineNumber
22052
+ );
21651
22053
  if (isDefault) {
21652
22054
  currentTagGroup.defaultValue = label;
21653
22055
  } else if (currentTagGroup.entries.length === 0) {
@@ -22019,12 +22421,7 @@ function parsePyramid(content) {
22019
22421
  const lines = content.split("\n");
22020
22422
  let headerParsed = false;
22021
22423
  let currentLayer = null;
22022
- const fail = (line11, message) => {
22023
- const diag = makeDgmoError(line11, message);
22024
- result.diagnostics.push(diag);
22025
- result.error = formatDgmoError(diag);
22026
- return result;
22027
- };
22424
+ const fail = makeFail(result);
22028
22425
  const warn = (line11, message, severity = "warning") => {
22029
22426
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22030
22427
  };
@@ -22171,12 +22568,7 @@ function parseRing(content) {
22171
22568
  const lines = content.split("\n");
22172
22569
  let headerParsed = false;
22173
22570
  let currentLayer = null;
22174
- const fail = (line11, message) => {
22175
- const diag = makeDgmoError(line11, message);
22176
- result.diagnostics.push(diag);
22177
- result.error = formatDgmoError(diag);
22178
- return result;
22179
- };
22571
+ const fail = makeFail(result);
22180
22572
  const warn = (line11, message, severity = "warning") => {
22181
22573
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22182
22574
  };
@@ -22640,12 +23032,7 @@ function parseRaci(content, palette) {
22640
23032
  diagnostics: [],
22641
23033
  error: null
22642
23034
  };
22643
- const fail = (line11, message) => {
22644
- const diag = makeDgmoError(line11, message);
22645
- result.diagnostics.push(diag);
22646
- result.error = formatDgmoError(diag);
22647
- return result;
22648
- };
23035
+ const fail = makeFail(result);
22649
23036
  const warn = (line11, message, code) => {
22650
23037
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22651
23038
  };
@@ -23248,6 +23635,81 @@ function measureInfra(content) {
23248
23635
  const parsed = parseInfra(content);
23249
23636
  return { nodes: parsed.nodes.length };
23250
23637
  }
23638
+ function minDimsSequence(c) {
23639
+ return {
23640
+ width: Math.max((c.participants ?? 2) * 80, 320),
23641
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23642
+ };
23643
+ }
23644
+ function minDimsRaci(c) {
23645
+ return {
23646
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23647
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23648
+ };
23649
+ }
23650
+ function minDimsMindmap(c) {
23651
+ return {
23652
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23653
+ height: Math.max((c.depth ?? 2) * 60, 200)
23654
+ };
23655
+ }
23656
+ function minDimsTechRadar() {
23657
+ return { width: 360, height: 400 };
23658
+ }
23659
+ function minDimsHeatmap(c) {
23660
+ return {
23661
+ width: Math.max((c.columns ?? 3) * 40, 300),
23662
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23663
+ };
23664
+ }
23665
+ function minDimsArc(c) {
23666
+ return {
23667
+ width: 300,
23668
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23669
+ };
23670
+ }
23671
+ function minDimsOrg(c) {
23672
+ return {
23673
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23674
+ height: Math.max((c.depth ?? 2) * 80, 200)
23675
+ };
23676
+ }
23677
+ function minDimsGantt(c) {
23678
+ return {
23679
+ width: 400,
23680
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23681
+ };
23682
+ }
23683
+ function minDimsKanban(c) {
23684
+ return {
23685
+ width: Math.max((c.columns ?? 3) * 120, 360),
23686
+ height: 300
23687
+ };
23688
+ }
23689
+ function minDimsEntities(c) {
23690
+ return {
23691
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23692
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23693
+ };
23694
+ }
23695
+ function minDimsGraph(c) {
23696
+ return {
23697
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23698
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23699
+ };
23700
+ }
23701
+ function minDimsPert(c) {
23702
+ return {
23703
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23704
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23705
+ };
23706
+ }
23707
+ function minDimsInfra(c) {
23708
+ return {
23709
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23710
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23711
+ };
23712
+ }
23251
23713
  function isExtendedChartParser(parse) {
23252
23714
  return EXTENDED_CHART_DOORS.has(parse);
23253
23715
  }
@@ -23291,33 +23753,50 @@ var init_chart_type_registry = __esm({
23291
23753
  id: "sequence",
23292
23754
  category: "diagram",
23293
23755
  parse: parseSequenceDgmo,
23294
- measure: measureSequence
23756
+ measure: measureSequence,
23757
+ minDims: minDimsSequence
23295
23758
  },
23296
23759
  {
23297
23760
  id: "flowchart",
23298
23761
  category: "diagram",
23299
23762
  parse: parseFlowchart,
23300
- measure: measureFlowchart
23763
+ measure: measureFlowchart,
23764
+ minDims: minDimsGraph
23301
23765
  },
23302
23766
  {
23303
23767
  id: "class",
23304
23768
  category: "diagram",
23305
23769
  parse: parseClassDiagram,
23306
- measure: measureClass
23770
+ measure: measureClass,
23771
+ minDims: minDimsEntities
23772
+ },
23773
+ {
23774
+ id: "er",
23775
+ category: "diagram",
23776
+ parse: parseERDiagram,
23777
+ measure: measureER,
23778
+ minDims: minDimsEntities
23307
23779
  },
23308
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23309
23780
  {
23310
23781
  id: "state",
23311
23782
  category: "diagram",
23312
23783
  parse: parseState,
23313
- measure: measureStateGraph
23784
+ measure: measureStateGraph,
23785
+ minDims: minDimsGraph
23786
+ },
23787
+ {
23788
+ id: "org",
23789
+ category: "diagram",
23790
+ parse: parseOrg,
23791
+ measure: measureOrg,
23792
+ minDims: minDimsOrg
23314
23793
  },
23315
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23316
23794
  {
23317
23795
  id: "kanban",
23318
23796
  category: "diagram",
23319
23797
  parse: parseKanban,
23320
- measure: measureKanban
23798
+ measure: measureKanban,
23799
+ minDims: minDimsKanban
23321
23800
  },
23322
23801
  { id: "c4", category: "diagram", parse: parseC4 },
23323
23802
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23325,25 +23804,40 @@ var init_chart_type_registry = __esm({
23325
23804
  id: "infra",
23326
23805
  category: "diagram",
23327
23806
  parse: parseInfra,
23328
- measure: measureInfra
23807
+ measure: measureInfra,
23808
+ minDims: minDimsInfra
23329
23809
  },
23330
23810
  {
23331
23811
  id: "gantt",
23332
23812
  category: "diagram",
23333
23813
  parse: parseGantt,
23334
- measure: measureGantt
23814
+ measure: measureGantt,
23815
+ minDims: minDimsGantt
23816
+ },
23817
+ {
23818
+ id: "pert",
23819
+ category: "diagram",
23820
+ parse: parsePert,
23821
+ measure: measurePert,
23822
+ minDims: minDimsPert
23335
23823
  },
23336
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23337
23824
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23338
23825
  {
23339
23826
  id: "mindmap",
23340
23827
  category: "diagram",
23341
23828
  parse: parseMindmap,
23342
- measure: measureMindmap
23829
+ measure: measureMindmap,
23830
+ minDims: minDimsMindmap
23343
23831
  },
23344
23832
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23345
23833
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23346
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23834
+ {
23835
+ id: "raci",
23836
+ category: "diagram",
23837
+ parse: parseRaci,
23838
+ measure: measureRaci,
23839
+ minDims: minDimsRaci
23840
+ },
23347
23841
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23348
23842
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23349
23843
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23365,7 +23859,8 @@ var init_chart_type_registry = __esm({
23365
23859
  id: "heatmap",
23366
23860
  category: "data-chart",
23367
23861
  parse: parseHeatmap,
23368
- measure: measureHeatmap
23862
+ measure: measureHeatmap,
23863
+ minDims: minDimsHeatmap
23369
23864
  },
23370
23865
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23371
23866
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23375,7 +23870,8 @@ var init_chart_type_registry = __esm({
23375
23870
  id: "arc",
23376
23871
  category: "visualization",
23377
23872
  parse: parseArc,
23378
- measure: measureArc
23873
+ measure: measureArc,
23874
+ minDims: minDimsArc
23379
23875
  },
23380
23876
  { id: "timeline", category: "visualization", parse: parseTimeline },
23381
23877
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23385,7 +23881,8 @@ var init_chart_type_registry = __esm({
23385
23881
  id: "tech-radar",
23386
23882
  category: "visualization",
23387
23883
  parse: parseTechRadar,
23388
- measure: measureTechRadar
23884
+ measure: measureTechRadar,
23885
+ minDims: minDimsTechRadar
23389
23886
  },
23390
23887
  { id: "cycle", category: "visualization", parse: parseCycle },
23391
23888
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24532,6 +25029,63 @@ var init_export_container = __esm({
24532
25029
  }
24533
25030
  });
24534
25031
 
25032
+ // src/utils/card.ts
25033
+ function renderNodeCard(container, opts) {
25034
+ 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);
25035
+ if (opts.dashed) {
25036
+ rect.attr("stroke-dasharray", "6 3");
25037
+ }
25038
+ 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);
25039
+ const meta = opts.meta;
25040
+ if (!meta || meta.rows.length === 0) return;
25041
+ 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);
25042
+ const keyX = meta.keyX ?? 10;
25043
+ const maxKeyWidth = Math.max(
25044
+ ...meta.rows.map(([key]) => measureText(`${key}: `, meta.fontSize))
25045
+ );
25046
+ const valueX = keyX + maxKeyWidth;
25047
+ const metaStartY = opts.headerHeight + meta.separatorGap + meta.fontSize;
25048
+ for (let i = 0; i < meta.rows.length; i++) {
25049
+ const [displayKey, value] = meta.rows[i];
25050
+ const rowY = metaStartY + i * meta.lineHeight;
25051
+ container.append("text").attr("x", keyX).attr("y", rowY).attr("fill", meta.textColor).attr("font-size", meta.fontSize).text(`${displayKey}: `);
25052
+ container.append("text").attr("x", valueX).attr("y", rowY).attr("fill", meta.textColor).attr("font-size", meta.fontSize).text(value);
25053
+ }
25054
+ }
25055
+ function renderCollapseBar(container, opts) {
25056
+ container.append("clipPath").attr("id", opts.clipId).append("rect").attr("width", opts.width).attr("height", opts.height).attr("rx", opts.rx);
25057
+ 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);
25058
+ }
25059
+ var init_card = __esm({
25060
+ "src/utils/card.ts"() {
25061
+ "use strict";
25062
+ init_text_measure();
25063
+ }
25064
+ });
25065
+
25066
+ // src/utils/visual-conventions.ts
25067
+ 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;
25068
+ var init_visual_conventions = __esm({
25069
+ "src/utils/visual-conventions.ts"() {
25070
+ "use strict";
25071
+ NODE_STROKE_WIDTH = 1.5;
25072
+ EDGE_STROKE_WIDTH = 1.5;
25073
+ CARD_RADIUS = 6;
25074
+ CONTAINER_RADIUS = 8;
25075
+ COLLAPSE_BAR_INSET = 0;
25076
+ HEADER_HEIGHT2 = 28;
25077
+ LABEL_FONT_SIZE2 = 13;
25078
+ META_FONT_SIZE2 = 11;
25079
+ META_LINE_HEIGHT2 = 16;
25080
+ SEPARATOR_GAP2 = 6;
25081
+ COLLAPSE_BAR_HEIGHT = 6;
25082
+ CONTAINER_HEADER_HEIGHT = 28;
25083
+ CONTAINER_LABEL_FONT_SIZE = 13;
25084
+ CONTAINER_META_FONT_SIZE = 11;
25085
+ CONTAINER_META_LINE_HEIGHT2 = 16;
25086
+ }
25087
+ });
25088
+
24535
25089
  // src/org/renderer.ts
24536
25090
  var renderer_exports = {};
24537
25091
  __export(renderer_exports, {
@@ -24550,7 +25104,7 @@ function nodeStroke(palette, nodeColor2) {
24550
25104
  }
24551
25105
  function containerFill(palette, isDark, nodeColor2) {
24552
25106
  if (nodeColor2) {
24553
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25107
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24554
25108
  }
24555
25109
  return mix(palette.surface, palette.bg, 40);
24556
25110
  }
@@ -24690,9 +25244,16 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24690
25244
  }
24691
25245
  }
24692
25246
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
24693
- const clipId = `clip-${c.nodeId}`;
24694
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", sContainerRadius);
24695
- 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");
25247
+ renderCollapseBar(cG, {
25248
+ width: c.width,
25249
+ height: c.height,
25250
+ barHeight: sCollapseBarHeight,
25251
+ inset: sCollapseBarInset,
25252
+ rx: sContainerRadius,
25253
+ fill: containerStroke(palette, colorOff ? void 0 : c.color),
25254
+ clipId: `clip-${c.nodeId}`,
25255
+ className: "org-collapse-bar"
25256
+ });
24696
25257
  }
24697
25258
  if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
24698
25259
  const iconSize = 14;
@@ -24701,10 +25262,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24701
25262
  const iconY = iconPad;
24702
25263
  const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24703
25264
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25265
+ const iconColor = contrastText(
25266
+ fill2,
25267
+ palette.textOnFillLight,
25268
+ palette.textOnFillDark
25269
+ );
24704
25270
  const cx = iconSize / 2;
24705
25271
  const cy = iconSize / 2;
24706
- 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);
24707
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25272
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25273
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24708
25274
  }
24709
25275
  }
24710
25276
  for (const edge of layout.edges) {
@@ -24743,42 +25309,48 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24743
25309
  solid
24744
25310
  );
24745
25311
  const stroke2 = nodeStroke(palette, colorOff ? void 0 : node.color);
24746
- 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);
24747
- if (node.isContainer) {
24748
- rect.attr("stroke-dasharray", "6 3");
24749
- }
24750
25312
  const labelColor = contrastText(
24751
25313
  fill2,
24752
25314
  palette.textOnFillLight,
24753
25315
  palette.textOnFillDark
24754
25316
  );
24755
- 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);
24756
25317
  const metaEntries = Object.entries(node.metadata);
24757
- if (metaEntries.length > 0) {
24758
- 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);
24759
- const metaDisplayKeys = metaEntries.map(
24760
- ([k]) => displayNames.get(k) ?? k
24761
- );
24762
- const maxKeyWidth = Math.max(
24763
- ...metaDisplayKeys.map((k) => measureText(`${k}: `, sMetaFontSize))
24764
- );
24765
- const valueX = 10 + maxKeyWidth;
24766
- const metaStartY = sHeaderHeight + sSeparatorGap + sMetaFontSize;
24767
- for (let i = 0; i < metaEntries.length; i++) {
24768
- const [, value] = metaEntries[i];
24769
- const displayKey = metaDisplayKeys[i];
24770
- const rowY = metaStartY + i * sMetaLineHeight;
24771
- nodeG.append("text").attr("x", 10).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(`${displayKey}: `);
24772
- nodeG.append("text").attr("x", valueX).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(value);
25318
+ renderNodeCard(nodeG, {
25319
+ width: node.width,
25320
+ height: node.height,
25321
+ rx: sCardRadius,
25322
+ fill: fill2,
25323
+ stroke: stroke2,
25324
+ strokeWidth: sNodeStrokeWidth,
25325
+ ...node.isContainer && { dashed: true },
25326
+ label: node.label,
25327
+ labelColor,
25328
+ labelFontSize: sLabelFontSize,
25329
+ headerHeight: sHeaderHeight,
25330
+ ...metaEntries.length > 0 && {
25331
+ meta: {
25332
+ rows: metaEntries.map(
25333
+ ([k, value]) => [displayNames.get(k) ?? k, value]
25334
+ ),
25335
+ fontSize: sMetaFontSize,
25336
+ lineHeight: sMetaLineHeight,
25337
+ separatorGap: sSeparatorGap,
25338
+ separatorColor: solid ? labelColor : stroke2,
25339
+ textColor: labelColor
25340
+ }
24773
25341
  }
24774
- }
25342
+ });
24775
25343
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
24776
- const clipId = `clip-${node.id}`;
24777
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", sCardRadius);
24778
- nodeG.append("rect").attr("x", sCollapseBarInset).attr("y", node.height - sCollapseBarHeight).attr("width", node.width - sCollapseBarInset * 2).attr("height", sCollapseBarHeight).attr(
24779
- "fill",
24780
- solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color)
24781
- ).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
25344
+ renderCollapseBar(nodeG, {
25345
+ width: node.width,
25346
+ height: node.height,
25347
+ barHeight: sCollapseBarHeight,
25348
+ inset: sCollapseBarInset,
25349
+ rx: sCardRadius,
25350
+ fill: solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color),
25351
+ clipId: `clip-${node.id}`,
25352
+ className: "org-collapse-bar"
25353
+ });
24782
25354
  }
24783
25355
  if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
24784
25356
  const iconSize = 14;
@@ -24787,10 +25359,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24787
25359
  const iconY = iconPad;
24788
25360
  const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24789
25361
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25362
+ const iconColor = labelColor;
24790
25363
  const cx = iconSize / 2;
24791
25364
  const cy = iconSize / 2;
24792
- 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);
24793
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25365
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25366
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24794
25367
  }
24795
25368
  }
24796
25369
  if (hasAncestorTrail) {
@@ -24922,7 +25495,7 @@ function renderOrgForExport(content, theme, palette) {
24922
25495
  return extractExportSvg(container, theme);
24923
25496
  });
24924
25497
  }
24925
- var 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;
25498
+ var DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
24926
25499
  var init_renderer = __esm({
24927
25500
  "src/org/renderer.ts"() {
24928
25501
  "use strict";
@@ -24935,27 +25508,14 @@ var init_renderer = __esm({
24935
25508
  init_layout();
24936
25509
  init_legend_constants();
24937
25510
  init_legend_integration();
25511
+ init_card();
24938
25512
  init_text_measure();
24939
25513
  init_legend_layout();
24940
25514
  init_title_constants();
25515
+ init_visual_conventions();
24941
25516
  DIAGRAM_PADDING = 20;
24942
25517
  MAX_SCALE = 3;
24943
25518
  TITLE_HEIGHT = 30;
24944
- LABEL_FONT_SIZE2 = 13;
24945
- META_FONT_SIZE2 = 11;
24946
- META_LINE_HEIGHT2 = 16;
24947
- HEADER_HEIGHT2 = 28;
24948
- SEPARATOR_GAP2 = 6;
24949
- EDGE_STROKE_WIDTH = 1.5;
24950
- NODE_STROKE_WIDTH = 1.5;
24951
- CARD_RADIUS = 6;
24952
- CONTAINER_RADIUS = 8;
24953
- CONTAINER_LABEL_FONT_SIZE = 13;
24954
- CONTAINER_META_FONT_SIZE = 11;
24955
- CONTAINER_META_LINE_HEIGHT2 = 16;
24956
- CONTAINER_HEADER_HEIGHT = 28;
24957
- COLLAPSE_BAR_HEIGHT = 6;
24958
- COLLAPSE_BAR_INSET = 0;
24959
25519
  ANCESTOR_DOT_R = 4;
24960
25520
  ANCESTOR_LABEL_FONT_SIZE = 11;
24961
25521
  ANCESTOR_ROW_HEIGHT = 22;
@@ -25725,7 +26285,7 @@ function nodeStroke2(_palette, nodeColor2) {
25725
26285
  }
25726
26286
  function containerFill2(palette, isDark, nodeColor2) {
25727
26287
  if (nodeColor2) {
25728
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26288
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25729
26289
  }
25730
26290
  return mix(palette.surface, palette.bg, 40);
25731
26291
  }
@@ -25739,21 +26299,21 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25739
26299
  if (width <= 0 || height <= 0) return;
25740
26300
  const ctx = ScaleContext.identity();
25741
26301
  const sDiagramPadding = ctx.aesthetic(DIAGRAM_PADDING2);
25742
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE4);
25743
- const sMetaFontSize = ctx.text(META_FONT_SIZE4);
25744
- const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT4);
25745
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT4);
25746
- const sSeparatorGap = ctx.structural(SEPARATOR_GAP4);
25747
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH2);
25748
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH2);
26302
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE2);
26303
+ const sMetaFontSize = ctx.text(META_FONT_SIZE2);
26304
+ const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT2);
26305
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT2);
26306
+ const sSeparatorGap = ctx.structural(SEPARATOR_GAP2);
26307
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
26308
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
25749
26309
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE);
25750
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE3);
25751
- const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE2);
25752
- const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT4);
25753
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT2);
26310
+ const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE);
26311
+ const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE);
26312
+ const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT2);
26313
+ const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT);
25754
26314
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
25755
26315
  const sTitleHeight = ctx.structural(TITLE_HEIGHT2);
25756
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT2);
26316
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
25757
26317
  const sLegendFixedGap = ctx.aesthetic(LEGEND_FIXED_GAP2);
25758
26318
  const hasLegend = layout.legend.length > 0;
25759
26319
  const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
@@ -25844,7 +26404,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25844
26404
  }
25845
26405
  const fill2 = containerFill2(palette, isDark, c.color);
25846
26406
  const stroke2 = containerStroke2(palette, c.color);
25847
- 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);
26407
+ 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);
25848
26408
  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);
25849
26409
  const metaEntries = Object.entries(c.metadata);
25850
26410
  if (metaEntries.length > 0) {
@@ -25869,7 +26429,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25869
26429
  }
25870
26430
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
25871
26431
  const clipId = `clip-${c.nodeId}`;
25872
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS2);
26432
+ cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
25873
26433
  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})`);
25874
26434
  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}`);
25875
26435
  }
@@ -25908,13 +26468,23 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25908
26468
  const solid = parsed.options["solid-fill"] === "on";
25909
26469
  const fill2 = nodeFill2(palette, isDark, node.color, solid);
25910
26470
  const stroke2 = nodeStroke2(palette, node.color);
25911
- 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);
25912
26471
  const labelColor = contrastText(
25913
26472
  fill2,
25914
26473
  palette.textOnFillLight,
25915
26474
  palette.textOnFillDark
25916
26475
  );
25917
- 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);
26476
+ renderNodeCard(nodeG, {
26477
+ width: node.width,
26478
+ height: node.height,
26479
+ rx: CARD_RADIUS,
26480
+ fill: fill2,
26481
+ stroke: stroke2,
26482
+ strokeWidth: sNodeStrokeWidth,
26483
+ label: node.label,
26484
+ labelColor,
26485
+ labelFontSize: sLabelFontSize,
26486
+ headerHeight: sHeaderHeight
26487
+ });
25918
26488
  const metaEntries = Object.entries(node.metadata);
25919
26489
  if (metaEntries.length > 0) {
25920
26490
  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);
@@ -25949,7 +26519,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25949
26519
  }
25950
26520
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
25951
26521
  const clipId = `clip-${node.id}`;
25952
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS2);
26522
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
25953
26523
  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})`);
25954
26524
  }
25955
26525
  }
@@ -26079,7 +26649,7 @@ async function renderSitemapForExport(content, theme, palette) {
26079
26649
  document.body.removeChild(container);
26080
26650
  return svgHtml;
26081
26651
  }
26082
- var 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;
26652
+ var DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, LEGEND_FIXED_GAP2, lineGenerator, lineGeneratorLinear;
26083
26653
  var init_renderer2 = __esm({
26084
26654
  "src/sitemap/renderer.ts"() {
26085
26655
  "use strict";
@@ -26092,27 +26662,15 @@ var init_renderer2 = __esm({
26092
26662
  init_legend_integration();
26093
26663
  init_legend_layout();
26094
26664
  init_scaling();
26665
+ init_card();
26095
26666
  init_title_constants();
26667
+ init_visual_conventions();
26096
26668
  DIAGRAM_PADDING2 = 20;
26097
26669
  MAX_SCALE2 = 3;
26098
26670
  TITLE_HEIGHT2 = 30;
26099
- LABEL_FONT_SIZE4 = 13;
26100
- META_FONT_SIZE4 = 11;
26101
- META_LINE_HEIGHT4 = 16;
26102
- HEADER_HEIGHT4 = 28;
26103
- SEPARATOR_GAP4 = 6;
26104
- EDGE_STROKE_WIDTH2 = 1.5;
26105
- NODE_STROKE_WIDTH2 = 1.5;
26106
- CARD_RADIUS2 = 6;
26107
- CONTAINER_RADIUS2 = 8;
26108
- CONTAINER_LABEL_FONT_SIZE3 = 13;
26109
- CONTAINER_META_FONT_SIZE2 = 11;
26110
- CONTAINER_META_LINE_HEIGHT4 = 16;
26111
- CONTAINER_HEADER_HEIGHT2 = 28;
26112
26671
  ARROWHEAD_W = 10;
26113
26672
  ARROWHEAD_H = 7;
26114
26673
  EDGE_LABEL_FONT_SIZE = 11;
26115
- COLLAPSE_BAR_HEIGHT2 = 6;
26116
26674
  LEGEND_FIXED_GAP2 = 8;
26117
26675
  lineGenerator = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveBasis);
26118
26676
  lineGeneratorLinear = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveLinear);
@@ -26280,7 +26838,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
26280
26838
  const sCardMetaLineHeight = ctx.structural(CARD_META_LINE_HEIGHT);
26281
26839
  const sCardSeparatorGap = ctx.structural(CARD_SEPARATOR_GAP);
26282
26840
  const sCardGap = ctx.aesthetic(CARD_GAP);
26283
- const sCardRadius = ctx.structural(CARD_RADIUS3);
26841
+ const sCardRadius = ctx.structural(CARD_RADIUS);
26284
26842
  const sCardPaddingX = ctx.aesthetic(CARD_PADDING_X);
26285
26843
  const sCardPaddingY = ctx.aesthetic(CARD_PADDING_Y);
26286
26844
  const sCardStrokeWidth = ctx.structural(CARD_STROKE_WIDTH);
@@ -26660,7 +27218,7 @@ function computeSwimlaneLayout(parsed, buckets, baseLayout, collapsedLanes, coll
26660
27218
  const totalHeight = laneY - sLaneGap + sColumnPadding + sDiagramPadding;
26661
27219
  return { columnXs, lanes, totalWidth, totalHeight, startY };
26662
27220
  }
26663
- 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) {
27221
+ 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) {
26664
27222
  const visibleColumns = parsed.columns.filter((c) => !isArchiveColumn(c.name));
26665
27223
  const buckets = bucketCardsBySwimlane(visibleColumns, swimlaneGroup);
26666
27224
  const grid = computeSwimlaneLayout(
@@ -26800,7 +27358,7 @@ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, is
26800
27358
  }
26801
27359
  }
26802
27360
  }
26803
- 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) {
27361
+ 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) {
26804
27362
  const card = cardLayout.card;
26805
27363
  const resolvedColor = resolveCardTagColor(card, tagGroups, activeTagGroup);
26806
27364
  const tagMeta = resolveCardTagMeta(card, tagGroups, hiddenMetaGroups);
@@ -26850,7 +27408,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
26850
27408
  }
26851
27409
  }
26852
27410
  }
26853
- var 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;
27411
+ var 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;
26854
27412
  var init_renderer3 = __esm({
26855
27413
  "src/kanban/renderer.ts"() {
26856
27414
  "use strict";
@@ -26864,6 +27422,7 @@ var init_renderer3 = __esm({
26864
27422
  init_legend_integration();
26865
27423
  init_scaling();
26866
27424
  init_text_measure();
27425
+ init_visual_conventions();
26867
27426
  init_title_constants();
26868
27427
  DIAGRAM_PADDING3 = 20;
26869
27428
  COLUMN_GAP = 16;
@@ -26874,7 +27433,6 @@ var init_renderer3 = __esm({
26874
27433
  CARD_META_LINE_HEIGHT = 14;
26875
27434
  CARD_SEPARATOR_GAP = 4;
26876
27435
  CARD_GAP = 8;
26877
- CARD_RADIUS3 = 6;
26878
27436
  CARD_PADDING_X = 10;
26879
27437
  CARD_PADDING_Y = 6;
26880
27438
  CARD_STROKE_WIDTH = 1.5;
@@ -27177,8 +27735,8 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
27177
27735
  const sClassFontSize = ctx.text(CLASS_FONT_SIZE2);
27178
27736
  const sMemberFontSize = ctx.text(MEMBER_FONT_SIZE2);
27179
27737
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE2);
27180
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH3);
27181
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH3);
27738
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
27739
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
27182
27740
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT2);
27183
27741
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y2);
27184
27742
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X);
@@ -27437,7 +27995,7 @@ function renderClassDiagramForExport(content, theme, palette) {
27437
27995
  return extractExportSvg(container, theme);
27438
27996
  });
27439
27997
  }
27440
- var 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;
27998
+ var 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;
27441
27999
  var init_renderer4 = __esm({
27442
28000
  "src/class/renderer.ts"() {
27443
28001
  "use strict";
@@ -27452,13 +28010,12 @@ var init_renderer4 = __esm({
27452
28010
  init_scaling();
27453
28011
  init_text_measure();
27454
28012
  init_note_box();
28013
+ init_visual_conventions();
27455
28014
  DIAGRAM_PADDING4 = 20;
27456
28015
  MAX_SCALE3 = 3;
27457
28016
  CLASS_FONT_SIZE2 = 13;
27458
28017
  MEMBER_FONT_SIZE2 = 11;
27459
28018
  EDGE_LABEL_FONT_SIZE2 = 11;
27460
- EDGE_STROKE_WIDTH3 = 1.5;
27461
- NODE_STROKE_WIDTH3 = 1.5;
27462
28019
  MEMBER_LINE_HEIGHT2 = 18;
27463
28020
  COMPARTMENT_PADDING_Y2 = 8;
27464
28021
  MEMBER_PADDING_X = 10;
@@ -27954,7 +28511,7 @@ function constraintIcon(constraint) {
27954
28511
  return "\u25CB";
27955
28512
  }
27956
28513
  }
27957
- function drawCardinality(g, point, prevPoint, cardinality, color, useLabels, edgeLabelFontSize = EDGE_LABEL_FONT_SIZE4, edgeStrokeWidth = EDGE_STROKE_WIDTH4) {
28514
+ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels, edgeLabelFontSize = EDGE_LABEL_FONT_SIZE4, edgeStrokeWidth = EDGE_STROKE_WIDTH) {
27958
28515
  const dx = point.x - prevPoint.x;
27959
28516
  const dy = point.y - prevPoint.y;
27960
28517
  const len = Math.sqrt(dx * dx + dy * dy);
@@ -28014,8 +28571,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
28014
28571
  const sTableFontSize = ctx.text(TABLE_FONT_SIZE2);
28015
28572
  const sColumnFontSize = ctx.text(COLUMN_FONT_SIZE2);
28016
28573
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE4);
28017
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH4);
28018
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH4);
28574
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
28575
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
28019
28576
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT4);
28020
28577
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y4);
28021
28578
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X2);
@@ -28293,7 +28850,7 @@ function renderERDiagramForExport(content, theme, palette) {
28293
28850
  document.body.removeChild(container);
28294
28851
  }
28295
28852
  }
28296
- var 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;
28853
+ var DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABEL_FONT_SIZE4, MEMBER_LINE_HEIGHT4, COMPARTMENT_PADDING_Y4, MEMBER_PADDING_X2, lineGenerator3;
28297
28854
  var init_renderer5 = __esm({
28298
28855
  "src/er/renderer.ts"() {
28299
28856
  "use strict";
@@ -28310,13 +28867,12 @@ var init_renderer5 = __esm({
28310
28867
  init_parser9();
28311
28868
  init_layout4();
28312
28869
  init_classify();
28870
+ init_visual_conventions();
28313
28871
  DIAGRAM_PADDING5 = 20;
28314
28872
  MAX_SCALE4 = 3;
28315
28873
  TABLE_FONT_SIZE2 = 13;
28316
28874
  COLUMN_FONT_SIZE2 = 11;
28317
28875
  EDGE_LABEL_FONT_SIZE4 = 11;
28318
- EDGE_STROKE_WIDTH4 = 1.5;
28319
- NODE_STROKE_WIDTH4 = 1.5;
28320
28876
  MEMBER_LINE_HEIGHT4 = 18;
28321
28877
  COMPARTMENT_PADDING_Y4 = 8;
28322
28878
  MEMBER_PADDING_X2 = 10;
@@ -28536,7 +29092,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28536
29092
  onToggleDescriptions,
28537
29093
  onToggleControlsExpand,
28538
29094
  exportMode = false,
28539
- controlsHost
29095
+ controlsHost,
29096
+ rampDomain
28540
29097
  } = options ?? {};
28541
29098
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28542
29099
  const width = exportDims?.width ?? container.clientWidth;
@@ -28546,9 +29103,9 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28546
29103
  const sDiagramPadding = sctx.aesthetic(DIAGRAM_PADDING6);
28547
29104
  const sMinNodeFontSize = sctx.text(MIN_NODE_FONT_SIZE);
28548
29105
  const sEdgeLabelFontSize = sctx.text(EDGE_LABEL_FONT_SIZE5);
28549
- const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH5);
28550
- const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH5);
28551
- const sCollapseBarHeight = sctx.structural(COLLAPSE_BAR_HEIGHT3);
29106
+ const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH);
29107
+ const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH);
29108
+ const sCollapseBarHeight = sctx.structural(COLLAPSE_BAR_HEIGHT2);
28552
29109
  const sDescFontSize = sctx.text(DESC_FONT_SIZE);
28553
29110
  const sGroupLabelFontSize = sctx.text(GROUP_LABEL_FONT_SIZE);
28554
29111
  const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
@@ -28556,8 +29113,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28556
29113
  const sTitleY = sctx.structural(TITLE_Y);
28557
29114
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28558
29115
  const hasRamp = nodeValues.length > 0;
28559
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28560
- const rampMax = Math.max(...nodeValues);
29116
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29117
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28561
29118
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28562
29119
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28563
29120
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28640,8 +29197,32 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28640
29197
  const scaleX = width / (contentW + sDiagramPadding * 2);
28641
29198
  const scaleY = height / (contentH + sDiagramPadding * 2);
28642
29199
  const scale = Math.min(scaleX, scaleY, 3);
28643
- const offsetX = (width - contentW * scale) / 2;
28644
- const offsetY = sDiagramPadding + titleOffset + legendH;
29200
+ let centerShiftX = 0;
29201
+ let centerShiftY = 0;
29202
+ if (parsed.nodePositions && parsed.nodePositions.size > 0) {
29203
+ let bMinX = Infinity, bMinY = Infinity, bMaxX = -Infinity, bMaxY = -Infinity;
29204
+ const accB = (x, y) => {
29205
+ if (x < bMinX) bMinX = x;
29206
+ if (x > bMaxX) bMaxX = x;
29207
+ if (y < bMinY) bMinY = y;
29208
+ if (y > bMaxY) bMaxY = y;
29209
+ };
29210
+ for (const n of layout.nodes) {
29211
+ accB(n.x - n.width / 2, n.y - n.height / 2);
29212
+ accB(n.x + n.width / 2, n.y + n.height / 2);
29213
+ }
29214
+ for (const g of layout.groups) {
29215
+ accB(g.x - g.width / 2, g.y - g.height / 2);
29216
+ accB(g.x + g.width / 2, g.y + g.height / 2);
29217
+ }
29218
+ for (const e of layout.edges) for (const p of e.points) accB(p.x, p.y);
29219
+ if (Number.isFinite(bMinX)) {
29220
+ centerShiftX = (layout.width - bMaxX - bMinX) / 2;
29221
+ centerShiftY = (layout.height - bMaxY - bMinY) / 2;
29222
+ }
29223
+ }
29224
+ const offsetX = (width - contentW * scale) / 2 + centerShiftX * scale;
29225
+ const offsetY = sDiagramPadding + titleOffset + legendH + centerShiftY * scale;
28645
29226
  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);
28646
29227
  if (sctx.isBelowFloor) {
28647
29228
  svg.attr("width", "100%");
@@ -28685,7 +29266,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28685
29266
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28686
29267
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28687
29268
  if (group.collapsed) {
28688
- const fillColor = isDark ? palette.surface : palette.bg;
29269
+ const fillColor = themeBaseBg(palette, isDark);
28689
29270
  const strokeColor = palette.border;
28690
29271
  groupG.append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", group.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", sNodeStrokeWidth);
28691
29272
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -28777,7 +29358,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28777
29358
  const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
28778
29359
  edgeGroups.set(i, edgeG);
28779
29360
  const markerId = `bl-arrow-${color.replace("#", "")}`;
28780
- const gen = parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
29361
+ const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
28781
29362
  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})`);
28782
29363
  if (le.bidirectional) {
28783
29364
  const revId = `bl-arrow-rev-${color.replace("#", "")}`;
@@ -29041,6 +29622,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
29041
29622
  });
29042
29623
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
29043
29624
  }
29625
+ if (!exportDims && !exportMode) {
29626
+ const iconSize = 14;
29627
+ const focusG = svg.append("g").attr("class", "bl-focus-icon").attr("data-export-ignore", "true").style("display", "none").style("pointer-events", "auto").style("cursor", "pointer");
29628
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29629
+ const cx = iconSize / 2;
29630
+ const cy = iconSize / 2;
29631
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", palette.bg).attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
29632
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29633
+ }
29044
29634
  }
29045
29635
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
29046
29636
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -29058,7 +29648,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
29058
29648
  }
29059
29649
  });
29060
29650
  }
29061
- var 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;
29651
+ var 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;
29062
29652
  var init_renderer6 = __esm({
29063
29653
  "src/boxes-and-lines/renderer.ts"() {
29064
29654
  "use strict";
@@ -29074,14 +29664,13 @@ var init_renderer6 = __esm({
29074
29664
  init_wrapped_desc();
29075
29665
  init_scaling();
29076
29666
  init_text_measure();
29667
+ init_visual_conventions();
29077
29668
  DIAGRAM_PADDING6 = 20;
29078
29669
  NODE_FONT_SIZE = 11;
29079
29670
  MIN_NODE_FONT_SIZE = 9;
29080
29671
  EDGE_LABEL_FONT_SIZE5 = 11;
29081
- EDGE_STROKE_WIDTH5 = 1.5;
29082
- NODE_STROKE_WIDTH5 = 1.5;
29083
29672
  NODE_RX = 8;
29084
- COLLAPSE_BAR_HEIGHT3 = 4;
29673
+ COLLAPSE_BAR_HEIGHT2 = 4;
29085
29674
  ARROWHEAD_W2 = 5;
29086
29675
  ARROWHEAD_H2 = 4;
29087
29676
  DESC_FONT_SIZE = 10;
@@ -29095,6 +29684,7 @@ var init_renderer6 = __esm({
29095
29684
  VALUE_FONT_SIZE = 11;
29096
29685
  lineGeneratorLR = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29097
29686
  lineGeneratorTB = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29687
+ lineGeneratorStraight = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveLinear);
29098
29688
  }
29099
29689
  });
29100
29690
 
@@ -29637,7 +30227,7 @@ function shuffle(a, r) {
29637
30227
  return x;
29638
30228
  }
29639
30229
  function flatten(d) {
29640
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30230
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29641
30231
  const pts = [];
29642
30232
  let i = 0, cx = 0, cy = 0, cmd = "";
29643
30233
  const num = () => parseFloat(toks[i++]);
@@ -29681,17 +30271,10 @@ function flatten(d) {
29681
30271
  }
29682
30272
  return pts;
29683
30273
  }
29684
- function segPoint(p1, p2, p3, p4) {
29685
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29686
- if (Math.abs(den) < 1e-9) return null;
29687
- 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;
29688
- 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;
29689
- }
29690
- function countSplineCrossings(layout) {
29691
- const center = /* @__PURE__ */ new Map();
29692
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29693
- for (const g of layout.groups)
29694
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30274
+ function flatPolys(layout) {
30275
+ const key = layout.edges;
30276
+ const hit = FLAT_CACHE.get(key);
30277
+ if (hit) return hit;
29695
30278
  const polys = layout.edges.map((e) => {
29696
30279
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29697
30280
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29701,8 +30284,24 @@ function countSplineCrossings(layout) {
29701
30284
  if (p.y < y0) y0 = p.y;
29702
30285
  if (p.y > y1) y1 = p.y;
29703
30286
  }
29704
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30287
+ return { pts, x0, y0, x1, y1 };
29705
30288
  });
30289
+ FLAT_CACHE.set(key, polys);
30290
+ return polys;
30291
+ }
30292
+ function segPoint(p1, p2, p3, p4) {
30293
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30294
+ if (Math.abs(den) < 1e-9) return null;
30295
+ const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
30296
+ return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
30297
+ }
30298
+ function countSplineCrossings(layout, floor = Infinity) {
30299
+ const center = /* @__PURE__ */ new Map();
30300
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30301
+ for (const g of layout.groups)
30302
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30303
+ const polys = flatPolys(layout);
30304
+ const edges = layout.edges;
29706
30305
  const R = 34;
29707
30306
  let total = 0;
29708
30307
  for (let a = 0; a < polys.length; a++)
@@ -29710,23 +30309,33 @@ function countSplineCrossings(layout) {
29710
30309
  const A = polys[a], B = polys[b];
29711
30310
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29712
30311
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29713
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30312
+ const ea = edges[a], eb = edges[b];
30313
+ let sh0, sh1;
30314
+ if (ea.source === eb.source || ea.source === eb.target)
30315
+ sh0 = center.get(ea.source);
30316
+ if (ea.target === eb.source || ea.target === eb.target)
30317
+ sh1 = center.get(ea.target);
29714
30318
  const hits = [];
29715
- for (let i = 1; i < A.pts.length; i++)
29716
- for (let j = 1; j < B.pts.length; j++) {
29717
- const p = segPoint(
29718
- A.pts[i - 1],
29719
- A.pts[i],
29720
- B.pts[j - 1],
29721
- B.pts[j]
29722
- );
30319
+ const ap = A.pts, bp = B.pts;
30320
+ for (let i = 1; i < ap.length; i++) {
30321
+ const a0 = ap[i - 1], a1 = ap[i];
30322
+ const axMin = a0.x < a1.x ? a0.x : a1.x, axMax = a0.x > a1.x ? a0.x : a1.x, ayMin = a0.y < a1.y ? a0.y : a1.y, ayMax = a0.y > a1.y ? a0.y : a1.y;
30323
+ for (let j = 1; j < bp.length; j++) {
30324
+ const b0 = bp[j - 1], b1 = bp[j];
30325
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30326
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30327
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30328
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30329
+ const p = segPoint(a0, a1, b0, b1);
29723
30330
  if (!p) continue;
29724
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29725
- continue;
30331
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30332
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29726
30333
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29727
30334
  hits.push(p);
29728
30335
  }
30336
+ }
29729
30337
  total += hits.length;
30338
+ if (total > floor) return total;
29730
30339
  }
29731
30340
  return total;
29732
30341
  }
@@ -29764,17 +30373,8 @@ function detectEdgeOverlaps(layout, opts) {
29764
30373
  w: g.width,
29765
30374
  h: g.height
29766
30375
  });
29767
- const polys = layout.edges.map((e) => {
29768
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29769
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29770
- for (const p of pts) {
29771
- if (p.x < x0) x0 = p.x;
29772
- if (p.x > x1) x1 = p.x;
29773
- if (p.y < y0) y0 = p.y;
29774
- if (p.y > y1) y1 = p.y;
29775
- }
29776
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29777
- });
30376
+ const polys = flatPolys(layout);
30377
+ const edges = layout.edges;
29778
30378
  const runs = [];
29779
30379
  for (let a = 0; a < polys.length; a++)
29780
30380
  for (let b = a + 1; b < polys.length; b++) {
@@ -29782,7 +30382,12 @@ function detectEdgeOverlaps(layout, opts) {
29782
30382
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29783
30383
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29784
30384
  continue;
29785
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30385
+ const ea = edges[a], eb = edges[b];
30386
+ let shr0, shr1;
30387
+ if (ea.source === eb.source || ea.source === eb.target)
30388
+ shr0 = rect.get(ea.source);
30389
+ if (ea.target === eb.source || ea.target === eb.target)
30390
+ shr1 = rect.get(ea.target);
29786
30391
  let run = [];
29787
30392
  let runLen = 0;
29788
30393
  const flush = () => {
@@ -29796,7 +30401,7 @@ function detectEdgeOverlaps(layout, opts) {
29796
30401
  runLen = 0;
29797
30402
  };
29798
30403
  for (const p of A.pts) {
29799
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30404
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
29800
30405
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29801
30406
  if (covered) {
29802
30407
  if (run.length)
@@ -29831,9 +30436,10 @@ function detectEdgeNodePierces(layout, opts) {
29831
30436
  });
29832
30437
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
29833
30438
  const out = [];
30439
+ const polys = flatPolys(layout);
29834
30440
  layout.edges.forEach((e, idx) => {
29835
30441
  if (e.points.length < 2) return;
29836
- const poly = flatten(splineGen(e.points) ?? "");
30442
+ const poly = polys[idx].pts;
29837
30443
  for (const r of rects) {
29838
30444
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29839
30445
  continue;
@@ -30180,8 +30786,10 @@ function edgeLength(layout) {
30180
30786
  );
30181
30787
  return total;
30182
30788
  }
30183
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30789
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30184
30790
  const hideDescriptions = opts?.hideDescriptions ?? false;
30791
+ const onProgress = opts?.onProgress;
30792
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30185
30793
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30186
30794
  if (collapseInfo) {
30187
30795
  const missing = /* @__PURE__ */ new Set();
@@ -30284,6 +30892,156 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30284
30892
  return { x: rect.x + dx * s, y: rect.y + dy * s };
30285
30893
  };
30286
30894
  const isInsideRect = (p, rect) => Math.abs(p.x - rect.x) <= rect.w / 2 && Math.abs(p.y - rect.y) <= rect.h / 2;
30895
+ const pinned = parsed.nodePositions;
30896
+ const groupLabelSet = new Set(parsed.groups.map((g) => g.label));
30897
+ const groupsAreFlat = parsed.groups.every(
30898
+ (g) => !g.parentGroup && !g.children.some((c) => groupLabelSet.has(c))
30899
+ );
30900
+ const allOriginalGroupLabels = new Set(
30901
+ (collapseInfo?.originalGroups ?? parsed.groups).map((g) => g.label)
30902
+ );
30903
+ const collapsedAreFlatPinned = collapsedGroupLabels.size === 0 || pinned !== void 0 && collapseInfo !== void 0 && [...collapsedGroupLabels].every((label) => {
30904
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
30905
+ if (!og || og.parentGroup) return false;
30906
+ return og.children.every(
30907
+ (c) => pinned.has(c) && !allOriginalGroupLabels.has(c)
30908
+ );
30909
+ });
30910
+ const allPinned = pinned !== void 0 && (parsed.nodes.length > 0 || collapsedGroupLabels.size > 0) && parsed.nodes.every((n2) => pinned.has(n2.label)) && groupsAreFlat && collapsedAreFlatPinned;
30911
+ function placePinned(pins) {
30912
+ const collapsedPosByGid = /* @__PURE__ */ new Map();
30913
+ const collapsedBoxes = [];
30914
+ if (collapseInfo)
30915
+ for (const label of collapsedGroupLabels) {
30916
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
30917
+ if (!og) continue;
30918
+ let cx0 = Infinity, cy0 = Infinity, cx1 = -Infinity, cy1 = -Infinity;
30919
+ for (const c of og.children) {
30920
+ const p = pins.get(c);
30921
+ if (!p) continue;
30922
+ cx0 = Math.min(cx0, p.x);
30923
+ cx1 = Math.max(cx1, p.x);
30924
+ cy0 = Math.min(cy0, p.y);
30925
+ cy1 = Math.max(cy1, p.y);
30926
+ }
30927
+ if (!Number.isFinite(cx0)) continue;
30928
+ const cx = (cx0 + cx1) / 2;
30929
+ const cy = (cy0 + cy1) / 2;
30930
+ collapsedPosByGid.set(`__group_${label}`, { x: cx, y: cy });
30931
+ collapsedBoxes.push({
30932
+ label,
30933
+ lineNumber: og.lineNumber,
30934
+ childCount: collapseInfo.collapsedChildCounts.get(label) ?? og.children.length,
30935
+ x: cx,
30936
+ y: cy
30937
+ });
30938
+ }
30939
+ const posOf = (label) => pins.get(label) ?? collapsedPosByGid.get(label);
30940
+ const rectOf = (label) => {
30941
+ const p = posOf(label);
30942
+ const s = sizes.get(label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30943
+ return { x: p.x, y: p.y, w: s.width, h: s.height };
30944
+ };
30945
+ const nodes = parsed.nodes.map((n2) => {
30946
+ const r = rectOf(n2.label);
30947
+ return { label: n2.label, x: r.x, y: r.y, width: r.w, height: r.h };
30948
+ });
30949
+ const edges = parsed.edges.flatMap((e) => {
30950
+ const sp = posOf(e.source);
30951
+ const tp = posOf(e.target);
30952
+ if (!sp || !tp) return [];
30953
+ const srcRect = rectOf(e.source);
30954
+ const tgtRect = rectOf(e.target);
30955
+ const p0 = rectBorderPoint(srcRect, tp);
30956
+ const p1 = rectBorderPoint(tgtRect, sp);
30957
+ return [
30958
+ {
30959
+ source: e.source,
30960
+ target: e.target,
30961
+ ...e.label !== void 0 && { label: e.label },
30962
+ bidirectional: e.bidirectional,
30963
+ lineNumber: e.lineNumber,
30964
+ points: [p0, p1],
30965
+ yOffset: 0,
30966
+ parallelCount: 1,
30967
+ metadata: e.metadata,
30968
+ straight: true
30969
+ }
30970
+ ];
30971
+ });
30972
+ const GROUP_PAD = 16;
30973
+ const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
30974
+ const groups = [];
30975
+ for (const grp of parsed.groups) {
30976
+ let gx0 = Infinity, gy0 = Infinity, gx1 = -Infinity, gy1 = -Infinity;
30977
+ for (const c of grp.children) {
30978
+ const n2 = nodeByLabel.get(c);
30979
+ if (!n2) continue;
30980
+ gx0 = Math.min(gx0, n2.x - n2.width / 2);
30981
+ gx1 = Math.max(gx1, n2.x + n2.width / 2);
30982
+ gy0 = Math.min(gy0, n2.y - n2.height / 2);
30983
+ gy1 = Math.max(gy1, n2.y + n2.height / 2);
30984
+ }
30985
+ if (!Number.isFinite(gx0)) continue;
30986
+ const x0 = gx0 - GROUP_PAD;
30987
+ const x1 = gx1 + GROUP_PAD;
30988
+ const y0 = gy0 - GROUP_LABEL_ZONE2;
30989
+ const y1 = gy1 + GROUP_PAD;
30990
+ groups.push({
30991
+ label: grp.label,
30992
+ lineNumber: grp.lineNumber,
30993
+ x: (x0 + x1) / 2,
30994
+ y: (y0 + y1) / 2,
30995
+ width: x1 - x0,
30996
+ height: y1 - y0,
30997
+ collapsed: false,
30998
+ childCount: grp.children.length
30999
+ });
31000
+ }
31001
+ for (const cb of collapsedBoxes) {
31002
+ groups.push({
31003
+ label: cb.label,
31004
+ lineNumber: cb.lineNumber,
31005
+ x: cb.x,
31006
+ y: cb.y,
31007
+ width: NODE_WIDTH,
31008
+ height: NODE_HEIGHT,
31009
+ collapsed: true,
31010
+ childCount: cb.childCount
31011
+ });
31012
+ }
31013
+ const M = 40;
31014
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
31015
+ const acc = (x, y) => {
31016
+ if (x < minX) minX = x;
31017
+ if (x > maxX) maxX = x;
31018
+ if (y < minY) minY = y;
31019
+ if (y > maxY) maxY = y;
31020
+ };
31021
+ for (const n2 of nodes) {
31022
+ acc(n2.x - n2.width / 2, n2.y - n2.height / 2);
31023
+ acc(n2.x + n2.width / 2, n2.y + n2.height / 2);
31024
+ }
31025
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
31026
+ for (const gr of groups) {
31027
+ acc(gr.x - gr.width / 2, gr.y - gr.height / 2);
31028
+ acc(gr.x + gr.width / 2, gr.y + gr.height / 2);
31029
+ }
31030
+ const TOL = 2;
31031
+ const sx = minX < M - TOL ? M - minX : 0;
31032
+ const sy = minY < M - TOL ? M - minY : 0;
31033
+ const shifted = sx !== 0 || sy !== 0;
31034
+ return {
31035
+ nodes: shifted ? nodes.map((n2) => ({ ...n2, x: n2.x + sx, y: n2.y + sy })) : nodes,
31036
+ edges: shifted ? edges.map((e) => ({
31037
+ ...e,
31038
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
31039
+ })) : edges,
31040
+ groups: shifted ? groups.map((gr) => ({ ...gr, x: gr.x + sx, y: gr.y + sy })) : groups,
31041
+ width: maxX + sx + M,
31042
+ height: maxY + sy + M
31043
+ };
31044
+ }
30287
31045
  function place(cfg) {
30288
31046
  const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
30289
31047
  const ord = (a) => r ? shuffle(a, r) : a.slice();
@@ -30422,6 +31180,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30422
31180
  height: gg.height ?? 600
30423
31181
  };
30424
31182
  }
31183
+ if (allPinned) return placePinned(pinned);
30425
31184
  const n = parsed.nodes.length;
30426
31185
  const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
30427
31186
  const REFINE_K = opts?.refineK ?? 6;
@@ -30444,17 +31203,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30444
31203
  seed: s
30445
31204
  });
30446
31205
  const badness = (lay, floor) => {
30447
- const x = countSplineCrossings(lay);
31206
+ const x = countSplineCrossings(lay, floor);
30448
31207
  if (x > floor) return Infinity;
30449
31208
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30450
31209
  };
30451
31210
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31211
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31212
+ let progressDone = 0;
31213
+ const step = async (phase) => {
31214
+ if (!onProgress) return;
31215
+ onProgress(++progressDone, progressTotal, phase);
31216
+ await tick();
31217
+ };
30452
31218
  const pool = [];
30453
31219
  for (const cfg of configs) {
30454
31220
  try {
30455
31221
  pool.push(place(cfg));
30456
31222
  } catch {
30457
31223
  }
31224
+ await step("Optimizing layout");
30458
31225
  }
30459
31226
  if (!pool.length)
30460
31227
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30463,9 +31230,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30463
31230
  layered = layeredCandidates(parsed, sizes);
30464
31231
  } catch {
30465
31232
  }
30466
- pool.sort(
30467
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30468
- );
31233
+ const fastKey = /* @__PURE__ */ new Map();
31234
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31235
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30469
31236
  const refineK = Math.min(REFINE_K, pool.length);
30470
31237
  let best = pool[0];
30471
31238
  let bestObj = Infinity;
@@ -30480,7 +31247,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30480
31247
  best = lay;
30481
31248
  }
30482
31249
  };
30483
- for (const lay of pool.slice(0, refineK)) consider(lay);
31250
+ for (const lay of pool.slice(0, refineK)) {
31251
+ consider(lay);
31252
+ await step("Refining layout");
31253
+ }
30484
31254
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30485
31255
  const extra = [];
30486
31256
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30496,9 +31266,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30496
31266
  } catch {
30497
31267
  }
30498
31268
  }
30499
- extra.sort(
30500
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30501
- );
31269
+ const extraKey = /* @__PURE__ */ new Map();
31270
+ for (const lay of extra)
31271
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31272
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30502
31273
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30503
31274
  }
30504
31275
  for (const lay of layered) {
@@ -30525,7 +31296,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30525
31296
  }
30526
31297
  return best;
30527
31298
  }
30528
- var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31299
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30529
31300
  var init_layout_search = __esm({
30530
31301
  "src/boxes-and-lines/layout-search.ts"() {
30531
31302
  "use strict";
@@ -30537,6 +31308,8 @@ var init_layout_search = __esm({
30537
31308
  ESCALATE_SEEDS = 18;
30538
31309
  ESCALATE_REFINE = 10;
30539
31310
  splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
31311
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31312
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30540
31313
  GROUP_LABEL_ZONE2 = 32;
30541
31314
  }
30542
31315
  });
@@ -30616,17 +31389,20 @@ function computeNodeSize(node, reserveValueRow) {
30616
31389
  }
30617
31390
  totalRenderedLines = Math.min(totalRenderedLines, MAX_DESC_LINES2);
30618
31391
  const descriptionHeight = totalRenderedLines * DESC_FONT_SIZE2 * DESC_LINE_HEIGHT2;
30619
- const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
31392
+ const totalHeight = labelHeight + SEPARATOR_GAP4 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
30620
31393
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
30621
31394
  }
30622
31395
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30623
31396
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30624
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31397
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30625
31398
  ...layoutOptions?.hideDescriptions !== void 0 && {
30626
31399
  hideDescriptions: layoutOptions.hideDescriptions
30627
31400
  },
30628
31401
  ...layoutOptions?.previousPositions !== void 0 && {
30629
31402
  previousPositions: layoutOptions.previousPositions
31403
+ },
31404
+ ...layoutOptions?.onProgress !== void 0 && {
31405
+ onProgress: layoutOptions.onProgress
30630
31406
  }
30631
31407
  });
30632
31408
  return attachNotes(
@@ -30745,7 +31521,7 @@ function applyParallelEdgeOffsets(layout) {
30745
31521
  }))
30746
31522
  };
30747
31523
  }
30748
- 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;
31524
+ 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;
30749
31525
  var init_layout5 = __esm({
30750
31526
  "src/boxes-and-lines/layout.ts"() {
30751
31527
  "use strict";
@@ -30761,13 +31537,13 @@ var init_layout5 = __esm({
30761
31537
  DESC_FONT_SIZE2 = 10;
30762
31538
  DESC_LINE_HEIGHT2 = 1.4;
30763
31539
  DESC_PADDING = 8;
30764
- SEPARATOR_GAP5 = 4;
31540
+ SEPARATOR_GAP4 = 4;
30765
31541
  MAX_DESC_LINES2 = 6;
30766
31542
  MAX_LABEL_LINES = 3;
30767
31543
  LABEL_LINE_HEIGHT = 1.3;
30768
31544
  LABEL_PAD = 12;
30769
31545
  VALUE_ROW_FONT = 11;
30770
- VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
31546
+ VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
30771
31547
  }
30772
31548
  });
30773
31549
 
@@ -31411,7 +32187,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31411
32187
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31412
32188
  const availWidth = containerWidth;
31413
32189
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31414
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32190
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32191
+ availWidth,
32192
+ layout.width,
32193
+ availHeight,
32194
+ layout.height
32195
+ );
31415
32196
  let renderLayout = layout;
31416
32197
  if (ctx.factor < 1) {
31417
32198
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31420,17 +32201,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31420
32201
  hiddenCounts.set(n.id, n.hiddenCount);
31421
32202
  }
31422
32203
  }
31423
- renderLayout = layoutMindmap(parsed, palette, {
32204
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31424
32205
  interactive: !isExport,
31425
32206
  ...hiddenCounts.size > 0 && { hiddenCounts },
31426
32207
  activeTagGroup: activeTagGroup ?? null,
31427
32208
  ...hideDescriptions !== void 0 && { hideDescriptions },
31428
- ctx
32209
+ ctx: c
31429
32210
  });
32211
+ renderLayout = relayout(ctx);
32212
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32213
+ const refit = Math.min(
32214
+ availWidth / renderLayout.width,
32215
+ availHeight / renderLayout.height
32216
+ );
32217
+ if (refit >= 0.999) break;
32218
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32219
+ renderLayout = relayout(ctx);
32220
+ }
31430
32221
  }
31431
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31432
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31433
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32222
+ const fitScale = isExport ? 1 : Math.min(
32223
+ 1,
32224
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32225
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32226
+ );
32227
+ const scaledWidth = renderLayout.width * fitScale;
32228
+ const scaledHeight = renderLayout.height * fitScale;
32229
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32230
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32231
+ const mainG = svg.append("g").attr(
32232
+ "transform",
32233
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32234
+ );
31434
32235
  if (ctx.isBelowFloor) {
31435
32236
  svg.attr("width", "100%");
31436
32237
  }
@@ -31529,11 +32330,11 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31529
32330
  });
31530
32331
  }
31531
32332
  for (const edge of renderLayout.edges) {
31532
- 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);
32333
+ 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);
31533
32334
  }
31534
32335
  for (const node of renderLayout.nodes) {
31535
32336
  const isRoot = node.radius === 0 && renderLayout.nodes.indexOf(node) === 0;
31536
- const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH6;
32337
+ const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH;
31537
32338
  const effectiveColor = options?.colorByDepth ? depthColor(node.depth, palette) : node.color;
31538
32339
  const fill2 = nodeFill4(
31539
32340
  palette,
@@ -31616,7 +32417,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31616
32417
  const clipId = `collapse-clip-${node.id}`;
31617
32418
  const defs = mainG.append("defs");
31618
32419
  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);
31619
- 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})`);
32420
+ 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})`);
31620
32421
  }
31621
32422
  if (onClickItem) {
31622
32423
  nodeG.style("cursor", "pointer").on("click", (event) => {
@@ -31664,7 +32465,7 @@ function renderMindmapForExport(content, theme, palette) {
31664
32465
  return extractExportSvg(container, theme);
31665
32466
  });
31666
32467
  }
31667
- var 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;
32468
+ var DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
31668
32469
  var init_renderer7 = __esm({
31669
32470
  "src/mindmap/renderer.ts"() {
31670
32471
  "use strict";
@@ -31681,6 +32482,7 @@ var init_renderer7 = __esm({
31681
32482
  init_legend_layout();
31682
32483
  init_title_constants();
31683
32484
  init_scaling();
32485
+ init_visual_conventions();
31684
32486
  DIAGRAM_PADDING7 = 20;
31685
32487
  TITLE_HEIGHT4 = 30;
31686
32488
  SINGLE_LABEL_HEIGHT2 = 28;
@@ -31688,9 +32490,6 @@ var init_renderer7 = __esm({
31688
32490
  DESC_LINE_HEIGHT4 = 14;
31689
32491
  NODE_RADIUS = 6;
31690
32492
  ROOT_STROKE_WIDTH = 2.5;
31691
- NODE_STROKE_WIDTH6 = 1.5;
31692
- EDGE_STROKE_WIDTH6 = 1.5;
31693
- COLLAPSE_BAR_HEIGHT4 = 6;
31694
32493
  DEPTH_COLOR_KEYS = [
31695
32494
  "red",
31696
32495
  "orange",
@@ -32919,10 +33718,10 @@ function computeC4NodeDimensions(el, options) {
32919
33718
  const metaEntries = collectCardMetadata(el.metadata);
32920
33719
  if (metaEntries.length > 0) {
32921
33720
  height2 += DIVIDER_GAP;
32922
- height2 += metaEntries.length * META_LINE_HEIGHT5;
33721
+ height2 += metaEntries.length * META_LINE_HEIGHT4;
32923
33722
  const maxMetaWidth = Math.max(
32924
33723
  ...metaEntries.map(
32925
- (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE5) + CARD_H_PAD3 * 2
33724
+ (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE4) + CARD_H_PAD3 * 2
32926
33725
  )
32927
33726
  );
32928
33727
  if (maxMetaWidth > width) width = Math.min(MAX_NODE_WIDTH, maxMetaWidth);
@@ -34199,7 +34998,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34199
34998
  height: totalHeight
34200
34999
  };
34201
35000
  }
34202
- var 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;
35001
+ var 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;
34203
35002
  var init_layout8 = __esm({
34204
35003
  "src/c4/layout.ts"() {
34205
35004
  "use strict";
@@ -34217,8 +35016,8 @@ var init_layout8 = __esm({
34217
35016
  DESC_FONT_SIZE4 = 11;
34218
35017
  CARD_V_PAD3 = 14;
34219
35018
  CARD_H_PAD3 = 20;
34220
- META_LINE_HEIGHT5 = 16;
34221
- META_FONT_SIZE5 = 11;
35019
+ META_LINE_HEIGHT4 = 16;
35020
+ META_FONT_SIZE4 = 11;
34222
35021
  MARGIN6 = 40;
34223
35022
  BOUNDARY_PAD = 40;
34224
35023
  GROUP_BOUNDARY_PAD = 24;
@@ -34341,7 +35140,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34341
35140
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34342
35141
  const pathD = lineGenerator4(edge.points);
34343
35142
  if (pathD) {
34344
- 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)");
35143
+ 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)");
34345
35144
  if (dashed) {
34346
35145
  pathEl.attr("stroke-dasharray", "6 3");
34347
35146
  }
@@ -34407,7 +35206,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34407
35206
  palette.textOnFillLight,
34408
35207
  palette.textOnFillDark
34409
35208
  );
34410
- 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);
35209
+ 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);
34411
35210
  let yPos = -h / 2 + CARD_V_PAD4;
34412
35211
  const typeLabel = `\xAB${node.type}\xBB`;
34413
35212
  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);
@@ -34451,7 +35250,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34451
35250
  }
34452
35251
  if (node.drillable) {
34453
35252
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
34454
- 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);
35253
+ 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);
34455
35254
  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");
34456
35255
  }
34457
35256
  }
@@ -34511,14 +35310,14 @@ function drawCylinderCard(nodeG, w, h, fill2, stroke2, dashed) {
34511
35310
  `A ${w / 2} ${ry} 0 0 1 ${-w / 2} ${h / 2 - ry}`,
34512
35311
  "Z"
34513
35312
  ].join(" ");
34514
- const el = nodeG.append("path").attr("d", path).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35313
+ const el = nodeG.append("path").attr("d", path).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34515
35314
  if (dashed) {
34516
35315
  el.attr("stroke-dasharray", "6 3");
34517
35316
  }
34518
- 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);
35317
+ 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);
34519
35318
  }
34520
35319
  function drawCardRect(nodeG, w, h, fill2, stroke2, dashed) {
34521
- 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);
35320
+ 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);
34522
35321
  if (dashed) {
34523
35322
  el.attr("stroke-dasharray", "6 3");
34524
35323
  }
@@ -34538,7 +35337,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
34538
35337
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34539
35338
  const pathD = lineGenerator4(edge.points);
34540
35339
  if (pathD) {
34541
- 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)");
35340
+ 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)");
34542
35341
  if (dashed) {
34543
35342
  pathEl.attr("stroke-dasharray", "6 3");
34544
35343
  }
@@ -35033,13 +35832,13 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35033
35832
  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);
35034
35833
  yPos += DIVIDER_GAP2;
35035
35834
  const maxKeyWidth = Math.max(
35036
- ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE6))
35835
+ ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE2))
35037
35836
  );
35038
35837
  const valueX = -w / 2 + CARD_H_PAD4 + maxKeyWidth;
35039
35838
  for (const entry of metaEntries) {
35040
- 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}:`);
35041
- 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);
35042
- yPos += META_LINE_HEIGHT6;
35839
+ 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}:`);
35840
+ 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);
35841
+ yPos += META_LINE_HEIGHT2;
35043
35842
  }
35044
35843
  }
35045
35844
  } else {
@@ -35066,7 +35865,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
35066
35865
  }
35067
35866
  if (node.drillable) {
35068
35867
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
35069
- 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);
35868
+ 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);
35070
35869
  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");
35071
35870
  }
35072
35871
  }
@@ -35195,7 +35994,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
35195
35994
  document.body.removeChild(el);
35196
35995
  }
35197
35996
  }
35198
- var 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;
35997
+ var 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;
35199
35998
  var init_renderer9 = __esm({
35200
35999
  "src/c4/renderer.ts"() {
35201
36000
  "use strict";
@@ -35209,6 +36008,7 @@ var init_renderer9 = __esm({
35209
36008
  init_legend_constants();
35210
36009
  init_legend_integration();
35211
36010
  init_title_constants();
36011
+ init_visual_conventions();
35212
36012
  DIAGRAM_PADDING8 = 20;
35213
36013
  MAX_SCALE5 = 3;
35214
36014
  TITLE_HEIGHT6 = 30;
@@ -35218,16 +36018,11 @@ var init_renderer9 = __esm({
35218
36018
  DESC_LINE_HEIGHT6 = 16;
35219
36019
  EDGE_LABEL_FONT_SIZE6 = 11;
35220
36020
  TECH_FONT_SIZE = 10;
35221
- EDGE_STROKE_WIDTH7 = 1.5;
35222
- NODE_STROKE_WIDTH7 = 1.5;
35223
- CARD_RADIUS4 = 6;
35224
36021
  CARD_H_PAD4 = 20;
35225
36022
  CARD_V_PAD4 = 14;
35226
36023
  TYPE_LABEL_HEIGHT2 = 18;
35227
36024
  DIVIDER_GAP2 = 6;
35228
36025
  NAME_HEIGHT2 = 20;
35229
- META_FONT_SIZE6 = 11;
35230
- META_LINE_HEIGHT6 = 16;
35231
36026
  BOUNDARY_LABEL_FONT_SIZE = 12;
35232
36027
  BOUNDARY_STROKE_WIDTH = 1.5;
35233
36028
  BOUNDARY_RADIUS = 8;
@@ -35632,7 +36427,7 @@ function nodeFill6(palette, isDark, shape, nodeColor2, isEndTerminal, colorOff,
35632
36427
  function nodeStroke6(palette, shape, nodeColor2, isEndTerminal, colorOff) {
35633
36428
  return nodeColor2 ?? shapeDefaultColor(shape, palette, isEndTerminal, colorOff);
35634
36429
  }
35635
- function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36430
+ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35636
36431
  const w = node.width;
35637
36432
  const h = node.height;
35638
36433
  const rx = h / 2;
@@ -35644,7 +36439,7 @@ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeS
35644
36439
  nodeStroke6(palette, node.shape, node.color, isEnd, colorOff)
35645
36440
  ).attr("stroke-width", sNodeStrokeWidth);
35646
36441
  }
35647
- function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36442
+ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35648
36443
  const w = node.width;
35649
36444
  const h = node.height;
35650
36445
  g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr(
@@ -35663,7 +36458,7 @@ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWid
35663
36458
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35664
36459
  ).attr("stroke-width", sNodeStrokeWidth);
35665
36460
  }
35666
- function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36461
+ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35667
36462
  const w = node.width / 2;
35668
36463
  const h = node.height / 2;
35669
36464
  const points = [`${0},${-h}`, `${w},${0}`, `${0},${h}`, `${-w},${0}`].join(
@@ -35685,7 +36480,7 @@ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35685
36480
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35686
36481
  ).attr("stroke-width", sNodeStrokeWidth);
35687
36482
  }
35688
- function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sIoSkew = IO_SKEW) {
36483
+ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sIoSkew = IO_SKEW) {
35689
36484
  const w = node.width / 2;
35690
36485
  const h = node.height / 2;
35691
36486
  const sk = sIoSkew;
@@ -35711,7 +36506,7 @@ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth =
35711
36506
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35712
36507
  ).attr("stroke-width", sNodeStrokeWidth);
35713
36508
  }
35714
- function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sSubroutineInset = SUBROUTINE_INSET) {
36509
+ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sSubroutineInset = SUBROUTINE_INSET) {
35715
36510
  const w = node.width;
35716
36511
  const h = node.height;
35717
36512
  const s = nodeStroke6(palette, node.shape, node.color, void 0, colorOff);
@@ -35729,7 +36524,7 @@ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStroke
35729
36524
  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);
35730
36525
  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);
35731
36526
  }
35732
- function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sDocWaveHeight = DOC_WAVE_HEIGHT) {
36527
+ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sDocWaveHeight = DOC_WAVE_HEIGHT) {
35733
36528
  const w = node.width;
35734
36529
  const h = node.height;
35735
36530
  const waveH = sDocWaveHeight;
@@ -35760,7 +36555,7 @@ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35760
36555
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35761
36556
  ).attr("stroke-width", sNodeStrokeWidth);
35762
36557
  }
35763
- function renderNodeShape(g, node, palette, isDark, endTerminalIds, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sIoSkew = IO_SKEW, sSubroutineInset = SUBROUTINE_INSET, sDocWaveHeight = DOC_WAVE_HEIGHT) {
36558
+ function renderNodeShape(g, node, palette, isDark, endTerminalIds, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sIoSkew = IO_SKEW, sSubroutineInset = SUBROUTINE_INSET, sDocWaveHeight = DOC_WAVE_HEIGHT) {
35764
36559
  switch (node.shape) {
35765
36560
  case "terminal":
35766
36561
  renderTerminal(
@@ -35847,8 +36642,8 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
35847
36642
  const sTitleY = ctx.structural(TITLE_Y);
35848
36643
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
35849
36644
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE7);
35850
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH8);
35851
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH8);
36645
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
36646
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
35852
36647
  const sArrowheadW = ctx.structural(ARROWHEAD_W3);
35853
36648
  const sArrowheadH = ctx.structural(ARROWHEAD_H3);
35854
36649
  const sIoSkew = ctx.structural(IO_SKEW);
@@ -36072,7 +36867,7 @@ function renderFlowchartForExport(content, theme, palette) {
36072
36867
  document.body.removeChild(container);
36073
36868
  }
36074
36869
  }
36075
- var 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;
36870
+ var DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
36076
36871
  var init_flowchart_renderer = __esm({
36077
36872
  "src/graph/flowchart-renderer.ts"() {
36078
36873
  "use strict";
@@ -36087,12 +36882,11 @@ var init_flowchart_renderer = __esm({
36087
36882
  init_scaling();
36088
36883
  init_text_measure();
36089
36884
  init_note_box();
36885
+ init_visual_conventions();
36090
36886
  DIAGRAM_PADDING9 = 20;
36091
36887
  MAX_SCALE6 = 3;
36092
36888
  NODE_FONT_SIZE2 = 13;
36093
36889
  EDGE_LABEL_FONT_SIZE7 = 11;
36094
- EDGE_STROKE_WIDTH8 = 1.5;
36095
- NODE_STROKE_WIDTH8 = 1.5;
36096
36890
  ARROWHEAD_W3 = 10;
36097
36891
  ARROWHEAD_H3 = 7;
36098
36892
  IO_SKEW = 15;
@@ -37077,7 +37871,7 @@ function hasRoles(node) {
37077
37871
  }
37078
37872
  function computeNodeWidth2(node, expanded, options) {
37079
37873
  const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
37080
- const badgeWidth = badgeVal > 0 ? measureText(`${badgeVal}x`, META_FONT_SIZE7) + 2 * CHAR_WIDTH_RATIO * NODE_FONT_SIZE3 : 0;
37874
+ const badgeWidth = badgeVal > 0 ? measureText(`${badgeVal}x`, META_FONT_SIZE5) + 2 * CHAR_WIDTH_RATIO * NODE_FONT_SIZE3 : 0;
37081
37875
  const labelWidth2 = measureText(node.label, NODE_FONT_SIZE3) + badgeWidth + PADDING_X3;
37082
37876
  const allKeys = [];
37083
37877
  if (node.computedRps > 0) allKeys.push("RPS");
@@ -37124,7 +37918,7 @@ function computeNodeWidth2(node, expanded, options) {
37124
37918
  }
37125
37919
  if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
37126
37920
  const keyColWidth = Math.max(
37127
- ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE7))
37921
+ ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE5))
37128
37922
  );
37129
37923
  let maxRowWidth = 0;
37130
37924
  if (node.computedRps > 0) {
@@ -37137,7 +37931,7 @@ function computeNodeWidth2(node, expanded, options) {
37137
37931
  const rpsVal = effectiveCap > 0 && !node.isEdge ? `${formatRpsShort(node.computedRps)} / ${formatRpsShort(effectiveCap)}` : formatRps(node.computedRps);
37138
37932
  maxRowWidth = Math.max(
37139
37933
  maxRowWidth,
37140
- keyColWidth + measureText(rpsVal, META_FONT_SIZE7)
37934
+ keyColWidth + measureText(rpsVal, META_FONT_SIZE5)
37141
37935
  );
37142
37936
  }
37143
37937
  if (expanded) {
@@ -37154,7 +37948,7 @@ function computeNodeWidth2(node, expanded, options) {
37154
37948
  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);
37155
37949
  maxRowWidth = Math.max(
37156
37950
  maxRowWidth,
37157
- keyColWidth + measureText(valStr, META_FONT_SIZE7)
37951
+ keyColWidth + measureText(valStr, META_FONT_SIZE5)
37158
37952
  );
37159
37953
  }
37160
37954
  }
@@ -37165,7 +37959,7 @@ function computeNodeWidth2(node, expanded, options) {
37165
37959
  if (ms > 0) {
37166
37960
  maxRowWidth = Math.max(
37167
37961
  maxRowWidth,
37168
- keyColWidth + measureText(formatMs(ms), META_FONT_SIZE7)
37962
+ keyColWidth + measureText(formatMs(ms), META_FONT_SIZE5)
37169
37963
  );
37170
37964
  }
37171
37965
  }
@@ -37176,37 +37970,37 @@ function computeNodeWidth2(node, expanded, options) {
37176
37970
  const combinedVal = `${formatMs(perc.p90)} / ${formatMs(threshold)}`;
37177
37971
  maxRowWidth = Math.max(
37178
37972
  maxRowWidth,
37179
- keyColWidth + measureText(combinedVal, META_FONT_SIZE7)
37973
+ keyColWidth + measureText(combinedVal, META_FONT_SIZE5)
37180
37974
  );
37181
37975
  }
37182
37976
  }
37183
37977
  if (node.computedUptime < 1) {
37184
37978
  maxRowWidth = Math.max(
37185
37979
  maxRowWidth,
37186
- keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE7)
37980
+ keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE5)
37187
37981
  );
37188
37982
  }
37189
37983
  if (node.computedAvailability < 1) {
37190
37984
  maxRowWidth = Math.max(
37191
37985
  maxRowWidth,
37192
- keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE7)
37986
+ keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE5)
37193
37987
  );
37194
37988
  }
37195
37989
  if (node.computedCbState === "open") {
37196
37990
  maxRowWidth = Math.max(
37197
37991
  maxRowWidth,
37198
- measureText("CB: OPEN", META_FONT_SIZE7) + 8
37992
+ measureText("CB: OPEN", META_FONT_SIZE5) + 8
37199
37993
  );
37200
37994
  }
37201
37995
  }
37202
- const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE7;
37996
+ const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE5;
37203
37997
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
37204
37998
  let descWidth = 0;
37205
37999
  for (const dl of descLines) {
37206
- const truncated = truncateText(dl, META_FONT_SIZE7, DESC_MAX_WIDTH2);
38000
+ const truncated = truncateText(dl, META_FONT_SIZE5, DESC_MAX_WIDTH2);
37207
38001
  descWidth = Math.max(
37208
38002
  descWidth,
37209
- measureText(truncated, META_FONT_SIZE7) + PADDING_X3
38003
+ measureText(truncated, META_FONT_SIZE5) + PADDING_X3
37210
38004
  );
37211
38005
  }
37212
38006
  return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
@@ -37216,17 +38010,17 @@ function computeNodeHeight2(node, expanded, options) {
37216
38010
  const computedCount = countComputedRows(node, expanded);
37217
38011
  const hasRps = node.computedRps > 0;
37218
38012
  const descLineCount = expanded && node.description && !node.isEdge ? node.description.length : 0;
37219
- const descH = descLineCount * META_LINE_HEIGHT7;
38013
+ const descH = descLineCount * META_LINE_HEIGHT5;
37220
38014
  if (propCount === 0 && computedCount === 0 && !hasRps)
37221
38015
  return NODE_HEADER_HEIGHT + descH + NODE_PAD_BOTTOM;
37222
38016
  let h = NODE_HEADER_HEIGHT + descH + NODE_SEPARATOR_GAP;
37223
38017
  const computedSectionCount = (hasRps ? 1 : 0) + computedCount;
37224
- h += computedSectionCount * META_LINE_HEIGHT7;
38018
+ h += computedSectionCount * META_LINE_HEIGHT5;
37225
38019
  if (computedSectionCount > 0 && propCount > 0) h += NODE_SEPARATOR_GAP;
37226
- h += propCount * META_LINE_HEIGHT7;
38020
+ h += propCount * META_LINE_HEIGHT5;
37227
38021
  if (hasRoles(node)) h += ROLE_DOT_ROW;
37228
38022
  h += NODE_PAD_BOTTOM;
37229
- if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT5;
38023
+ if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT3;
37230
38024
  return h;
37231
38025
  }
37232
38026
  function formatRps(rps) {
@@ -37561,20 +38355,20 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
37561
38355
  height: totalHeight
37562
38356
  };
37563
38357
  }
37564
- var 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;
38358
+ var 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;
37565
38359
  var init_layout10 = __esm({
37566
38360
  "src/infra/layout.ts"() {
37567
38361
  "use strict";
37568
38362
  init_text_measure();
37569
38363
  MIN_NODE_WIDTH2 = 140;
37570
38364
  NODE_HEADER_HEIGHT = 28;
37571
- META_LINE_HEIGHT7 = 14;
38365
+ META_LINE_HEIGHT5 = 14;
37572
38366
  NODE_SEPARATOR_GAP = 4;
37573
38367
  NODE_PAD_BOTTOM = 10;
37574
38368
  ROLE_DOT_ROW = 12;
37575
- COLLAPSE_BAR_HEIGHT5 = 6;
38369
+ COLLAPSE_BAR_HEIGHT3 = 6;
37576
38370
  NODE_FONT_SIZE3 = 13;
37577
- META_FONT_SIZE7 = 10;
38371
+ META_FONT_SIZE5 = 10;
37578
38372
  EDGE_LABEL_FONT_SIZE8 = 11;
37579
38373
  PADDING_X3 = 24;
37580
38374
  GROUP_PADDING2 = 20;
@@ -37680,19 +38474,19 @@ import * as d3Shape8 from "d3-shape";
37680
38474
  function buildScaledConstants(ctx) {
37681
38475
  return {
37682
38476
  sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
37683
- sMetaFontSize: ctx.text(META_FONT_SIZE8),
37684
- sMetaLineHeight: ctx.structural(META_LINE_HEIGHT8),
38477
+ sMetaFontSize: ctx.text(META_FONT_SIZE6),
38478
+ sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
37685
38479
  sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE9),
37686
38480
  sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
37687
38481
  sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
37688
- sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH9),
37689
- sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH9),
38482
+ sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
38483
+ sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH),
37690
38484
  sOverloadStrokeWidth: ctx.structural(OVERLOAD_STROKE_WIDTH),
37691
38485
  sRoleDotRadius: ctx.structural(ROLE_DOT_RADIUS),
37692
38486
  sNodeSeparatorGap: ctx.structural(NODE_SEPARATOR_GAP2),
37693
38487
  sNodePadBottom: ctx.structural(NODE_PAD_BOTTOM2),
37694
- sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT6),
37695
- sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET2),
38488
+ sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT),
38489
+ sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET),
37696
38490
  sParticleR: ctx.structural(PARTICLE_R),
37697
38491
  sRejectParticleR: ctx.structural(PARTICLE_R),
37698
38492
  sRejectDropDistance: ctx.structural(REJECT_DROP_DISTANCE),
@@ -38016,7 +38810,7 @@ function isWarning(node) {
38016
38810
  return cap > 0 && node.computedRps / cap > 0.7;
38017
38811
  }
38018
38812
  function truncateDesc(text) {
38019
- return truncateText(text, META_FONT_SIZE8, DESC_MAX_WIDTH);
38813
+ return truncateText(text, META_FONT_SIZE6, DESC_MAX_WIDTH);
38020
38814
  }
38021
38815
  function sloLatencyColor(p90, slo) {
38022
38816
  const t = slo.latencyP90 ?? 0;
@@ -38495,7 +39289,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38495
39289
  if (!isNodeCollapsed) {
38496
39290
  const expanded = expandedNodeIds?.has(node.id) ?? false;
38497
39291
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
38498
- const descH = descLines.length * META_LINE_HEIGHT8;
39292
+ const descH = descLines.length * META_LINE_HEIGHT6;
38499
39293
  for (let di = 0; di < descLines.length; di++) {
38500
39294
  const rawLine = descLines[di];
38501
39295
  const processed = preprocessDescriptionLine(rawLine);
@@ -38503,7 +39297,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38503
39297
  const isTruncated = descTruncated !== processed;
38504
39298
  const textEl = g.append("text").attr("x", node.x).attr(
38505
39299
  "y",
38506
- y + NODE_HEADER_HEIGHT2 + di * META_LINE_HEIGHT8 + META_LINE_HEIGHT8 / 2 + sc.sMetaFontSize * 0.35
39300
+ y + NODE_HEADER_HEIGHT2 + di * META_LINE_HEIGHT6 + META_LINE_HEIGHT6 / 2 + sc.sMetaFontSize * 0.35
38507
39301
  ).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sMetaFontSize).attr("fill", textFill);
38508
39302
  renderInlineText(textEl, descTruncated, palette, sc.sMetaFontSize);
38509
39303
  if (isTruncated) textEl.append("title").text(rawLine);
@@ -39021,7 +39815,7 @@ function parseAndLayoutInfra(content) {
39021
39815
  const layout = layoutInfra(computed);
39022
39816
  return { parsed, computed, layout };
39023
39817
  }
39024
- var 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;
39818
+ var 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;
39025
39819
  var init_renderer10 = __esm({
39026
39820
  "src/infra/renderer.ts"() {
39027
39821
  "use strict";
@@ -39040,21 +39834,18 @@ var init_renderer10 = __esm({
39040
39834
  init_legend_layout();
39041
39835
  init_title_constants();
39042
39836
  init_scaling();
39837
+ init_visual_conventions();
39043
39838
  NODE_FONT_SIZE4 = 13;
39044
- META_FONT_SIZE8 = 10;
39045
- META_LINE_HEIGHT8 = 14;
39839
+ META_FONT_SIZE6 = 10;
39840
+ META_LINE_HEIGHT6 = 14;
39046
39841
  EDGE_LABEL_FONT_SIZE9 = 11;
39047
39842
  GROUP_LABEL_FONT_SIZE2 = 14;
39048
39843
  NODE_BORDER_RADIUS = 8;
39049
- EDGE_STROKE_WIDTH9 = 1.5;
39050
- NODE_STROKE_WIDTH9 = 1.5;
39051
39844
  OVERLOAD_STROKE_WIDTH = 3;
39052
39845
  ROLE_DOT_RADIUS = 3;
39053
39846
  NODE_HEADER_HEIGHT2 = 28;
39054
39847
  NODE_SEPARATOR_GAP2 = 4;
39055
39848
  NODE_PAD_BOTTOM2 = 10;
39056
- COLLAPSE_BAR_HEIGHT6 = 6;
39057
- COLLAPSE_BAR_INSET2 = 0;
39058
39849
  LEGEND_FIXED_GAP3 = 16;
39059
39850
  SPEED_BADGE_H_PAD = 5;
39060
39851
  SPEED_BADGE_V_PAD = 3;
@@ -39093,7 +39884,7 @@ var init_renderer10 = __esm({
39093
39884
  partitions: "partitions"
39094
39885
  };
39095
39886
  DESC_MAX_CHARS = 120;
39096
- DESC_MAX_WIDTH = DESC_MAX_CHARS * CHAR_WIDTH_RATIO * META_FONT_SIZE8;
39887
+ DESC_MAX_WIDTH = DESC_MAX_CHARS * CHAR_WIDTH_RATIO * META_FONT_SIZE6;
39097
39888
  RPS_FORMAT_KEYS = /* @__PURE__ */ new Set(["max-rps", "ratelimit-rps"]);
39098
39889
  MS_FORMAT_KEYS = /* @__PURE__ */ new Set([
39099
39890
  "latency-ms",
@@ -40784,7 +41575,7 @@ __export(renderer_exports11, {
40784
41575
  import * as d3Selection17 from "d3-selection";
40785
41576
  import * as d3Shape9 from "d3-shape";
40786
41577
  function analysisBlockChrome(palette, isDark) {
40787
- const surfaceBg = isDark ? palette.surface : palette.bg;
41578
+ const surfaceBg = themeBaseBg(palette, isDark);
40788
41579
  return {
40789
41580
  fill: mix(palette.surface, palette.bg, 40),
40790
41581
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -40882,18 +41673,18 @@ function renderPert(container, resolved, layout, palette, isDark, options = {})
40882
41673
  const sLegendPillHeight = ctx.structural(LEGEND_PILL_HEIGHT);
40883
41674
  const sLegendBlockHeight = showTagLegend ? sLegendTopGap + sLegendPillHeight + sLegendBottomGap : 0;
40884
41675
  const sNodeRadius = ctx.structural(NODE_RADIUS2);
40885
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH10);
41676
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
40886
41677
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE5);
40887
41678
  const sNodeCellFontSize = ctx.text(NODE_CELL_FONT_SIZE2);
40888
41679
  const sNodeTopRowHeight = ctx.structural(NODE_TOP_ROW_HEIGHT);
40889
41680
  const sNodeBottomRowHeight = ctx.structural(NODE_BOTTOM_ROW_HEIGHT);
40890
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH10);
41681
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
40891
41682
  const sArrowheadW = ctx.structural(ARROWHEAD_W4);
40892
41683
  const sArrowheadH = ctx.structural(ARROWHEAD_H4);
40893
- const sContainerRadius = ctx.structural(CONTAINER_RADIUS3);
40894
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE4);
40895
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT3);
40896
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT7);
41684
+ const sContainerRadius = ctx.structural(CONTAINER_RADIUS);
41685
+ const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE);
41686
+ const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT);
41687
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
40897
41688
  const sPinIconW = ctx.structural(PIN_ICON_W);
40898
41689
  const sPinIconH = ctx.structural(PIN_ICON_H);
40899
41690
  const scaledWidth = layout.width + sDiagramPad * 2;
@@ -41460,7 +42251,7 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41460
42251
  palette.textOnFillDark
41461
42252
  );
41462
42253
  const sNR = sc.nodeRadius ?? NODE_RADIUS2;
41463
- const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT7;
42254
+ const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT;
41464
42255
  drawTextbookCard(g, {
41465
42256
  width: grp.width,
41466
42257
  height: grp.height,
@@ -41494,10 +42285,10 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41494
42285
  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})`);
41495
42286
  continue;
41496
42287
  }
41497
- const sCR = sc.containerRadius ?? CONTAINER_RADIUS3;
41498
- const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE4;
41499
- const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT3;
41500
- const sNSW = sc.nodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42288
+ const sCR = sc.containerRadius ?? CONTAINER_RADIUS;
42289
+ const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE;
42290
+ const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT;
42291
+ const sNSW = sc.nodeStrokeWidth ?? NODE_STROKE_WIDTH;
41501
42292
  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);
41502
42293
  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);
41503
42294
  }
@@ -41550,7 +42341,7 @@ function renderEdges2(root, resolved, layout, palette, collapsedSet, sc = {}) {
41550
42341
  }
41551
42342
  const path = lineGenerator6(e.points);
41552
42343
  if (!path) continue;
41553
- const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH10;
42344
+ const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH;
41554
42345
  const sELFS = sc.edgeLabelFontSize ?? 10;
41555
42346
  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 ?? "");
41556
42347
  const parsedEdge = edgeByKey.get(`${e.source}->${e.target}`);
@@ -41732,7 +42523,7 @@ function computeDurationEmphasis(activities) {
41732
42523
  function drawTextbookCard(g, a) {
41733
42524
  const { width: w, height: h, x, y } = a;
41734
42525
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41735
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42526
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41736
42527
  const sTRH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41737
42528
  const sBRH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41738
42529
  const sNFS = a.sNodeFontSize ?? NODE_FONT_SIZE5;
@@ -41831,7 +42622,7 @@ function drawTextbookCard(g, a) {
41831
42622
  function drawMilestonePill(g, a) {
41832
42623
  const { width: w, height: h, x, y } = a;
41833
42624
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41834
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42625
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41835
42626
  const topRowH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41836
42627
  const botRowH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41837
42628
  const sNCFS = a.sNodeCellFontSize ?? NODE_CELL_FONT_SIZE2;
@@ -41964,7 +42755,7 @@ function renderCaptionBlock(svg, bullets, args) {
41964
42755
  palette.textOnFillDark
41965
42756
  );
41966
42757
  const block = svg.append("g").attr("class", "pert-caption-block").attr("data-pert-caption", "");
41967
- 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);
42758
+ 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);
41968
42759
  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");
41969
42760
  const textX = x + CAPTION_BOX_PADDING_X;
41970
42761
  const firstBaselineY = y + CAPTION_BOX_PADDING_Y + CAPTION_HEADER_BAND_HEIGHT + CAPTION_FONT_SIZE;
@@ -42081,7 +42872,7 @@ function renderTornadoBlock(svg, rows, args) {
42081
42872
  palette.textOnFillDark
42082
42873
  );
42083
42874
  const block = svg.append("g").attr("class", "pert-tornado-block").attr("data-pert-tornado", "");
42084
- 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);
42875
+ 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);
42085
42876
  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");
42086
42877
  const fmt = (v) => {
42087
42878
  const r = Math.round(v * 100) / 100;
@@ -42237,7 +43028,7 @@ function renderScurveBlock(svg, data, args) {
42237
43028
  palette.textOnFillDark
42238
43029
  );
42239
43030
  const block = svg.append("g").attr("class", "pert-scurve-block").attr("data-pert-scurve", "");
42240
- 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);
43031
+ 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);
42241
43032
  const hasTitle = typeof title === "string" && title.length > 0;
42242
43033
  if (hasTitle) {
42243
43034
  const titleText = title.replace(/\.$/, "");
@@ -42421,7 +43212,7 @@ function formatScurveDate(iso) {
42421
43212
  if (month < 0 || month > 11 || isNaN(day)) return iso;
42422
43213
  return `${SCURVE_MONTH_NAMES[month]} ${day}`;
42423
43214
  }
42424
- var 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;
43215
+ var 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;
42425
43216
  var init_renderer11 = __esm({
42426
43217
  "src/pert/renderer.ts"() {
42427
43218
  "use strict";
@@ -42437,20 +43228,15 @@ var init_renderer11 = __esm({
42437
43228
  init_analyzer();
42438
43229
  init_layout11();
42439
43230
  init_internal();
43231
+ init_visual_conventions();
42440
43232
  DIAGRAM_PADDING11 = 20;
42441
43233
  NODE_FONT_SIZE5 = 13;
42442
43234
  NODE_CELL_FONT_SIZE2 = 11;
42443
43235
  NODE_RADIUS2 = 6;
42444
- NODE_STROKE_WIDTH10 = 1.5;
42445
43236
  NODE_TOP_ROW_HEIGHT = 26;
42446
43237
  NODE_BOTTOM_ROW_HEIGHT = 26;
42447
- EDGE_STROKE_WIDTH10 = 1.5;
42448
43238
  ARROWHEAD_W4 = 10;
42449
43239
  ARROWHEAD_H4 = 7;
42450
- CONTAINER_RADIUS3 = 8;
42451
- CONTAINER_LABEL_FONT_SIZE4 = 13;
42452
- CONTAINER_HEADER_HEIGHT3 = 28;
42453
- COLLAPSE_BAR_HEIGHT7 = 6;
42454
43240
  DURATION_FADE_OPACITY = 0.55;
42455
43241
  PIN_ICON_W = 13;
42456
43242
  PIN_ICON_H = 13;
@@ -45442,8 +46228,8 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45442
46228
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
45443
46229
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE10);
45444
46230
  const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
45445
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH11);
45446
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH11);
46231
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
46232
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
45447
46233
  const sArrowheadW = ctx.structural(ARROWHEAD_W5);
45448
46234
  const sArrowheadH = ctx.structural(ARROWHEAD_H5);
45449
46235
  const sPseudostateRadius = ctx.structural(PSEUDOSTATE_RADIUS);
@@ -45500,7 +46286,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45500
46286
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45501
46287
  const gw = group.width + sGroupExtraPadding * 2;
45502
46288
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45503
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46289
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45504
46290
  const strokeColor = group.color ?? palette.textMuted;
45505
46291
  const groupWrapper = contentG.append("g").attr("class", "st-group-wrapper").attr("data-line-number", String(group.lineNumber)).attr("data-group-id", group.id).attr("data-group-toggle", group.id).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", `Collapse group ${group.label}`).style("cursor", "pointer");
45506
46292
  groupWrapper.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "st-group");
@@ -45720,7 +46506,7 @@ function renderStateForExport(content, theme, palette) {
45720
46506
  document.body.removeChild(container);
45721
46507
  }
45722
46508
  }
45723
- var 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;
46509
+ var 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;
45724
46510
  var init_state_renderer = __esm({
45725
46511
  "src/graph/state-renderer.ts"() {
45726
46512
  "use strict";
@@ -45735,13 +46521,12 @@ var init_state_renderer = __esm({
45735
46521
  init_scaling();
45736
46522
  init_text_measure();
45737
46523
  init_note_box();
46524
+ init_visual_conventions();
45738
46525
  DIAGRAM_PADDING12 = 20;
45739
46526
  MAX_SCALE7 = 3;
45740
46527
  NODE_FONT_SIZE6 = 13;
45741
46528
  EDGE_LABEL_FONT_SIZE10 = 11;
45742
46529
  GROUP_LABEL_FONT_SIZE3 = 11;
45743
- EDGE_STROKE_WIDTH11 = 1.5;
45744
- NODE_STROKE_WIDTH11 = 1.5;
45745
46530
  ARROWHEAD_W5 = 10;
45746
46531
  ARROWHEAD_H5 = 7;
45747
46532
  PSEUDOSTATE_RADIUS = 10;
@@ -47456,14 +48241,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47456
48241
  const panelY = PADDING2;
47457
48242
  const textX = panelX + CARD_PADDING_X3;
47458
48243
  const clipId = "persona-clip";
47459
- defs.append("clipPath").attr("id", clipId).append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5);
48244
+ defs.append("clipPath").attr("id", clipId).append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS);
47460
48245
  const personaFill = shapeFill(palette, personaColor, isDark, { solid });
47461
48246
  const onPersonaText = contrastText(
47462
48247
  personaFill,
47463
48248
  palette.textOnFillLight,
47464
48249
  palette.textOnFillDark
47465
48250
  );
47466
- personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5).attr("fill", personaFill);
48251
+ personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", personaFill);
47467
48252
  if (descLines.length > 0) {
47468
48253
  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);
47469
48254
  }
@@ -47471,7 +48256,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47471
48256
  const silY = panelY + panelHeight / 2 - 6;
47472
48257
  const silClip = personaG.append("g").attr("clip-path", `url(#${clipId})`);
47473
48258
  renderPersonaSilhouette(silClip, silX, silY, personaColor, palette, 1.2);
47474
- 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);
48259
+ 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);
47475
48260
  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);
47476
48261
  for (let li = 0; li < descLines.length; li++) {
47477
48262
  const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
@@ -47711,7 +48496,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47711
48496
  { solid }
47712
48497
  );
47713
48498
  const rowStroke = stepColor ?? palette.textMuted;
47714
- 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);
48499
+ 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);
47715
48500
  const faceCx = listX + CARD_PADDING_X3 + COLLAPSED_FACE_R;
47716
48501
  const faceCy = itemY + COLLAPSED_CARD_H / 2;
47717
48502
  if (step.score !== void 0) {
@@ -47874,7 +48659,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47874
48659
  const scoreColor = scoreToColor(score, palette);
47875
48660
  const tintedBg = mix(scoreColor, palette.surface, 20);
47876
48661
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
47877
- 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);
48662
+ 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);
47878
48663
  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);
47879
48664
  const centerX = bx + bw / 2;
47880
48665
  for (let i = 0; i < lines.length; i++) {
@@ -48031,7 +48816,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
48031
48816
  palette.textOnFillLight,
48032
48817
  palette.textOnFillDark
48033
48818
  );
48034
- 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);
48819
+ 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);
48035
48820
  const titleMaxW = sl.width - CARD_PADDING_X3 * 2;
48036
48821
  const titleLines = wrapTextToWidth(sl.step.title, FONT_SIZE_STEP, titleMaxW);
48037
48822
  for (let i = 0; i < titleLines.length; i++) {
@@ -48090,7 +48875,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
48090
48875
  const stripFill = shapeFill(palette, stripColor, isDark, {
48091
48876
  ...solid !== void 0 && { solid }
48092
48877
  });
48093
- 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);
48878
+ 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);
48094
48879
  const stripTextColor = contrastText(
48095
48880
  stripFill,
48096
48881
  palette.textOnFillLight,
@@ -48206,7 +48991,7 @@ function renderJourneyMapForExport(content, theme, palette) {
48206
48991
  }
48207
48992
  return svgEl.outerHTML;
48208
48993
  }
48209
- var 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;
48994
+ var 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;
48210
48995
  var init_renderer14 = __esm({
48211
48996
  "src/journey-map/renderer.ts"() {
48212
48997
  "use strict";
@@ -48219,9 +49004,9 @@ var init_renderer14 = __esm({
48219
49004
  init_tag_groups();
48220
49005
  init_scaling();
48221
49006
  init_text_measure();
49007
+ init_visual_conventions();
48222
49008
  DIAGRAM_PADDING13 = 20;
48223
49009
  PADDING2 = DIAGRAM_PADDING13;
48224
- CARD_RADIUS5 = 6;
48225
49010
  CARD_PADDING_X3 = 10;
48226
49011
  CARD_PADDING_Y3 = 6;
48227
49012
  CARD_HEADER_HEIGHT3 = 24;
@@ -48298,7 +49083,7 @@ function computeCycleLayout(parsed, options) {
48298
49083
  const hasDesc = !hideDescriptions && node.description.length > 0;
48299
49084
  const labelWidth2 = Math.max(
48300
49085
  MIN_NODE_WIDTH4,
48301
- measureText(node.label, LABEL_FONT_SIZE5) + NODE_PAD_X * 2
49086
+ measureText(node.label, LABEL_FONT_SIZE4) + NODE_PAD_X * 2
48302
49087
  );
48303
49088
  if (circleNodes) {
48304
49089
  return computeCircleNodeDims(node, hasDesc);
@@ -48515,7 +49300,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48515
49300
  let bestScore = Infinity;
48516
49301
  for (let w = minW; w <= MAX_NODE_WIDTH3; w += DESC_WIDTH_STEP) {
48517
49302
  const wrapped2 = wrapDescForWidth(description, w);
48518
- const h = HEADER_HEIGHT5 + wrapped2.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y;
49303
+ const h = HEADER_HEIGHT4 + wrapped2.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y;
48519
49304
  const ratio = w / h;
48520
49305
  const score = Math.abs(Math.log(ratio / DESC_TARGET_RATIO));
48521
49306
  if (score < bestScore) {
@@ -48527,7 +49312,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48527
49312
  const wrapped = wrapDescForWidth(description, minW);
48528
49313
  return {
48529
49314
  width: minW,
48530
- height: HEADER_HEIGHT5 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
49315
+ height: HEADER_HEIGHT4 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
48531
49316
  wrappedDesc: wrapped
48532
49317
  };
48533
49318
  }
@@ -48540,7 +49325,7 @@ function wrapDescForWidth(description, nodeWidth) {
48540
49325
  );
48541
49326
  }
48542
49327
  function renderedDescNodeHeight(numLines, scale) {
48543
- const headerH = HEADER_HEIGHT5 * scale;
49328
+ const headerH = HEADER_HEIGHT4 * scale;
48544
49329
  const descFont = Math.max(
48545
49330
  RENDERER_DESC_FONT_MIN,
48546
49331
  Math.round(RENDERER_DESC_FONT * scale)
@@ -48875,7 +49660,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
48875
49660
  const midAngle = srcExitAngle + dir * visibleSweep / 2;
48876
49661
  return { path, midAngle };
48877
49662
  }
48878
- 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;
49663
+ 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;
48879
49664
  var init_layout14 = __esm({
48880
49665
  "src/cycle/layout.ts"() {
48881
49666
  "use strict";
@@ -48883,7 +49668,7 @@ var init_layout14 = __esm({
48883
49668
  init_wrapped_desc();
48884
49669
  init_text_measure();
48885
49670
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
48886
- LABEL_FONT_SIZE5 = 13;
49671
+ LABEL_FONT_SIZE4 = 13;
48887
49672
  CIRCLE_LABEL_FONT_SIZE = 16;
48888
49673
  DESC_FONT_SIZE6 = 11;
48889
49674
  EDGE_LABEL_FONT_SIZE11 = 11;
@@ -48893,7 +49678,7 @@ var init_layout14 = __esm({
48893
49678
  DESC_WIDTH_STEP = 20;
48894
49679
  DESC_TARGET_RATIO = 1.6;
48895
49680
  PLAIN_NODE_HEIGHT = 50;
48896
- HEADER_HEIGHT5 = 36;
49681
+ HEADER_HEIGHT4 = 36;
48897
49682
  DESC_LINE_HEIGHT7 = 16;
48898
49683
  DESC_PAD_Y = 14;
48899
49684
  NODE_PAD_X = 20;
@@ -49473,6 +50258,16 @@ __export(resolver_exports, {
49473
50258
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49474
50259
  resolveMap: () => resolveMap
49475
50260
  });
50261
+ function containerOvershoot(span, usOriented) {
50262
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50263
+ return Math.max(
50264
+ CONTAINER_OVERSHOOT_MIN,
50265
+ Math.min(
50266
+ CONTAINER_OVERSHOOT_MAX,
50267
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50268
+ )
50269
+ );
50270
+ }
49476
50271
  function bboxArea(b) {
49477
50272
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49478
50273
  }
@@ -49969,7 +50764,7 @@ function resolveMap(parsed, data) {
49969
50764
  const containerUnion = unionExtent(containerBoxes, points);
49970
50765
  if (containerUnion)
49971
50766
  extent3 = pad(
49972
- clampContainerToCluster(containerUnion, points),
50767
+ clampContainerToCluster(containerUnion, points, usOriented),
49973
50768
  PAD_FRACTION
49974
50769
  );
49975
50770
  }
@@ -50053,17 +50848,21 @@ function mostCommonCountry(regions, poiCountries) {
50053
50848
  }
50054
50849
  return best;
50055
50850
  }
50056
- function clampContainerToCluster(container, points) {
50851
+ function clampContainerToCluster(container, points, usOriented) {
50057
50852
  const poi = unionExtent([], points);
50058
50853
  if (!poi) return container;
50059
50854
  let [[west, south], [east, north]] = container;
50060
50855
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
50061
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
50062
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
50063
- if (east <= 180 && pEast <= 180) {
50064
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
50065
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
50066
- }
50856
+ const over = containerOvershoot(
50857
+ Math.max(pEast - pWest, pNorth - pSouth),
50858
+ usOriented
50859
+ );
50860
+ south = Math.max(south, pSouth - over);
50861
+ north = Math.min(north, pNorth + over);
50862
+ const wOver = pWest - over;
50863
+ const eOver = pEast + over;
50864
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
50865
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
50067
50866
  return [
50068
50867
  [west, south],
50069
50868
  [east, north]
@@ -50081,7 +50880,7 @@ function firstError(diags) {
50081
50880
  const e = diags.find((d) => d.severity === "error");
50082
50881
  return e ? formatDgmoError(e) : null;
50083
50882
  }
50084
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_DEG, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50883
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_MAX, CONTAINER_OVERSHOOT_MIN, CONTAINER_OVERSHOOT_DECAY, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50085
50884
  var init_resolver2 = __esm({
50086
50885
  "src/map/resolver.ts"() {
50087
50886
  "use strict";
@@ -50094,7 +50893,9 @@ var init_resolver2 = __esm({
50094
50893
  WORLD_LAT_SOUTH = -58;
50095
50894
  WORLD_LAT_NORTH = 78;
50096
50895
  POI_ZOOM_FLOOR_DEG = 7;
50097
- CONTAINER_OVERSHOOT_DEG = 8;
50896
+ CONTAINER_OVERSHOOT_MAX = 8;
50897
+ CONTAINER_OVERSHOOT_MIN = 3;
50898
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50098
50899
  US_NATIONAL_LON_SPAN = 48;
50099
50900
  CONUS_BBOX = [
50100
50901
  [-125, 25],
@@ -50260,12 +51061,12 @@ function tierBand(maxSpanDeg) {
50260
51061
  }
50261
51062
  function labelBudget(width, height, band) {
50262
51063
  const bandCap = {
50263
- world: 7,
50264
- continental: 6,
50265
- regional: 5,
50266
- local: 4
51064
+ world: 10,
51065
+ continental: 9,
51066
+ regional: 7,
51067
+ local: 6
50267
51068
  };
50268
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51069
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50269
51070
  return Math.max(0, Math.min(area2, bandCap[band]));
50270
51071
  }
50271
51072
  function waterEligible(tier, kind, band) {
@@ -50319,6 +51120,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50319
51120
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50320
51121
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50321
51122
  }
51123
+ function rectDist2(px, py, x0, y0, x1, y1) {
51124
+ const dx = Math.max(x0 - px, 0, px - x1);
51125
+ const dy = Math.max(y0 - py, 0, py - y1);
51126
+ return dx * dx + dy * dy;
51127
+ }
50322
51128
  function rectFits(r, width, height) {
50323
51129
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50324
51130
  }
@@ -50337,6 +51143,7 @@ function placeContextLabels(args) {
50337
51143
  palette,
50338
51144
  project,
50339
51145
  collides,
51146
+ contentPoints,
50340
51147
  overLand
50341
51148
  } = args;
50342
51149
  void projection;
@@ -50407,8 +51214,17 @@ function placeContextLabels(args) {
50407
51214
  const [x0, y0, x1, y1] = c.bbox;
50408
51215
  const w = x1 - x0;
50409
51216
  const h = y1 - y0;
50410
- return { c, w, h, area: w * h };
50411
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51217
+ let dist = Infinity;
51218
+ if (contentPoints?.length) {
51219
+ for (const p of contentPoints) {
51220
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51221
+ if (d < dist) dist = d;
51222
+ }
51223
+ }
51224
+ return { c, w, h, area: w * h, dist };
51225
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51226
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51227
+ );
50412
51228
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50413
51229
  let ci = 0;
50414
51230
  for (const r of ranked) {
@@ -50425,7 +51241,7 @@ function placeContextLabels(args) {
50425
51241
  );
50426
51242
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50427
51243
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50428
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51244
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50429
51245
  const text = c.name;
50430
51246
  const tw = labelWidth(text, 0, fontSize);
50431
51247
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50438,6 +51254,9 @@ function placeContextLabels(args) {
50438
51254
  letterSpacing: 0,
50439
51255
  color,
50440
51256
  fontSize,
51257
+ // Multi-position dodging: carry the ordered interior positions through to the
51258
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51259
+ ...c.positions ? { positions: c.positions } : {},
50441
51260
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50442
51261
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50443
51262
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50452,39 +51271,53 @@ function placeContextLabels(args) {
50452
51271
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50453
51272
  const waterCap = budget - Math.min(2, countryCount);
50454
51273
  let waterPlaced = 0;
50455
- for (const cand of candidates) {
50456
- if (placed.length >= budget) break;
50457
- if (cand.italic && waterPlaced >= waterCap) continue;
51274
+ const gateAt = (cx, cy, cand) => {
50458
51275
  const rect = rectAround(
50459
- cand.cx,
50460
- cand.cy,
51276
+ cx,
51277
+ cy,
50461
51278
  cand.lines,
50462
51279
  cand.letterSpacing,
50463
51280
  cand.fontSize
50464
51281
  );
50465
- if (!rectFits(rect, width, height)) continue;
51282
+ if (!rectFits(rect, width, height)) return null;
50466
51283
  if (cand.italic && overLand) {
50467
51284
  const inset = 2;
50468
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51285
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50469
51286
  const touchesLand = cand.lines.some((line11, li) => {
50470
51287
  const lw = labelWidth(line11, cand.letterSpacing);
50471
- const x0 = cand.cx - lw / 2 + inset;
50472
- const x1 = cand.cx + lw / 2 - inset;
50473
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51288
+ const x0 = cx - lw / 2 + inset;
51289
+ const x1 = cx + lw / 2 - inset;
51290
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
50474
51291
  const base = top + li * LINE_HEIGHT;
50475
51292
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50476
51293
  (y) => xs.some((x) => overLand(x, y))
50477
51294
  );
50478
51295
  });
50479
- if (touchesLand) continue;
51296
+ if (touchesLand) return null;
50480
51297
  }
50481
- if (collides(rect)) continue;
50482
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50483
- placedRects.push(rect);
51298
+ if (collides(rect)) return null;
51299
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51300
+ return null;
51301
+ return rect;
51302
+ };
51303
+ for (const cand of candidates) {
51304
+ if (placed.length >= budget) break;
51305
+ if (cand.italic && waterPlaced >= waterCap) continue;
51306
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51307
+ let chosen = null;
51308
+ for (const [px, py] of positions) {
51309
+ const rect = gateAt(px, py, cand);
51310
+ if (rect) {
51311
+ chosen = { x: px, y: py, rect };
51312
+ break;
51313
+ }
51314
+ }
51315
+ if (!chosen) continue;
51316
+ placedRects.push(chosen.rect);
50484
51317
  if (cand.italic) waterPlaced++;
50485
51318
  placed.push({
50486
- x: cand.cx,
50487
- y: cand.cy,
51319
+ x: chosen.x,
51320
+ y: chosen.y,
50488
51321
  text: cand.text,
50489
51322
  anchor: "middle",
50490
51323
  color: cand.color,
@@ -50502,7 +51335,7 @@ function placeContextLabels(args) {
50502
51335
  }
50503
51336
  return placed;
50504
51337
  }
50505
- var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, KIND_ORDER;
51338
+ var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, MAX_COUNTRY_POSITIONS, COUNTRY_POS_GRID, COUNTRY_POS_TOPN_MARGIN, KIND_ORDER;
50506
51339
  var init_context_labels = __esm({
50507
51340
  "src/map/context-labels.ts"() {
50508
51341
  "use strict";
@@ -50520,6 +51353,9 @@ var init_context_labels = __esm({
50520
51353
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50521
51354
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50522
51355
  COUNTRY_FADE_MAX = 45;
51356
+ MAX_COUNTRY_POSITIONS = 4;
51357
+ COUNTRY_POS_GRID = 5;
51358
+ COUNTRY_POS_TOPN_MARGIN = 3;
50523
51359
  KIND_ORDER = {
50524
51360
  ocean: 0,
50525
51361
  sea: 1,
@@ -50613,6 +51449,84 @@ function decodeLayer(topo) {
50613
51449
  decodeCache.set(topo, out);
50614
51450
  return out;
50615
51451
  }
51452
+ function countryLabelPositions(args) {
51453
+ const { geometry, bounds, project, width, height, curated } = args;
51454
+ const w0 = bounds[0][0];
51455
+ const s0 = bounds[0][1];
51456
+ const e0 = bounds[1][0];
51457
+ const n0 = bounds[1][1];
51458
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51459
+ return mkCurated(curated, project);
51460
+ }
51461
+ const N = COUNTRY_POS_GRID;
51462
+ const onLand = [];
51463
+ const kept = [];
51464
+ for (let i = 0; i < N; i++) {
51465
+ onLand[i] = [];
51466
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51467
+ for (let j = 0; j < N; j++) {
51468
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51469
+ const land = pointInGeometry(geometry, lon, lat);
51470
+ onLand[i][j] = land;
51471
+ if (!land) continue;
51472
+ const p = project(lon, lat);
51473
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51474
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51475
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51476
+ }
51477
+ }
51478
+ if (!kept.length) return mkCurated(curated, project);
51479
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51480
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51481
+ const interiorness = (c) => {
51482
+ let n = 0;
51483
+ for (let di = -1; di <= 1; di++)
51484
+ for (let dj = -1; dj <= 1; dj++) {
51485
+ if (di === 0 && dj === 0) continue;
51486
+ const ni = c.i + di;
51487
+ const nj = c.j + dj;
51488
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51489
+ }
51490
+ return n;
51491
+ };
51492
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51493
+ const pool = [...kept];
51494
+ pool.sort((a, b) => {
51495
+ const d = interiorness(b) - interiorness(a);
51496
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51497
+ });
51498
+ const ordered = [pool.shift()];
51499
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51500
+ let bestIdx = 0;
51501
+ let bestMin = -1;
51502
+ for (let k = 0; k < pool.length; k++) {
51503
+ const c = pool[k];
51504
+ let minD = Infinity;
51505
+ for (const o of ordered) {
51506
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51507
+ if (d < minD) minD = d;
51508
+ }
51509
+ if (minD > bestMin) {
51510
+ bestMin = minD;
51511
+ bestIdx = k;
51512
+ }
51513
+ }
51514
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51515
+ }
51516
+ const grid = ordered.map((c) => ({
51517
+ lonLat: [c.lon, c.lat],
51518
+ screen: [c.sx, c.sy]
51519
+ }));
51520
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51521
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51522
+ return out;
51523
+ }
51524
+ function mkCurated(curated, project) {
51525
+ if (!curated) return [];
51526
+ const p = project(curated[0], curated[1]);
51527
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51528
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51529
+ }
50616
51530
  function projectionFor(family, extent3) {
50617
51531
  switch (family) {
50618
51532
  case "albers-usa":
@@ -51440,6 +52354,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51440
52354
  if (p[1] < minY) minY = p[1];
51441
52355
  if (p[1] > maxY) maxY = p[1];
51442
52356
  }
52357
+ r.bbox = [minX, minY, maxX, maxY];
52358
+ r.rings = rings;
51443
52359
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51444
52360
  });
51445
52361
  const fillAt = (x, y) => {
@@ -52630,6 +53546,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52630
53546
  for (const box of insets)
52631
53547
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52632
53548
  const countryCandidates = [];
53549
+ const rawCountries = [];
52633
53550
  for (const f of worldLayer.values()) {
52634
53551
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52635
53552
  if (!iso || regionById.has(iso)) continue;
@@ -52646,11 +53563,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52646
53563
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52647
53564
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52648
53565
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52649
- countryCandidates.push({
53566
+ rawCountries.push({
53567
+ f,
53568
+ iso,
52650
53569
  name: f.properties?.name ?? iso,
52651
53570
  bbox: [x0, y0, x1, y1],
52652
53571
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52653
- curatedAnchor: !!anchorLngLat
53572
+ curatedLngLat: anchorLngLat ?? null,
53573
+ area: (x1 - x0) * (y1 - y0)
53574
+ });
53575
+ }
53576
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53577
+ const cBudget = labelBudget(width, height, cBand);
53578
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53579
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53580
+ const rankOrder = rawCountries.map((r, idx) => {
53581
+ let dist = Infinity;
53582
+ const [x0, y0, x1, y1] = r.bbox;
53583
+ for (const [px, py] of contentPoints) {
53584
+ const dx = Math.max(x0 - px, 0, px - x1);
53585
+ const dy = Math.max(y0 - py, 0, py - y1);
53586
+ const d = dx * dx + dy * dy;
53587
+ if (d < dist) dist = d;
53588
+ }
53589
+ return { idx, area: r.area, dist };
53590
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53591
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53592
+ ).slice(0, topN);
53593
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53594
+ for (let i = 0; i < rawCountries.length; i++) {
53595
+ const r = rawCountries[i];
53596
+ let anchor = r.anchor;
53597
+ let positions;
53598
+ if (genIdx.has(i) && anchor) {
53599
+ const gb = geoBounds2(r.f);
53600
+ const gen = countryLabelPositions({
53601
+ geometry: r.f.geometry,
53602
+ bounds: gb,
53603
+ project,
53604
+ width,
53605
+ height,
53606
+ curated: r.curatedLngLat
53607
+ });
53608
+ if (gen.length) {
53609
+ positions = gen.map((p) => p.screen);
53610
+ anchor = positions[0];
53611
+ }
53612
+ }
53613
+ countryCandidates.push({
53614
+ name: r.name,
53615
+ bbox: r.bbox,
53616
+ anchor,
53617
+ curatedAnchor: !!r.curatedLngLat,
53618
+ ...positions ? { positions } : {}
52654
53619
  });
52655
53620
  }
52656
53621
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52684,6 +53649,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52684
53649
  palette,
52685
53650
  project,
52686
53651
  collides,
53652
+ contentPoints,
52687
53653
  // Water labels must stay over open water — `fillAt` returns the ocean
52688
53654
  // backdrop colour off-land and a region fill on-land (lakes/states count
52689
53655
  // as land here, which is the safe side for an ocean name).
@@ -52936,7 +53902,7 @@ function ringToCoastPaths(ring, frame) {
52936
53902
  function coastlineOuterRings(regions, minExtent, frame) {
52937
53903
  const paths = [];
52938
53904
  for (const r of regions) {
52939
- const rings = parsePathRings(r.d);
53905
+ const rings = r.rings ?? parsePathRings(r.d);
52940
53906
  for (let i = 0; i < rings.length; i++) {
52941
53907
  const ring = rings[i];
52942
53908
  if (ring.length < 3) continue;
@@ -53000,6 +53966,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53000
53966
  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);
53001
53967
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", layout.background);
53002
53968
  const defs = svg.append("defs");
53969
+ const uid = mapInstanceCounter++;
53970
+ const nid = (base) => `${base}__m${uid}`;
53003
53971
  const arrowSize = (w) => Math.min(15, 7 + w * 0.95);
53004
53972
  const haloColor = palette.bg;
53005
53973
  const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
@@ -53032,8 +54000,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53032
54000
  for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
53033
54001
  if (layout.relief.length && layout.reliefHatch) {
53034
54002
  const h = layout.reliefHatch;
53035
- const rangeClipId = "dgmo-relief-clip";
53036
- const landClipId = "dgmo-relief-land";
54003
+ const rangeClipId = nid("dgmo-relief-clip");
54004
+ const landClipId = nid("dgmo-relief-land");
53037
54005
  const rangeClip = defs.append("clipPath").attr("id", rangeClipId);
53038
54006
  for (const s of layout.relief) rangeClip.append("path").attr("d", s.d);
53039
54007
  const landClip = defs.append("clipPath").attr("id", landClipId);
@@ -53046,7 +54014,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53046
54014
  }
53047
54015
  if (layout.coastlineStyle) {
53048
54016
  const cs = layout.coastlineStyle;
53049
- const maskId = "dgmo-map-water-mask";
54017
+ const maskId = nid("dgmo-map-water-mask");
53050
54018
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
53051
54019
  mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
53052
54020
  const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
@@ -53064,7 +54032,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53064
54032
  appendWaterLines(
53065
54033
  gWater,
53066
54034
  defs,
53067
- "dgmo-map-coast",
54035
+ nid("dgmo-map-coast"),
53068
54036
  // Pass the canvas frame so edges collinear with it (the antimeridian on a
53069
54037
  // world map, regional clipExtent cuts) don't get ringed as fake coast —
53070
54038
  // land runs cleanly to the render-area edge.
@@ -53101,7 +54069,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53101
54069
  (l) => l.poiId !== void 0 && !l.hidden
53102
54070
  );
53103
54071
  if (poiLabels.length) {
53104
- const patchBlurId = "dgmo-map-label-patch-blur";
54072
+ const patchBlurId = nid("dgmo-map-label-patch-blur");
53105
54073
  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);
53106
54074
  const PAD = 8;
53107
54075
  const buildPatch = (labels, maskId, decoCluster) => {
@@ -53129,14 +54097,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53129
54097
  if (decoCluster !== void 0)
53130
54098
  gPatch.attr("data-cluster-deco", decoCluster);
53131
54099
  for (const r of layout.regions) {
53132
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53133
- for (const ring of parsePathRings(r.d))
53134
- for (const [px, py] of ring) {
53135
- if (px < minX) minX = px;
53136
- if (px > maxX) maxX = px;
53137
- if (py < minY) minY = py;
53138
- if (py > maxY) maxY = py;
53139
- }
54100
+ let minX, minY, maxX, maxY;
54101
+ if (r.bbox) {
54102
+ [minX, minY, maxX, maxY] = r.bbox;
54103
+ } else {
54104
+ minX = Infinity;
54105
+ minY = Infinity;
54106
+ maxX = -Infinity;
54107
+ maxY = -Infinity;
54108
+ for (const ring of parsePathRings(r.d))
54109
+ for (const [px, py] of ring) {
54110
+ if (px < minX) minX = px;
54111
+ if (px > maxX) maxX = px;
54112
+ if (py < minY) minY = py;
54113
+ if (py > maxY) maxY = py;
54114
+ }
54115
+ }
53140
54116
  const hit = blobRects.some(
53141
54117
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53142
54118
  );
@@ -53146,7 +54122,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53146
54122
  if (clusterUi) {
53147
54123
  buildPatch(
53148
54124
  poiLabels.filter((l) => l.clusterMember === void 0),
53149
- "dgmo-map-label-patch"
54125
+ nid("dgmo-map-label-patch")
53150
54126
  );
53151
54127
  const byCluster = /* @__PURE__ */ new Map();
53152
54128
  for (const l of poiLabels) {
@@ -53157,9 +54133,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53157
54133
  }
53158
54134
  let ci = 0;
53159
54135
  for (const [cid, labs] of byCluster)
53160
- buildPatch(labs, `dgmo-map-label-patch-c${ci++}`, cid);
54136
+ buildPatch(labs, nid(`dgmo-map-label-patch-c${ci++}`), cid);
53161
54137
  } else {
53162
- buildPatch(poiLabels, "dgmo-map-label-patch");
54138
+ buildPatch(poiLabels, nid("dgmo-map-label-patch"));
53163
54139
  }
53164
54140
  }
53165
54141
  if (layout.insets.length) {
@@ -53168,7 +54144,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53168
54144
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
53169
54145
  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");
53170
54146
  if (box.contextLand) {
53171
- const clipId = `dgmo-map-inset-clip-${bi}`;
54147
+ const clipId = nid(`dgmo-map-inset-clip-${bi}`);
53172
54148
  defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
53173
54149
  insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
53174
54150
  }
@@ -53176,7 +54152,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53176
54152
  for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
53177
54153
  if (layout.coastlineStyle) {
53178
54154
  const cs = layout.coastlineStyle;
53179
- const maskId = "dgmo-map-inset-water-mask";
54155
+ const maskId = nid("dgmo-map-inset-water-mask");
53180
54156
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
53181
54157
  const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
53182
54158
  for (const box of layout.insets) {
@@ -53185,7 +54161,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53185
54161
  }
53186
54162
  layout.insets.forEach((box, bi) => {
53187
54163
  if (box.contextLand)
53188
- mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
54164
+ mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#${nid(`dgmo-map-inset-clip-${bi}`)})`);
53189
54165
  });
53190
54166
  for (const r of layout.insetRegions)
53191
54167
  if (r.id !== "lake")
@@ -53193,7 +54169,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53193
54169
  for (const r of layout.insetRegions)
53194
54170
  if (r.id === "lake")
53195
54171
  mask.append("path").attr("d", r.d).attr("fill", "white");
53196
- const clipId = "dgmo-map-inset-water-clip";
54172
+ const clipId = nid("dgmo-map-inset-water-clip");
53197
54173
  const clip = defs.append("clipPath").attr("id", clipId);
53198
54174
  for (const box of layout.insets) {
53199
54175
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
@@ -53203,7 +54179,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53203
54179
  appendWaterLines(
53204
54180
  gInsetWater,
53205
54181
  defs,
53206
- "dgmo-map-inset-coast",
54182
+ nid("dgmo-map-inset-coast"),
53207
54183
  coastlineOuterRings(layout.insetRegions, cs.minExtent),
53208
54184
  cs,
53209
54185
  layout.background
@@ -53232,7 +54208,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53232
54208
  }
53233
54209
  wireSync(p, leg.lineNumber);
53234
54210
  if (leg.arrow) {
53235
- const id = `dgmo-map-arrow-${i}`;
54211
+ const id = nid(`dgmo-map-arrow-${i}`);
53236
54212
  const s = arrowSize(leg.width);
53237
54213
  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);
53238
54214
  p.attr("marker-end", `url(#${id})`);
@@ -53428,7 +54404,7 @@ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic
53428
54404
  }
53429
54405
  return t;
53430
54406
  }
53431
- var LABEL_FONT;
54407
+ var LABEL_FONT, mapInstanceCounter;
53432
54408
  var init_renderer16 = __esm({
53433
54409
  "src/map/renderer.ts"() {
53434
54410
  "use strict";
@@ -53440,6 +54416,7 @@ var init_renderer16 = __esm({
53440
54416
  init_legend_band();
53441
54417
  init_layout15();
53442
54418
  LABEL_FONT = 11;
54419
+ mapInstanceCounter = 0;
53443
54420
  }
53444
54421
  });
53445
54422
 
@@ -54362,7 +55339,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54362
55339
  const tasksAll = [...allTasks(parsed)];
54363
55340
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54364
55341
  const solid = parsed.options["solid-fill"] === "on";
54365
- const surfaceBg = isDark ? palette.surface : palette.bg;
55342
+ const surfaceBg = themeBaseBg(palette, isDark);
54366
55343
  const roleCount = Math.max(1, parsed.roles.length);
54367
55344
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54368
55345
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54371,7 +55348,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54371
55348
  const sHMargin = ctx.aesthetic(H_MARGIN);
54372
55349
  const sVMargin = ctx.aesthetic(V_MARGIN3);
54373
55350
  const sTitleAreaHeight = ctx.structural(TITLE_AREA_HEIGHT4);
54374
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT6);
55351
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT5);
54375
55352
  const sRowHeight = ctx.structural(ROW_HEIGHT);
54376
55353
  const sPhaseHeight = ctx.structural(PHASE_HEIGHT);
54377
55354
  const sTaskLabelMin = ctx.structural(TASK_LABEL_MIN);
@@ -54396,7 +55373,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54396
55373
  const sLegendLetterFont = ctx.text(LEGEND_LETTER_FONT);
54397
55374
  const sViolationLineHeight = ctx.structural(VIOLATION_LINE_HEIGHT);
54398
55375
  const sStackTopGap = ctx.structural(STACK_TOP_GAP);
54399
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH12);
55376
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
54400
55377
  const sNodeRadius = ctx.structural(NODE_RADIUS3);
54401
55378
  const innerWidth = Math.max(0, width - 2 * sHMargin);
54402
55379
  let roleColW = Math.max(
@@ -54540,10 +55517,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54540
55517
  const cx = roleX(i) + sColumnInset;
54541
55518
  const cw = roleColW - 2 * sColumnInset;
54542
55519
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54543
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55520
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54544
55521
  const headerFill = mix(
54545
55522
  roleColor,
54546
- isDark ? palette.surface : palette.bg,
55523
+ themeBaseBg(palette, isDark),
54547
55524
  30
54548
55525
  );
54549
55526
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -54942,7 +55919,7 @@ function parseQuotedSegments(message) {
54942
55919
  out.push({ text: message.slice(last), bold: false });
54943
55920
  return out;
54944
55921
  }
54945
- var 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;
55922
+ var 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;
54946
55923
  var init_renderer19 = __esm({
54947
55924
  "src/raci/renderer.ts"() {
54948
55925
  "use strict";
@@ -54953,6 +55930,7 @@ var init_renderer19 = __esm({
54953
55930
  init_variants();
54954
55931
  init_scaling();
54955
55932
  init_text_measure();
55933
+ init_visual_conventions();
54956
55934
  MARKER_LABELS = {
54957
55935
  raci: { R: "Responsible", A: "Accountable", C: "Consulted", I: "Informed" },
54958
55936
  rasci: {
@@ -54974,7 +55952,7 @@ var init_renderer19 = __esm({
54974
55952
  TITLE_LEGEND_GAP = 16;
54975
55953
  LEGEND_LABEL_FONT = 12;
54976
55954
  LEGEND_LETTER_FONT = 14;
54977
- HEADER_HEIGHT6 = 36;
55955
+ HEADER_HEIGHT5 = 36;
54978
55956
  ROW_HEIGHT = 36;
54979
55957
  PHASE_HEIGHT = 40;
54980
55958
  TASK_LABEL_MIN = 200;
@@ -54994,7 +55972,6 @@ var init_renderer19 = __esm({
54994
55972
  ROLE_HEADER_FONT = 12;
54995
55973
  PHASE_FONT = 13;
54996
55974
  TINT_PCT = 25;
54997
- NODE_STROKE_WIDTH12 = 1.5;
54998
55975
  NODE_RADIUS3 = 6;
54999
55976
  AUTO_ACCENTS = [
55000
55977
  "blue",
@@ -55595,11 +56572,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55595
56572
  const lines = splitParticipantLabel(
55596
56573
  p.label,
55597
56574
  labelTextWidth(PARTICIPANT_BOX_WIDTH),
55598
- LABEL_FONT_SIZE6
56575
+ LABEL_FONT_SIZE5
55599
56576
  );
55600
56577
  if (lines.length === 0) continue;
55601
56578
  const widest = Math.max(
55602
- ...lines.map((l) => measureText(l, LABEL_FONT_SIZE6))
56579
+ ...lines.map((l) => measureText(l, LABEL_FONT_SIZE5))
55603
56580
  );
55604
56581
  const labelWidth2 = widest + 10;
55605
56582
  uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
@@ -55633,7 +56610,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55633
56610
  const sSelfCallWidth = ctx.structural(SELF_CALL_WIDTH);
55634
56611
  const sNoteTextWidthMax = sNoteMaxW - sNotePadH * 2 - sNoteFold;
55635
56612
  const sNoteLaneMax = sGap - sActivationWidth - sNoteGap;
55636
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE6);
56613
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE5);
55637
56614
  const sLabelTextWidth = labelTextWidth(sBoxW);
55638
56615
  const participantIndexMap = /* @__PURE__ */ new Map();
55639
56616
  participants.forEach((p, i) => participantIndexMap.set(p.id, i));
@@ -56270,7 +57247,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56270
57247
  const groupTagColor = getTagColor(groupTagValue || void 0);
56271
57248
  const fillColor = groupTagColor ? mix(
56272
57249
  groupTagColor,
56273
- isDark ? palette.surface : palette.bg,
57250
+ themeBaseBg(palette, isDark),
56274
57251
  isDark ? 15 : 20
56275
57252
  ) : isDark ? palette.surface : palette.bg;
56276
57253
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56324,7 +57301,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56324
57301
  participantG.append("title").text("Click to expand");
56325
57302
  const pFill = effectiveTagColor ? mix(
56326
57303
  effectiveTagColor,
56327
- isDark ? palette.surface : palette.bg,
57304
+ themeBaseBg(palette, isDark),
56328
57305
  isDark ? 30 : 40
56329
57306
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56330
57307
  const pStroke = effectiveTagColor || palette.border;
@@ -56531,7 +57508,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56531
57508
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56532
57509
  const actTagColor = getTagColor(actTagValue);
56533
57510
  const actBaseColor = actTagColor || palette.primary;
56534
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57511
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
56535
57512
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56536
57513
  const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
56537
57514
  if (tagKey && actTagValue) {
@@ -56770,7 +57747,7 @@ function buildNoteMessageMap(elements) {
56770
57747
  walk(elements);
56771
57748
  return map;
56772
57749
  }
56773
- function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr, solid, boxW = W, boxH = H, labelTextW = labelTextWidth(W), labelFontSize2 = LABEL_FONT_SIZE6) {
57750
+ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr, solid, boxW = W, boxH = H, labelTextW = labelTextWidth(W), labelFontSize2 = LABEL_FONT_SIZE5) {
56774
57751
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
56775
57752
  if (tagAttr) {
56776
57753
  g.attr(`data-tag-${tagAttr.key}`, tagAttr.value);
@@ -56818,7 +57795,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
56818
57795
  });
56819
57796
  }
56820
57797
  }
56821
- var 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;
57798
+ var 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;
56822
57799
  var init_renderer20 = __esm({
56823
57800
  "src/sequence/renderer.ts"() {
56824
57801
  "use strict";
@@ -56838,7 +57815,7 @@ var init_renderer20 = __esm({
56838
57815
  PARTICIPANT_GAP = 160;
56839
57816
  PARTICIPANT_BOX_WIDTH = 120;
56840
57817
  PARTICIPANT_BOX_HEIGHT = 50;
56841
- LABEL_FONT_SIZE6 = 13;
57818
+ LABEL_FONT_SIZE5 = 13;
56842
57819
  TOP_MARGIN = 20;
56843
57820
  TITLE_HEIGHT8 = 30;
56844
57821
  PARTICIPANT_Y_OFFSET = 10;
@@ -58232,7 +59209,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58232
59209
  const textColor = palette.text;
58233
59210
  const mutedColor = palette.border;
58234
59211
  const bgColor = palette.bg;
58235
- const bg = isDark ? palette.surface : palette.bg;
59212
+ const bg = themeBaseBg(palette, isDark);
58236
59213
  const colors = getSeriesColors(palette);
58237
59214
  const groupColorMap = /* @__PURE__ */ new Map();
58238
59215
  timelineGroups.forEach((grp, i) => {
@@ -58647,9 +59624,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58647
59624
  drawLegend2();
58648
59625
  }
58649
59626
  }
58650
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59627
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58651
59628
  const {
58652
59629
  width,
59630
+ height,
58653
59631
  tooltip,
58654
59632
  solid,
58655
59633
  textColor,
@@ -58701,9 +59679,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58701
59679
  const rowH = ctx.structural(28);
58702
59680
  const innerHeight = rowH * sorted.length;
58703
59681
  const usedHeight = margin.top + innerHeight + margin.bottom;
59682
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59683
+ const svgHeight = fitToContainer ? height : usedHeight;
58704
59684
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58705
- 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);
58706
- if (ctx.isBelowFloor) {
59685
+ const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", svgHeight).attr("viewBox", `0 0 ${width} ${usedHeight}`).attr("preserveAspectRatio", "xMidYMin meet").style("background", bgColor);
59686
+ if (ctx.isBelowFloor && !fitToContainer) {
58707
59687
  svg.attr("width", "100%");
58708
59688
  }
58709
59689
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60377,7 +61357,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60377
61357
  onClickItem
60378
61358
  );
60379
61359
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60380
- const bg = isDark ? palette.surface : palette.bg;
61360
+ const bg = themeBaseBg(palette, isDark);
60381
61361
  const getQuadrantColor = (label, defaultIdx) => {
60382
61362
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60383
61363
  };
@@ -60714,18 +61694,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60714
61694
  palette,
60715
61695
  viewState,
60716
61696
  options,
60717
- exportMode
61697
+ exportMode,
61698
+ isDark: theme === "dark"
60718
61699
  };
60719
61700
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
60720
61701
  return (handler ?? exportVisualization)(ctx);
60721
61702
  }
61703
+ function ctxTagOverride(ctx) {
61704
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61705
+ }
60722
61706
  async function exportOrg(ctx) {
60723
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61707
+ const { content, theme, palette, viewState, exportMode } = ctx;
60724
61708
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
60725
61709
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
60726
61710
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
60727
61711
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
60728
- const isDark = theme === "dark";
61712
+ const isDark = ctx.isDark;
60729
61713
  const effectivePalette = await resolveExportPalette(theme, palette);
60730
61714
  const orgParsed = parseOrg2(content, effectivePalette);
60731
61715
  if (orgParsed.error) return "";
@@ -60733,7 +61717,7 @@ async function exportOrg(ctx) {
60733
61717
  const activeTagGroup = resolveActiveTagGroup(
60734
61718
  orgParsed.tagGroups,
60735
61719
  orgParsed.options["active-tag"],
60736
- viewState?.tag ?? options?.tagGroup
61720
+ ctxTagOverride(ctx)
60737
61721
  );
60738
61722
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60739
61723
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60766,12 +61750,12 @@ async function exportOrg(ctx) {
60766
61750
  return finalizeSvgExport(container, theme, effectivePalette);
60767
61751
  }
60768
61752
  async function exportSitemap(ctx) {
60769
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61753
+ const { content, theme, palette, viewState, exportMode } = ctx;
60770
61754
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
60771
61755
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
60772
61756
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
60773
61757
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
60774
- const isDark = theme === "dark";
61758
+ const isDark = ctx.isDark;
60775
61759
  const effectivePalette = await resolveExportPalette(theme, palette);
60776
61760
  const sitemapParsed = parseSitemap2(content, effectivePalette);
60777
61761
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -60779,7 +61763,7 @@ async function exportSitemap(ctx) {
60779
61763
  const activeTagGroup = resolveActiveTagGroup(
60780
61764
  sitemapParsed.tagGroups,
60781
61765
  sitemapParsed.options["active-tag"],
60782
- viewState?.tag ?? options?.tagGroup
61766
+ ctxTagOverride(ctx)
60783
61767
  );
60784
61768
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60785
61769
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60810,7 +61794,7 @@ async function exportSitemap(ctx) {
60810
61794
  return finalizeSvgExport(container, theme, effectivePalette);
60811
61795
  }
60812
61796
  async function exportKanban(ctx) {
60813
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61797
+ const { content, theme, palette, viewState, exportMode } = ctx;
60814
61798
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
60815
61799
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
60816
61800
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -60822,11 +61806,11 @@ async function exportKanban(ctx) {
60822
61806
  document.body.appendChild(container);
60823
61807
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
60824
61808
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
60825
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61809
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
60826
61810
  activeTagGroup: resolveActiveTagGroup(
60827
61811
  kanbanParsed.tagGroups,
60828
61812
  kanbanParsed.options["active-tag"],
60829
- viewState?.tag ?? options?.tagGroup
61813
+ ctxTagOverride(ctx)
60830
61814
  ),
60831
61815
  currentSwimlaneGroup: viewState?.swim ?? null,
60832
61816
  ...kanbanCollapsedLanes !== void 0 && {
@@ -60859,7 +61843,7 @@ async function exportClass(ctx) {
60859
61843
  classParsed,
60860
61844
  classLayout,
60861
61845
  effectivePalette,
60862
- theme === "dark",
61846
+ ctx.isDark,
60863
61847
  void 0,
60864
61848
  { width: exportWidth, height: exportHeight },
60865
61849
  void 0,
@@ -60868,7 +61852,7 @@ async function exportClass(ctx) {
60868
61852
  return finalizeSvgExport(container, theme, effectivePalette);
60869
61853
  }
60870
61854
  async function exportEr(ctx) {
60871
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61855
+ const { content, theme, palette, viewState, exportMode } = ctx;
60872
61856
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
60873
61857
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
60874
61858
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -60886,13 +61870,13 @@ async function exportEr(ctx) {
60886
61870
  erParsed,
60887
61871
  erLayout,
60888
61872
  effectivePalette,
60889
- theme === "dark",
61873
+ ctx.isDark,
60890
61874
  void 0,
60891
61875
  { width: exportWidth, height: exportHeight },
60892
61876
  resolveActiveTagGroup(
60893
61877
  erParsed.tagGroups,
60894
61878
  erParsed.options["active-tag"],
60895
- viewState?.tag ?? options?.tagGroup
61879
+ ctxTagOverride(ctx)
60896
61880
  ),
60897
61881
  viewState?.sem,
60898
61882
  exportMode
@@ -60900,7 +61884,7 @@ async function exportEr(ctx) {
60900
61884
  return finalizeSvgExport(container, theme, effectivePalette);
60901
61885
  }
60902
61886
  async function exportBoxesAndLines(ctx) {
60903
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61887
+ const { content, theme, palette, viewState, exportMode } = ctx;
60904
61888
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
60905
61889
  const effectivePalette = await resolveExportPalette(theme, palette);
60906
61890
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -60920,13 +61904,13 @@ async function exportBoxesAndLines(ctx) {
60920
61904
  const exportWidth = blLayout.width + PADDING3 * 2;
60921
61905
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
60922
61906
  const container = createExportContainer(exportWidth, exportHeight);
60923
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61907
+ const blActiveTagGroup = ctxTagOverride(ctx);
60924
61908
  renderBoxesAndLinesForExport2(
60925
61909
  container,
60926
61910
  blParsed,
60927
61911
  blLayout,
60928
61912
  effectivePalette,
60929
- theme === "dark",
61913
+ ctx.isDark,
60930
61914
  {
60931
61915
  exportDims: { width: exportWidth, height: exportHeight },
60932
61916
  ...blActiveTagGroup !== void 0 && {
@@ -60941,12 +61925,12 @@ async function exportBoxesAndLines(ctx) {
60941
61925
  return finalizeSvgExport(container, theme, effectivePalette);
60942
61926
  }
60943
61927
  async function exportMindmap(ctx) {
60944
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61928
+ const { content, theme, palette, viewState, exportMode } = ctx;
60945
61929
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
60946
61930
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
60947
61931
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
60948
61932
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
60949
- const isDark = theme === "dark";
61933
+ const isDark = ctx.isDark;
60950
61934
  const effectivePalette = await resolveExportPalette(theme, palette);
60951
61935
  const mmParsed = parseMindmap2(content, effectivePalette);
60952
61936
  if (mmParsed.error) return "";
@@ -60954,7 +61938,7 @@ async function exportMindmap(ctx) {
60954
61938
  const activeTagGroup = resolveActiveTagGroup(
60955
61939
  mmParsed.tagGroups,
60956
61940
  mmParsed.options["active-tag"],
60957
- viewState?.tag ?? options?.tagGroup
61941
+ ctxTagOverride(ctx)
60958
61942
  );
60959
61943
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
60960
61944
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -61004,7 +61988,7 @@ async function exportWireframe(ctx) {
61004
61988
  wireframeParsed,
61005
61989
  wireframeLayout,
61006
61990
  effectivePalette,
61007
- theme === "dark",
61991
+ ctx.isDark,
61008
61992
  void 0,
61009
61993
  { width: exportWidth, height: exportHeight },
61010
61994
  theme
@@ -61012,7 +61996,7 @@ async function exportWireframe(ctx) {
61012
61996
  return finalizeSvgExport(container, theme, effectivePalette);
61013
61997
  }
61014
61998
  async function exportC4(ctx) {
61015
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61999
+ const { content, theme, palette, viewState, exportMode } = ctx;
61016
62000
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
61017
62001
  const {
61018
62002
  layoutC4Context: layoutC4Context2,
@@ -61024,9 +62008,9 @@ async function exportC4(ctx) {
61024
62008
  const effectivePalette = await resolveExportPalette(theme, palette);
61025
62009
  const c4Parsed = parseC42(content, effectivePalette);
61026
62010
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
61027
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
61028
- const c4System = options?.c4System ?? viewState?.c4s;
61029
- const c4Container = options?.c4Container ?? viewState?.c4c;
62011
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62012
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62013
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
61030
62014
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
61031
62015
  if (c4Layout.nodes.length === 0) return "";
61032
62016
  const PADDING3 = 20;
@@ -61040,13 +62024,13 @@ async function exportC4(ctx) {
61040
62024
  c4Parsed,
61041
62025
  c4Layout,
61042
62026
  effectivePalette,
61043
- theme === "dark",
62027
+ ctx.isDark,
61044
62028
  void 0,
61045
62029
  { width: exportWidth, height: exportHeight },
61046
62030
  resolveActiveTagGroup(
61047
62031
  c4Parsed.tagGroups,
61048
62032
  c4Parsed.options["active-tag"],
61049
- viewState?.tag ?? options?.tagGroup
62033
+ ctxTagOverride(ctx)
61050
62034
  ),
61051
62035
  exportMode
61052
62036
  );
@@ -61067,14 +62051,14 @@ async function exportFlowchart(ctx) {
61067
62051
  fcParsed,
61068
62052
  layout,
61069
62053
  effectivePalette,
61070
- theme === "dark",
62054
+ ctx.isDark,
61071
62055
  void 0,
61072
62056
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61073
62057
  );
61074
62058
  return finalizeSvgExport(container, theme, effectivePalette);
61075
62059
  }
61076
62060
  async function exportInfra(ctx) {
61077
- const { content, theme, palette, viewState, options } = ctx;
62061
+ const { content, theme, palette, viewState } = ctx;
61078
62062
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61079
62063
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61080
62064
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61087,7 +62071,7 @@ async function exportInfra(ctx) {
61087
62071
  const activeTagGroup = resolveActiveTagGroup(
61088
62072
  infraParsed.tagGroups,
61089
62073
  infraParsed.options["active-tag"],
61090
- viewState?.tag ?? options?.tagGroup
62074
+ ctxTagOverride(ctx)
61091
62075
  );
61092
62076
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61093
62077
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61105,7 +62089,7 @@ async function exportInfra(ctx) {
61105
62089
  container,
61106
62090
  infraLayout,
61107
62091
  effectivePalette,
61108
- theme === "dark",
62092
+ ctx.isDark,
61109
62093
  showInfraTitle ? infraParsed.title : null,
61110
62094
  showInfraTitle ? infraParsed.titleLineNumber : null,
61111
62095
  infraTagGroups,
@@ -61152,7 +62136,7 @@ async function exportPert(ctx) {
61152
62136
  pertResolved,
61153
62137
  pertLayout,
61154
62138
  effectivePalette,
61155
- theme === "dark",
62139
+ ctx.isDark,
61156
62140
  {
61157
62141
  title: pertParsed.title,
61158
62142
  exportDims: { width: exportW, height: exportH },
@@ -61165,7 +62149,7 @@ async function exportPert(ctx) {
61165
62149
  return finalizeSvgExport(container, theme, effectivePalette);
61166
62150
  }
61167
62151
  async function exportGantt(ctx) {
61168
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62152
+ const { content, theme, palette, viewState, exportMode } = ctx;
61169
62153
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61170
62154
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61171
62155
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61183,7 +62167,7 @@ async function exportGantt(ctx) {
61183
62167
  container,
61184
62168
  resolved,
61185
62169
  effectivePalette,
61186
- theme === "dark",
62170
+ ctx.isDark,
61187
62171
  {
61188
62172
  ...ganttCollapsedGroups !== void 0 && {
61189
62173
  collapsedGroups: ganttCollapsedGroups
@@ -61197,7 +62181,7 @@ async function exportGantt(ctx) {
61197
62181
  currentActiveGroup: resolveActiveTagGroup(
61198
62182
  resolved.tagGroups,
61199
62183
  resolved.options.activeTag ?? void 0,
61200
- viewState?.tag ?? options?.tagGroup
62184
+ ctxTagOverride(ctx)
61201
62185
  ),
61202
62186
  exportMode
61203
62187
  },
@@ -61220,7 +62204,7 @@ async function exportState(ctx) {
61220
62204
  stateParsed,
61221
62205
  layout,
61222
62206
  effectivePalette,
61223
- theme === "dark",
62207
+ ctx.isDark,
61224
62208
  void 0,
61225
62209
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61226
62210
  );
@@ -61240,7 +62224,7 @@ async function exportTechRadar(ctx) {
61240
62224
  container,
61241
62225
  radarParsed,
61242
62226
  effectivePalette,
61243
- theme === "dark",
62227
+ ctx.isDark,
61244
62228
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61245
62229
  viewState,
61246
62230
  exportMode
@@ -61257,13 +62241,13 @@ async function exportJourneyMap(ctx) {
61257
62241
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61258
62242
  return "";
61259
62243
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61260
- isDark: theme === "dark"
62244
+ isDark: ctx.isDark
61261
62245
  });
61262
62246
  const container = createExportContainer(
61263
62247
  jmLayout.totalWidth,
61264
62248
  jmLayout.totalHeight
61265
62249
  );
61266
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62250
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61267
62251
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61268
62252
  exportMode
61269
62253
  });
@@ -61281,7 +62265,7 @@ async function exportCycle(ctx) {
61281
62265
  container,
61282
62266
  cycleParsed,
61283
62267
  effectivePalette,
61284
- theme === "dark",
62268
+ ctx.isDark,
61285
62269
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61286
62270
  viewState,
61287
62271
  exportMode
@@ -61318,7 +62302,7 @@ async function exportMap(ctx) {
61318
62302
  mapResolved,
61319
62303
  mapData,
61320
62304
  effectivePalette,
61321
- theme === "dark",
62305
+ ctx.isDark,
61322
62306
  dims
61323
62307
  );
61324
62308
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61335,7 +62319,7 @@ async function exportPyramid(ctx) {
61335
62319
  container,
61336
62320
  pyramidParsed,
61337
62321
  effectivePalette,
61338
- theme === "dark",
62322
+ ctx.isDark,
61339
62323
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61340
62324
  );
61341
62325
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61352,7 +62336,7 @@ async function exportRing(ctx) {
61352
62336
  container,
61353
62337
  ringParsed,
61354
62338
  effectivePalette,
61355
- theme === "dark",
62339
+ ctx.isDark,
61356
62340
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61357
62341
  );
61358
62342
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61369,7 +62353,7 @@ async function exportRaci(ctx) {
61369
62353
  container,
61370
62354
  raciParsed,
61371
62355
  effectivePalette,
61372
- theme === "dark",
62356
+ ctx.isDark,
61373
62357
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61374
62358
  );
61375
62359
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61392,7 +62376,7 @@ async function exportSlope(ctx) {
61392
62376
  container,
61393
62377
  parsed,
61394
62378
  effectivePalette,
61395
- theme === "dark",
62379
+ ctx.isDark,
61396
62380
  void 0,
61397
62381
  dims
61398
62382
  );
@@ -61408,14 +62392,14 @@ async function exportArc(ctx) {
61408
62392
  container,
61409
62393
  parsed,
61410
62394
  effectivePalette,
61411
- theme === "dark",
62395
+ ctx.isDark,
61412
62396
  void 0,
61413
62397
  dims
61414
62398
  );
61415
62399
  return finalizeSvgExport(container, theme, effectivePalette);
61416
62400
  }
61417
62401
  async function exportTimeline(ctx) {
61418
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62402
+ const { content, theme, palette, viewState, exportMode } = ctx;
61419
62403
  const parsed = parseTimeline(content, palette);
61420
62404
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61421
62405
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61424,13 +62408,13 @@ async function exportTimeline(ctx) {
61424
62408
  container,
61425
62409
  parsed,
61426
62410
  effectivePalette,
61427
- theme === "dark",
62411
+ ctx.isDark,
61428
62412
  void 0,
61429
62413
  dims,
61430
62414
  resolveActiveTagGroup(
61431
62415
  parsed.timelineTagGroups,
61432
62416
  parsed.timelineActiveTag,
61433
- viewState?.tag ?? options?.tagGroup
62417
+ ctxTagOverride(ctx)
61434
62418
  ),
61435
62419
  viewState?.swim,
61436
62420
  void 0,
@@ -61449,7 +62433,7 @@ async function exportWordcloud(ctx) {
61449
62433
  container,
61450
62434
  parsed,
61451
62435
  effectivePalette,
61452
- theme === "dark",
62436
+ ctx.isDark,
61453
62437
  dims
61454
62438
  );
61455
62439
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61464,7 +62448,7 @@ async function exportVenn(ctx) {
61464
62448
  container,
61465
62449
  parsed,
61466
62450
  effectivePalette,
61467
- theme === "dark",
62451
+ ctx.isDark,
61468
62452
  void 0,
61469
62453
  dims
61470
62454
  );
@@ -61480,14 +62464,14 @@ async function exportQuadrant(ctx) {
61480
62464
  container,
61481
62465
  parsed,
61482
62466
  effectivePalette,
61483
- theme === "dark",
62467
+ ctx.isDark,
61484
62468
  void 0,
61485
62469
  dims
61486
62470
  );
61487
62471
  return finalizeSvgExport(container, theme, effectivePalette);
61488
62472
  }
61489
62473
  async function exportVisualization(ctx) {
61490
- const { content, theme, palette, viewState, options } = ctx;
62474
+ const { content, theme, palette, viewState } = ctx;
61491
62475
  const parsed = parseVisualization(content, palette);
61492
62476
  if (parsed.type !== "sequence") {
61493
62477
  if (parsed.error) {
@@ -61498,7 +62482,7 @@ async function exportVisualization(ctx) {
61498
62482
  }
61499
62483
  }
61500
62484
  const effectivePalette = await resolveExportPalette(theme, palette);
61501
- const isDark = theme === "dark";
62485
+ const isDark = ctx.isDark;
61502
62486
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61503
62487
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61504
62488
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61506,7 +62490,7 @@ async function exportVisualization(ctx) {
61506
62490
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61507
62491
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61508
62492
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61509
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62493
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61510
62494
  renderSequenceDiagram2(
61511
62495
  container,
61512
62496
  seqParsed,
@@ -61752,16 +62736,13 @@ var themes = {
61752
62736
 
61753
62737
  // src/dimensions.ts
61754
62738
  init_dgmo_router();
61755
- init_scaling();
61756
62739
  init_chart_type_registry();
61757
62740
  function getMinDimensions(content) {
61758
62741
  const { chartType } = parseDgmo(content);
61759
62742
  if (!chartType) return { width: 300, height: 200 };
61760
- const counts = extractContentCounts(content, chartType);
61761
- return computeMinDimensions(chartType, counts);
61762
- }
61763
- function extractContentCounts(content, chartType) {
61764
- return REGISTRY_BY_ID.get(chartType)?.measure?.(content) ?? {};
62743
+ const descriptor = REGISTRY_BY_ID.get(chartType);
62744
+ const counts = descriptor?.measure?.(content) ?? {};
62745
+ return descriptor?.minDims?.(counts) ?? { width: 300, height: 200 };
61765
62746
  }
61766
62747
 
61767
62748
  // src/utils/svg-embed.ts