@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/auto.mjs 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
@@ -4092,6 +4303,30 @@ var init_scaling = __esm({
4092
4303
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4093
4304
  return new _ScaleContext(clamped, minScaleFactor);
4094
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
+ }
4095
4330
  static identity() {
4096
4331
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4097
4332
  }
@@ -5792,12 +6027,7 @@ function parseSequenceDgmo(content, palette) {
5792
6027
  const trimmed = token.trim();
5793
6028
  return nameAliasMap.get(trimmed) ?? trimmed;
5794
6029
  };
5795
- const fail = (line11, message) => {
5796
- const diag = makeDgmoError(line11, message);
5797
- result.diagnostics.push(diag);
5798
- result.error = formatDgmoError(diag);
5799
- return result;
5800
- };
6030
+ const fail = makeFail(result);
5801
6031
  const pushError = (line11, message) => {
5802
6032
  const diag = makeDgmoError(line11, message);
5803
6033
  result.diagnostics.push(diag);
@@ -5812,6 +6042,7 @@ function parseSequenceDgmo(content, palette) {
5812
6042
  const lines = content.split("\n");
5813
6043
  let hasExplicitChart = false;
5814
6044
  let contentStarted = false;
6045
+ let bodyStarted = false;
5815
6046
  let firstLineIndex = -1;
5816
6047
  for (let fi = 0; fi < lines.length; fi++) {
5817
6048
  const fl = lines[fi].trim();
@@ -6123,6 +6354,7 @@ function parseSequenceDgmo(content, palette) {
6123
6354
  );
6124
6355
  }
6125
6356
  contentStarted = true;
6357
+ bodyStarted = true;
6126
6358
  const section = {
6127
6359
  kind: "section",
6128
6360
  // Capture group 1 guaranteed present after successful match.
@@ -6338,7 +6570,7 @@ function parseSequenceDgmo(content, palette) {
6338
6570
  alias: bareAlias
6339
6571
  } = splitPipe(trimmed, lineNumber);
6340
6572
  const inGroup = activeGroup && measureIndent(raw) > 0;
6341
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6573
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6342
6574
  contentStarted = true;
6343
6575
  const id = bareCore;
6344
6576
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6385,6 +6617,7 @@ function parseSequenceDgmo(content, palette) {
6385
6617
  }
6386
6618
  if (labeledArrow) {
6387
6619
  contentStarted = true;
6620
+ bodyStarted = true;
6388
6621
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6389
6622
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6390
6623
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6449,6 +6682,7 @@ function parseSequenceDgmo(content, palette) {
6449
6682
  const bareCall = bareCallSync || bareCallAsync;
6450
6683
  if (bareCall) {
6451
6684
  contentStarted = true;
6685
+ bodyStarted = true;
6452
6686
  const from = bareCall[1];
6453
6687
  const to = bareCall[2];
6454
6688
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6470,6 +6704,7 @@ function parseSequenceDgmo(content, palette) {
6470
6704
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6471
6705
  if (ifMatch) {
6472
6706
  contentStarted = true;
6707
+ bodyStarted = true;
6473
6708
  const block = {
6474
6709
  kind: "block",
6475
6710
  type: "if",
@@ -6486,6 +6721,7 @@ function parseSequenceDgmo(content, palette) {
6486
6721
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6487
6722
  if (loopMatch) {
6488
6723
  contentStarted = true;
6724
+ bodyStarted = true;
6489
6725
  const block = {
6490
6726
  kind: "block",
6491
6727
  type: "loop",
@@ -6502,6 +6738,7 @@ function parseSequenceDgmo(content, palette) {
6502
6738
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6503
6739
  if (parallelMatch) {
6504
6740
  contentStarted = true;
6741
+ bodyStarted = true;
6505
6742
  const block = {
6506
6743
  kind: "block",
6507
6744
  type: "parallel",
@@ -6598,6 +6835,7 @@ function parseSequenceDgmo(content, palette) {
6598
6835
  lineNumber,
6599
6836
  endLineNumber: lineNumber
6600
6837
  };
6838
+ bodyStarted = true;
6601
6839
  currentContainer().push(note);
6602
6840
  continue;
6603
6841
  }
@@ -6622,6 +6860,7 @@ function parseSequenceDgmo(content, palette) {
6622
6860
  endLineNumber: i + 1
6623
6861
  // i has advanced past the body lines (1-based)
6624
6862
  };
6863
+ bodyStarted = true;
6625
6864
  currentContainer().push(note);
6626
6865
  continue;
6627
6866
  }
@@ -7466,6 +7705,15 @@ function parseNodeRef(text) {
7466
7705
  }
7467
7706
  return null;
7468
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
+ }
7469
7717
  function splitArrows(line11) {
7470
7718
  const segments = [];
7471
7719
  const arrowPositions = [];
@@ -7561,6 +7809,7 @@ function parseFlowchart(content, palette) {
7561
7809
  const notes = [];
7562
7810
  let contentStarted = false;
7563
7811
  let firstLineParsed = false;
7812
+ let prevLineLastNodeId = null;
7564
7813
  const nameAliasMap = /* @__PURE__ */ new Map();
7565
7814
  function peelAlias2(seg) {
7566
7815
  const trimmed = seg.trim();
@@ -7568,6 +7817,19 @@ function parseFlowchart(content, palette) {
7568
7817
  if (!m) return { seg: trimmed };
7569
7818
  return { seg: m[1].trim(), alias: m[2] };
7570
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
+ }
7571
7833
  function getOrCreateNode(ref, lineNumber) {
7572
7834
  const key = ref.id;
7573
7835
  const existing = nodeMap.get(key);
@@ -7626,6 +7888,8 @@ function parseFlowchart(content, palette) {
7626
7888
  indentStack[indentStack.length - 1].nodeId
7627
7889
  ) : null;
7628
7890
  const segments = splitArrows(trimmed);
7891
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7892
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7629
7893
  if (segments.length === 1) {
7630
7894
  const peeled = peelAlias2(segments[0]);
7631
7895
  const ref = parseNodeRef(peeled.seg);
@@ -7635,6 +7899,13 @@ function parseFlowchart(content, palette) {
7635
7899
  indentStack.push({ nodeId: node.id, indent });
7636
7900
  return node.id;
7637
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
+ }
7638
7909
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7639
7910
  if (aliasResolved !== void 0) {
7640
7911
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7670,16 +7941,23 @@ function parseFlowchart(content, palette) {
7670
7941
  }
7671
7942
  }
7672
7943
  }
7944
+ if (!ref) {
7945
+ const loose = parseNodeRefLoose(peeled.seg);
7946
+ if (loose) {
7947
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7948
+ ref = loose.ref;
7949
+ }
7950
+ }
7673
7951
  if (!ref) continue;
7674
7952
  const node = getOrCreateNode(ref, lineNumber);
7675
7953
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7676
7954
  if (pendingArrow !== null) {
7677
- const sourceId = lastNodeId ?? implicitSourceId;
7955
+ const sourceId = lastNodeId ?? effectiveSource;
7678
7956
  if (sourceId) {
7679
7957
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7680
7958
  }
7681
7959
  pendingArrow = null;
7682
- } else if (lastNodeId === null && implicitSourceId === null) {
7960
+ } else if (lastNodeId === null && effectiveSource === null) {
7683
7961
  }
7684
7962
  lastNodeId = node.id;
7685
7963
  }
@@ -7761,7 +8039,8 @@ function parseFlowchart(content, palette) {
7761
8039
  continue;
7762
8040
  }
7763
8041
  }
7764
- processContentLine(trimmed, lineNumber, indent);
8042
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8043
+ if (lastId) prevLineLastNodeId = lastId;
7765
8044
  }
7766
8045
  if (result.nodes.length === 0 && !result.error) {
7767
8046
  const diag = makeDgmoError(
@@ -8844,7 +9123,12 @@ function parseERDiagram(content, palette) {
8844
9123
  }
8845
9124
  if (currentTagGroup && !contentStarted && indent > 0) {
8846
9125
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8847
- const { label, color } = extractColor(cleanEntry, palette);
9126
+ const { label, color } = extractColor(
9127
+ cleanEntry,
9128
+ palette,
9129
+ result.diagnostics,
9130
+ lineNumber
9131
+ );
8848
9132
  if (isDefault) {
8849
9133
  currentTagGroup.defaultValue = label;
8850
9134
  } else if (currentTagGroup.entries.length === 0) {
@@ -9162,12 +9446,7 @@ function parseOrg(content, palette) {
9162
9446
  diagnostics: [],
9163
9447
  error: null
9164
9448
  };
9165
- const fail = (line11, message) => {
9166
- const diag = makeDgmoError(line11, message);
9167
- result.diagnostics.push(diag);
9168
- result.error = formatDgmoError(diag);
9169
- return result;
9170
- };
9449
+ const fail = makeFail(result);
9171
9450
  const pushError = (line11, message) => {
9172
9451
  const diag = makeDgmoError(line11, message);
9173
9452
  result.diagnostics.push(diag);
@@ -9276,7 +9555,12 @@ function parseOrg(content, palette) {
9276
9555
  const indent2 = measureIndent(line11);
9277
9556
  if (indent2 > 0) {
9278
9557
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9279
- const { label, color } = extractColor(cleanEntry, palette);
9558
+ const { label, color } = extractColor(
9559
+ cleanEntry,
9560
+ palette,
9561
+ result.diagnostics,
9562
+ lineNumber
9563
+ );
9280
9564
  if (isDefault) {
9281
9565
  currentTagGroup.defaultValue = label;
9282
9566
  } else if (currentTagGroup.entries.length === 0) {
@@ -9562,12 +9846,7 @@ function parseSitemap(content, palette) {
9562
9846
  diagnostics: [],
9563
9847
  error: null
9564
9848
  };
9565
- const fail = (line11, message) => {
9566
- const diag = makeDgmoError(line11, message);
9567
- result.diagnostics.push(diag);
9568
- result.error = formatDgmoError(diag);
9569
- return result;
9570
- };
9849
+ const fail = makeFail(result);
9571
9850
  const pushError = (line11, message) => {
9572
9851
  const diag = makeDgmoError(line11, message);
9573
9852
  result.diagnostics.push(diag);
@@ -9668,7 +9947,12 @@ function parseSitemap(content, palette) {
9668
9947
  const indent2 = measureIndent(line11);
9669
9948
  if (indent2 > 0) {
9670
9949
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9671
- const { label, color } = extractColor(cleanEntry, palette);
9950
+ const { label, color } = extractColor(
9951
+ cleanEntry,
9952
+ palette,
9953
+ result.diagnostics,
9954
+ lineNumber
9955
+ );
9672
9956
  currentTagGroup.entries.push({
9673
9957
  value: label,
9674
9958
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11383,7 +11667,7 @@ var init_chart_types = __esm({
11383
11667
  },
11384
11668
  {
11385
11669
  id: "sequence",
11386
- description: "Message / interaction flows",
11670
+ description: "Message request and response interaction flows",
11387
11671
  fallback: true
11388
11672
  },
11389
11673
  {
@@ -11474,7 +11758,7 @@ var init_chart_types = __esm({
11474
11758
  },
11475
11759
  {
11476
11760
  id: "slope",
11477
- description: "Change between two periods"
11761
+ description: "Change between 2 time periods"
11478
11762
  },
11479
11763
  {
11480
11764
  id: "sankey",
@@ -11503,7 +11787,7 @@ var init_chart_types = __esm({
11503
11787
  // ── Tier 4 — General-purpose data charts ──────────────────
11504
11788
  {
11505
11789
  id: "bar",
11506
- description: "Categorical comparisons",
11790
+ description: "Categorical comparisons for 3 - 5 figures",
11507
11791
  fallback: true
11508
11792
  },
11509
11793
  {
@@ -11765,7 +12049,9 @@ function parseChart(content, palette) {
11765
12049
  if (dataValues) {
11766
12050
  const { label: rawLabel, color: pointColor } = extractColor(
11767
12051
  dataValues.label,
11768
- palette
12052
+ palette,
12053
+ result.diagnostics,
12054
+ lineNumber
11769
12055
  );
11770
12056
  const [first, ...rest] = dataValues.values;
11771
12057
  result.data.push({
@@ -11830,6 +12116,12 @@ function parseChart(content, palette) {
11830
12116
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11831
12117
  );
11832
12118
  }
12119
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12120
+ warn2(
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
+ }
11833
12125
  if (!result.error && result.seriesNames) {
11834
12126
  const expectedCount = result.seriesNames.length;
11835
12127
  for (const dp of result.data) {
@@ -12100,7 +12392,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12100
12392
  if (!dataRow || dataRow.values.length < 2) return null;
12101
12393
  const { label: rawLabel, color: pointColor } = extractColor(
12102
12394
  dataRow.label,
12103
- palette
12395
+ palette,
12396
+ diagnostics,
12397
+ lineNumber
12104
12398
  );
12105
12399
  return {
12106
12400
  name: rawLabel,
@@ -12238,11 +12532,15 @@ function parseExtendedChartFull(content, palette) {
12238
12532
  const targetResolved = resolveSlot(rawTarget);
12239
12533
  const { label: source, color: sourceColor } = extractColor(
12240
12534
  sourceResolved,
12241
- palette
12535
+ palette,
12536
+ result.diagnostics,
12537
+ lineNumber
12242
12538
  );
12243
12539
  const { label: target, color: targetColor } = extractColor(
12244
12540
  targetResolved,
12245
- palette
12541
+ palette,
12542
+ result.diagnostics,
12543
+ lineNumber
12246
12544
  );
12247
12545
  if (sourceColor || targetColor) {
12248
12546
  if (!result.nodeColors) result.nodeColors = {};
@@ -12294,7 +12592,9 @@ function parseExtendedChartFull(content, palette) {
12294
12592
  const targetResolved = resolveSlot(dataRow2.label);
12295
12593
  const { label: target, color: targetColor } = extractColor(
12296
12594
  targetResolved,
12297
- palette
12595
+ palette,
12596
+ result.diagnostics,
12597
+ lineNumber
12298
12598
  );
12299
12599
  if (targetColor) {
12300
12600
  if (!result.nodeColors) result.nodeColors = {};
@@ -12327,7 +12627,9 @@ function parseExtendedChartFull(content, palette) {
12327
12627
  const trimmedResolved = resolveSlot(trimmed);
12328
12628
  const { label: nodeName, color: nodeColor2 } = extractColor(
12329
12629
  trimmedResolved,
12330
- palette
12630
+ palette,
12631
+ result.diagnostics,
12632
+ lineNumber
12331
12633
  );
12332
12634
  if (nodeColor2) {
12333
12635
  if (!result.nodeColors) result.nodeColors = {};
@@ -12417,8 +12719,11 @@ function parseExtendedChartFull(content, palette) {
12417
12719
  min: parseFloat(rangeMatch[1]),
12418
12720
  max: parseFloat(rangeMatch[2])
12419
12721
  };
12722
+ continue;
12723
+ }
12724
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12725
+ continue;
12420
12726
  }
12421
- continue;
12422
12727
  }
12423
12728
  }
12424
12729
  if (firstToken === "no-name") {
@@ -12478,7 +12783,9 @@ function parseExtendedChartFull(content, palette) {
12478
12783
  if (colonIndex >= 0) {
12479
12784
  const { label: fnName, color: fnColor } = extractColor(
12480
12785
  trimmed.substring(0, colonIndex).trim(),
12481
- palette
12786
+ palette,
12787
+ result.diagnostics,
12788
+ lineNumber
12482
12789
  );
12483
12790
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12484
12791
  if (!result.functions) result.functions = [];
@@ -12526,7 +12833,9 @@ function parseExtendedChartFull(content, palette) {
12526
12833
  if (dataRow?.values.length === 1) {
12527
12834
  const { label: rawLabel, color: pointColor } = extractColor(
12528
12835
  dataRow.label,
12529
- palette
12836
+ palette,
12837
+ result.diagnostics,
12838
+ lineNumber
12530
12839
  );
12531
12840
  result.data.push({
12532
12841
  label: rawLabel,
@@ -12625,11 +12934,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12625
12934
  const sc = ctx ?? ScaleContext.identity();
12626
12935
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12627
12936
  if (parsed.type === "sankey") {
12628
- const bg = isDark ? palette.surface : palette.bg;
12937
+ const bg = themeBaseBg(palette, isDark);
12629
12938
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12630
12939
  }
12631
12940
  if (parsed.type === "chord") {
12632
- const bg = isDark ? palette.surface : palette.bg;
12941
+ const bg = themeBaseBg(palette, isDark);
12633
12942
  return buildChordOption(
12634
12943
  parsed,
12635
12944
  palette,
@@ -12670,7 +12979,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12670
12979
  );
12671
12980
  }
12672
12981
  if (parsed.type === "funnel") {
12673
- const bg = isDark ? palette.surface : palette.bg;
12982
+ const bg = themeBaseBg(palette, isDark);
12674
12983
  return buildFunnelOption(
12675
12984
  parsed,
12676
12985
  palette,
@@ -13266,7 +13575,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13266
13575
  const gridLeft = parsed.ylabel ? 12 : 3;
13267
13576
  const gridRight = 4;
13268
13577
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13269
- 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;
13270
13580
  let graphic;
13271
13581
  if (showLabels && points.length > 0) {
13272
13582
  const labelPoints = [];
@@ -13406,7 +13716,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13406
13716
  }
13407
13717
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13408
13718
  const sc = ctx ?? ScaleContext.identity();
13409
- const bg = isDark ? palette.surface : palette.bg;
13719
+ const bg = themeBaseBg(palette, isDark);
13410
13720
  const heatmapRows = parsed.heatmapRows ?? [];
13411
13721
  const columns = parsed.columns ?? [];
13412
13722
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13725,7 +14035,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13725
14035
  colors,
13726
14036
  titleConfig
13727
14037
  } = buildChartCommons(parsed, palette, isDark, sc);
13728
- const bg = isDark ? palette.surface : palette.bg;
14038
+ const bg = themeBaseBg(palette, isDark);
13729
14039
  switch (parsed.type) {
13730
14040
  case "bar":
13731
14041
  return buildBarOption(
@@ -14673,12 +14983,7 @@ function parseKanban(content, palette) {
14673
14983
  diagnostics: [],
14674
14984
  error: null
14675
14985
  };
14676
- const fail = (line11, message) => {
14677
- const diag = makeDgmoError(line11, message);
14678
- result.diagnostics.push(diag);
14679
- result.error = formatDgmoError(diag);
14680
- return result;
14681
- };
14986
+ const fail = makeFail(result);
14682
14987
  const warn2 = (line11, message) => {
14683
14988
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14684
14989
  };
@@ -14781,7 +15086,12 @@ function parseKanban(content, palette) {
14781
15086
  const indent2 = measureIndent(line11);
14782
15087
  if (indent2 > 0) {
14783
15088
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14784
- const { label, color } = extractColor(cleanEntry, palette);
15089
+ const { label, color } = extractColor(
15090
+ cleanEntry,
15091
+ palette,
15092
+ result.diagnostics,
15093
+ lineNumber
15094
+ );
14785
15095
  if (isDefault) {
14786
15096
  currentTagGroup.defaultValue = label;
14787
15097
  } else if (currentTagGroup.entries.length === 0) {
@@ -15125,12 +15435,7 @@ function parseC4(content, palette) {
15125
15435
  if (!result.error && severity === "error")
15126
15436
  result.error = formatDgmoError(diag);
15127
15437
  };
15128
- const fail = (line11, message) => {
15129
- const diag = makeDgmoError(line11, message);
15130
- result.diagnostics.push(diag);
15131
- result.error = formatDgmoError(diag);
15132
- return result;
15133
- };
15438
+ const fail = makeFail(result);
15134
15439
  if (!content?.trim()) {
15135
15440
  return fail(0, "No content provided");
15136
15441
  }
@@ -15231,7 +15536,12 @@ function parseC4(content, palette) {
15231
15536
  const indent2 = measureIndent(line11);
15232
15537
  if (indent2 > 0) {
15233
15538
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15234
- const { label, color } = extractColor(cleanEntry, palette);
15539
+ const { label, color } = extractColor(
15540
+ cleanEntry,
15541
+ palette,
15542
+ result.diagnostics,
15543
+ lineNumber
15544
+ );
15235
15545
  if (isDefault) {
15236
15546
  currentTagGroup.defaultValue = label;
15237
15547
  } else if (currentTagGroup.entries.length === 0) {
@@ -16896,8 +17206,66 @@ function parseInfra(content) {
16896
17206
  }
16897
17207
  }
16898
17208
  validateTagGroupNames(result.tagGroups, warn2, setError);
17209
+ checkReachability(result);
16899
17210
  return result;
16900
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
+ }
16901
17269
  function stripNodeDecorations(name) {
16902
17270
  let s = name.trim();
16903
17271
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17187,7 +17555,12 @@ function parseGantt(content, palette) {
17187
17555
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17188
17556
  if (eraEntryMatch) {
17189
17557
  const eraLabelRaw = eraEntryMatch[3].trim();
17190
- const eraExtracted = extractColor(eraLabelRaw, palette);
17558
+ const eraExtracted = extractColor(
17559
+ eraLabelRaw,
17560
+ palette,
17561
+ diagnostics,
17562
+ lineNumber
17563
+ );
17191
17564
  result.eras.push({
17192
17565
  startDate: eraEntryMatch[1],
17193
17566
  endDate: eraEntryMatch[2],
@@ -17209,7 +17582,12 @@ function parseGantt(content, palette) {
17209
17582
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17210
17583
  if (markerEntryMatch) {
17211
17584
  const markerLabelRaw = markerEntryMatch[2].trim();
17212
- const markerExtracted = extractColor(markerLabelRaw, palette);
17585
+ const markerExtracted = extractColor(
17586
+ markerLabelRaw,
17587
+ palette,
17588
+ diagnostics,
17589
+ lineNumber
17590
+ );
17213
17591
  result.markers.push({
17214
17592
  date: markerEntryMatch[1],
17215
17593
  label: markerExtracted.label,
@@ -17230,7 +17608,12 @@ function parseGantt(content, palette) {
17230
17608
  } else {
17231
17609
  if (COMMENT_RE.test(line11)) continue;
17232
17610
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17233
- const extracted = extractColor(cleanEntry, palette);
17611
+ const extracted = extractColor(
17612
+ cleanEntry,
17613
+ palette,
17614
+ diagnostics,
17615
+ lineNumber
17616
+ );
17234
17617
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17235
17618
  const isFirstEntry = currentTagGroup.entries.length === 0;
17236
17619
  currentTagGroup.entries.push({
@@ -17425,7 +17808,12 @@ function parseGantt(content, palette) {
17425
17808
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17426
17809
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17427
17810
  const eraLabelRaw = eraOffsetMatch[3].trim();
17428
- const eraExtracted = extractColor(eraLabelRaw, palette);
17811
+ const eraExtracted = extractColor(
17812
+ eraLabelRaw,
17813
+ palette,
17814
+ diagnostics,
17815
+ lineNumber
17816
+ );
17429
17817
  result.eras.push({
17430
17818
  startDate: "",
17431
17819
  endDate: "",
@@ -17441,7 +17829,12 @@ function parseGantt(content, palette) {
17441
17829
  if (markerOffsetMatch) {
17442
17830
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17443
17831
  const markerLabelRaw = markerOffsetMatch[2].trim();
17444
- const markerExtracted = extractColor(markerLabelRaw, palette);
17832
+ const markerExtracted = extractColor(
17833
+ markerLabelRaw,
17834
+ palette,
17835
+ diagnostics,
17836
+ lineNumber
17837
+ );
17445
17838
  result.markers.push({
17446
17839
  date: "",
17447
17840
  label: markerExtracted.label,
@@ -17515,7 +17908,12 @@ function parseGantt(content, palette) {
17515
17908
  const eraMatch = line11.match(ERA_RE);
17516
17909
  if (eraMatch) {
17517
17910
  const eraLabelRaw = eraMatch[3].trim();
17518
- const eraExtracted = extractColor(eraLabelRaw, palette);
17911
+ const eraExtracted = extractColor(
17912
+ eraLabelRaw,
17913
+ palette,
17914
+ diagnostics,
17915
+ lineNumber
17916
+ );
17519
17917
  result.eras.push({
17520
17918
  startDate: eraMatch[1],
17521
17919
  endDate: eraMatch[2],
@@ -17533,7 +17931,12 @@ function parseGantt(content, palette) {
17533
17931
  const markerMatch = line11.match(MARKER_RE);
17534
17932
  if (markerMatch) {
17535
17933
  const markerLabelRaw = markerMatch[2].trim();
17536
- const markerExtracted = extractColor(markerLabelRaw, palette);
17934
+ const markerExtracted = extractColor(
17935
+ markerLabelRaw,
17936
+ palette,
17937
+ diagnostics,
17938
+ lineNumber
17939
+ );
17537
17940
  result.markers.push({
17538
17941
  date: markerMatch[1],
17539
17942
  label: markerExtracted.label,
@@ -18930,6 +19333,7 @@ function parseBoxesAndLines(content, palette) {
18930
19333
  const nodes = [];
18931
19334
  const edges = [];
18932
19335
  const groups = [];
19336
+ const nodePositions = /* @__PURE__ */ new Map();
18933
19337
  const result = {
18934
19338
  type: "boxes-and-lines",
18935
19339
  title: null,
@@ -18963,6 +19367,8 @@ function parseBoxesAndLines(content, palette) {
18963
19367
  }
18964
19368
  const groupStack = [];
18965
19369
  let contentStarted = false;
19370
+ let inLayoutBlock = false;
19371
+ const LAYOUT_ENTRY_RE = /^(.+?):\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/;
18966
19372
  let currentTagGroup = null;
18967
19373
  const metaAliasMap = /* @__PURE__ */ new Map();
18968
19374
  const nameAliasMap = /* @__PURE__ */ new Map();
@@ -19002,7 +19408,7 @@ function parseBoxesAndLines(content, palette) {
19002
19408
  const trimmed = raw.trim();
19003
19409
  const indent = measureIndent2(raw);
19004
19410
  if (!trimmed || trimmed.startsWith("//")) continue;
19005
- 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)) {
19006
19412
  result.diagnostics.push(
19007
19413
  makeDgmoError(
19008
19414
  lineNum,
@@ -19131,7 +19537,12 @@ function parseBoxesAndLines(content, palette) {
19131
19537
  if (tagBlockMatch.inlineValues) {
19132
19538
  for (const rawVal of tagBlockMatch.inlineValues) {
19133
19539
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19134
- const { label, color } = extractColor(cleanVal);
19540
+ const { label, color } = extractColor(
19541
+ cleanVal,
19542
+ palette,
19543
+ result.diagnostics,
19544
+ lineNum
19545
+ );
19135
19546
  newTagGroup.entries.push({
19136
19547
  value: label,
19137
19548
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19148,7 +19559,12 @@ function parseBoxesAndLines(content, palette) {
19148
19559
  }
19149
19560
  if (currentTagGroup && !contentStarted && indent > 0) {
19150
19561
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19151
- const { label, color } = extractColor(cleanEntry);
19562
+ const { label, color } = extractColor(
19563
+ cleanEntry,
19564
+ palette,
19565
+ result.diagnostics,
19566
+ lineNum
19567
+ );
19152
19568
  currentTagGroup.entries.push({
19153
19569
  value: label,
19154
19570
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19164,6 +19580,42 @@ function parseBoxesAndLines(content, palette) {
19164
19580
  if (currentTagGroup && indent === 0) {
19165
19581
  currentTagGroup = null;
19166
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
+ }
19167
19619
  if (descState !== null) {
19168
19620
  if (indent > descState.indent) {
19169
19621
  if (trimmed.includes("->") || trimmed.includes("<->")) {
@@ -19416,6 +19868,22 @@ function parseBoxesAndLines(content, palette) {
19416
19868
  );
19417
19869
  }
19418
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
+ }
19419
19887
  if (result.tagGroups.length > 0) {
19420
19888
  injectDefaultTagMetadata(result.nodes, result.tagGroups);
19421
19889
  validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
@@ -19694,7 +20162,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19694
20162
  metadata
19695
20163
  };
19696
20164
  }
19697
- var MAX_GROUP_DEPTH;
20165
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19698
20166
  var init_parser18 = __esm({
19699
20167
  "src/boxes-and-lines/parser.ts"() {
19700
20168
  "use strict";
@@ -19706,6 +20174,8 @@ var init_parser18 = __esm({
19706
20174
  init_reserved_key_registry();
19707
20175
  init_notes();
19708
20176
  MAX_GROUP_DEPTH = 2;
20177
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20178
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19709
20179
  }
19710
20180
  });
19711
20181
 
@@ -19725,12 +20195,7 @@ function parseMindmap(content, palette) {
19725
20195
  diagnostics: [],
19726
20196
  error: null
19727
20197
  };
19728
- const fail = (line11, message) => {
19729
- const diag = makeDgmoError(line11, message);
19730
- result.diagnostics.push(diag);
19731
- result.error = formatDgmoError(diag);
19732
- return result;
19733
- };
20198
+ const fail = makeFail(result);
19734
20199
  const pushError = (line11, message) => {
19735
20200
  const diag = makeDgmoError(line11, message);
19736
20201
  result.diagnostics.push(diag);
@@ -19843,7 +20308,12 @@ function parseMindmap(content, palette) {
19843
20308
  const indent2 = measureIndent(line11);
19844
20309
  if (indent2 > 0) {
19845
20310
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19846
- const { label, color } = extractColor(cleanEntry, palette);
20311
+ const { label, color } = extractColor(
20312
+ cleanEntry,
20313
+ palette,
20314
+ result.diagnostics,
20315
+ lineNumber
20316
+ );
19847
20317
  if (isDefault) {
19848
20318
  currentTagGroup.defaultValue = label;
19849
20319
  } else if (currentTagGroup.entries.length === 0) {
@@ -19920,6 +20390,7 @@ function parseMindmap(content, palette) {
19920
20390
  collectAll(result.roots);
19921
20391
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
19922
20392
  validateTagGroupNames(result.tagGroups, pushWarning);
20393
+ cascadeTagMetadata(result.roots, result.tagGroups);
19923
20394
  }
19924
20395
  if (result.roots.length === 0 && !result.error) {
19925
20396
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20526,7 +20997,12 @@ function parseWireframe(content) {
20526
20997
  }
20527
20998
  if (indent > 0 && currentTagGroup) {
20528
20999
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20529
- const { label, color } = extractColor(cleanEntry);
21000
+ const { label, color } = extractColor(
21001
+ cleanEntry,
21002
+ void 0,
21003
+ diagnostics,
21004
+ lineNumber
21005
+ );
20530
21006
  currentTagGroup.entries.push({
20531
21007
  value: label,
20532
21008
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20774,12 +21250,7 @@ function parseTechRadar(content) {
20774
21250
  diagnostics: [],
20775
21251
  error: null
20776
21252
  };
20777
- const fail = (line11, message) => {
20778
- const diag = makeDgmoError(line11, message);
20779
- result.diagnostics.push(diag);
20780
- result.error = formatDgmoError(diag);
20781
- return result;
20782
- };
21253
+ const fail = makeFail(result);
20783
21254
  const warn2 = (line11, message) => {
20784
21255
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20785
21256
  };
@@ -21147,12 +21618,7 @@ function parseCycle(content) {
21147
21618
  let state = "top";
21148
21619
  let currentNode = null;
21149
21620
  let currentEdge = null;
21150
- const fail = (line11, message) => {
21151
- const diag = makeDgmoError(line11, message);
21152
- result.diagnostics.push(diag);
21153
- result.error = formatDgmoError(diag);
21154
- return result;
21155
- };
21621
+ const fail = makeFail(result);
21156
21622
  const warn2 = (line11, message, severity = "warning") => {
21157
21623
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21158
21624
  };
@@ -21429,12 +21895,7 @@ function parseJourneyMap(content, palette) {
21429
21895
  diagnostics: [],
21430
21896
  error: null
21431
21897
  };
21432
- const fail = (line11, message) => {
21433
- const diag = makeDgmoError(line11, message);
21434
- result.diagnostics.push(diag);
21435
- result.error = formatDgmoError(diag);
21436
- return result;
21437
- };
21898
+ const fail = makeFail(result);
21438
21899
  const warn2 = (line11, message) => {
21439
21900
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21440
21901
  };
@@ -21516,7 +21977,18 @@ function parseJourneyMap(content, palette) {
21516
21977
  }
21517
21978
  }
21518
21979
  } else {
21519
- 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
+ }
21520
21992
  }
21521
21993
  if (!personaName) {
21522
21994
  return fail(lineNumber, "persona requires a name");
@@ -21572,7 +22044,12 @@ function parseJourneyMap(content, palette) {
21572
22044
  if (currentTagGroup && !contentStarted) {
21573
22045
  if (indent > 0) {
21574
22046
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21575
- const { label, color } = extractColor(cleanEntry, palette);
22047
+ const { label, color } = extractColor(
22048
+ cleanEntry,
22049
+ palette,
22050
+ result.diagnostics,
22051
+ lineNumber
22052
+ );
21576
22053
  if (isDefault) {
21577
22054
  currentTagGroup.defaultValue = label;
21578
22055
  } else if (currentTagGroup.entries.length === 0) {
@@ -21944,12 +22421,7 @@ function parsePyramid(content) {
21944
22421
  const lines = content.split("\n");
21945
22422
  let headerParsed = false;
21946
22423
  let currentLayer = null;
21947
- const fail = (line11, message) => {
21948
- const diag = makeDgmoError(line11, message);
21949
- result.diagnostics.push(diag);
21950
- result.error = formatDgmoError(diag);
21951
- return result;
21952
- };
22424
+ const fail = makeFail(result);
21953
22425
  const warn2 = (line11, message, severity = "warning") => {
21954
22426
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21955
22427
  };
@@ -22096,12 +22568,7 @@ function parseRing(content) {
22096
22568
  const lines = content.split("\n");
22097
22569
  let headerParsed = false;
22098
22570
  let currentLayer = null;
22099
- const fail = (line11, message) => {
22100
- const diag = makeDgmoError(line11, message);
22101
- result.diagnostics.push(diag);
22102
- result.error = formatDgmoError(diag);
22103
- return result;
22104
- };
22571
+ const fail = makeFail(result);
22105
22572
  const warn2 = (line11, message, severity = "warning") => {
22106
22573
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22107
22574
  };
@@ -22565,12 +23032,7 @@ function parseRaci(content, palette) {
22565
23032
  diagnostics: [],
22566
23033
  error: null
22567
23034
  };
22568
- const fail = (line11, message) => {
22569
- const diag = makeDgmoError(line11, message);
22570
- result.diagnostics.push(diag);
22571
- result.error = formatDgmoError(diag);
22572
- return result;
22573
- };
23035
+ const fail = makeFail(result);
22574
23036
  const warn2 = (line11, message, code) => {
22575
23037
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22576
23038
  };
@@ -23173,6 +23635,81 @@ function measureInfra(content) {
23173
23635
  const parsed = parseInfra(content);
23174
23636
  return { nodes: parsed.nodes.length };
23175
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
+ }
23176
23713
  function isExtendedChartParser(parse) {
23177
23714
  return EXTENDED_CHART_DOORS.has(parse);
23178
23715
  }
@@ -23216,33 +23753,50 @@ var init_chart_type_registry = __esm({
23216
23753
  id: "sequence",
23217
23754
  category: "diagram",
23218
23755
  parse: parseSequenceDgmo,
23219
- measure: measureSequence
23756
+ measure: measureSequence,
23757
+ minDims: minDimsSequence
23220
23758
  },
23221
23759
  {
23222
23760
  id: "flowchart",
23223
23761
  category: "diagram",
23224
23762
  parse: parseFlowchart,
23225
- measure: measureFlowchart
23763
+ measure: measureFlowchart,
23764
+ minDims: minDimsGraph
23226
23765
  },
23227
23766
  {
23228
23767
  id: "class",
23229
23768
  category: "diagram",
23230
23769
  parse: parseClassDiagram,
23231
- 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
23232
23779
  },
23233
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23234
23780
  {
23235
23781
  id: "state",
23236
23782
  category: "diagram",
23237
23783
  parse: parseState,
23238
- 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
23239
23793
  },
23240
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23241
23794
  {
23242
23795
  id: "kanban",
23243
23796
  category: "diagram",
23244
23797
  parse: parseKanban,
23245
- measure: measureKanban
23798
+ measure: measureKanban,
23799
+ minDims: minDimsKanban
23246
23800
  },
23247
23801
  { id: "c4", category: "diagram", parse: parseC4 },
23248
23802
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23250,25 +23804,40 @@ var init_chart_type_registry = __esm({
23250
23804
  id: "infra",
23251
23805
  category: "diagram",
23252
23806
  parse: parseInfra,
23253
- measure: measureInfra
23807
+ measure: measureInfra,
23808
+ minDims: minDimsInfra
23254
23809
  },
23255
23810
  {
23256
23811
  id: "gantt",
23257
23812
  category: "diagram",
23258
23813
  parse: parseGantt,
23259
- 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
23260
23823
  },
23261
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23262
23824
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23263
23825
  {
23264
23826
  id: "mindmap",
23265
23827
  category: "diagram",
23266
23828
  parse: parseMindmap,
23267
- measure: measureMindmap
23829
+ measure: measureMindmap,
23830
+ minDims: minDimsMindmap
23268
23831
  },
23269
23832
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23270
23833
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23271
- { 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
+ },
23272
23841
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23273
23842
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23274
23843
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23290,7 +23859,8 @@ var init_chart_type_registry = __esm({
23290
23859
  id: "heatmap",
23291
23860
  category: "data-chart",
23292
23861
  parse: parseHeatmap,
23293
- measure: measureHeatmap
23862
+ measure: measureHeatmap,
23863
+ minDims: minDimsHeatmap
23294
23864
  },
23295
23865
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23296
23866
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23300,7 +23870,8 @@ var init_chart_type_registry = __esm({
23300
23870
  id: "arc",
23301
23871
  category: "visualization",
23302
23872
  parse: parseArc,
23303
- measure: measureArc
23873
+ measure: measureArc,
23874
+ minDims: minDimsArc
23304
23875
  },
23305
23876
  { id: "timeline", category: "visualization", parse: parseTimeline },
23306
23877
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23310,7 +23881,8 @@ var init_chart_type_registry = __esm({
23310
23881
  id: "tech-radar",
23311
23882
  category: "visualization",
23312
23883
  parse: parseTechRadar,
23313
- measure: measureTechRadar
23884
+ measure: measureTechRadar,
23885
+ minDims: minDimsTechRadar
23314
23886
  },
23315
23887
  { id: "cycle", category: "visualization", parse: parseCycle },
23316
23888
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24457,6 +25029,63 @@ var init_export_container = __esm({
24457
25029
  }
24458
25030
  });
24459
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
+
24460
25089
  // src/org/renderer.ts
24461
25090
  var renderer_exports = {};
24462
25091
  __export(renderer_exports, {
@@ -24475,7 +25104,7 @@ function nodeStroke(palette, nodeColor2) {
24475
25104
  }
24476
25105
  function containerFill(palette, isDark, nodeColor2) {
24477
25106
  if (nodeColor2) {
24478
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25107
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24479
25108
  }
24480
25109
  return mix(palette.surface, palette.bg, 40);
24481
25110
  }
@@ -24615,9 +25244,16 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24615
25244
  }
24616
25245
  }
24617
25246
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
24618
- const clipId = `clip-${c.nodeId}`;
24619
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", sContainerRadius);
24620
- 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
+ });
24621
25257
  }
24622
25258
  if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
24623
25259
  const iconSize = 14;
@@ -24626,10 +25262,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24626
25262
  const iconY = iconPad;
24627
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})`);
24628
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
+ );
24629
25270
  const cx = iconSize / 2;
24630
25271
  const cy = iconSize / 2;
24631
- 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);
24632
- 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);
24633
25274
  }
24634
25275
  }
24635
25276
  for (const edge of layout.edges) {
@@ -24668,42 +25309,48 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24668
25309
  solid
24669
25310
  );
24670
25311
  const stroke2 = nodeStroke(palette, colorOff ? void 0 : node.color);
24671
- 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);
24672
- if (node.isContainer) {
24673
- rect.attr("stroke-dasharray", "6 3");
24674
- }
24675
25312
  const labelColor = contrastText(
24676
25313
  fill2,
24677
25314
  palette.textOnFillLight,
24678
25315
  palette.textOnFillDark
24679
25316
  );
24680
- 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);
24681
25317
  const metaEntries = Object.entries(node.metadata);
24682
- if (metaEntries.length > 0) {
24683
- 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);
24684
- const metaDisplayKeys = metaEntries.map(
24685
- ([k]) => displayNames.get(k) ?? k
24686
- );
24687
- const maxKeyWidth = Math.max(
24688
- ...metaDisplayKeys.map((k) => measureText(`${k}: `, sMetaFontSize))
24689
- );
24690
- const valueX = 10 + maxKeyWidth;
24691
- const metaStartY = sHeaderHeight + sSeparatorGap + sMetaFontSize;
24692
- for (let i = 0; i < metaEntries.length; i++) {
24693
- const [, value] = metaEntries[i];
24694
- const displayKey = metaDisplayKeys[i];
24695
- const rowY = metaStartY + i * sMetaLineHeight;
24696
- nodeG.append("text").attr("x", 10).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(`${displayKey}: `);
24697
- 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
+ }
24698
25341
  }
24699
- }
25342
+ });
24700
25343
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
24701
- const clipId = `clip-${node.id}`;
24702
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", sCardRadius);
24703
- nodeG.append("rect").attr("x", sCollapseBarInset).attr("y", node.height - sCollapseBarHeight).attr("width", node.width - sCollapseBarInset * 2).attr("height", sCollapseBarHeight).attr(
24704
- "fill",
24705
- solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color)
24706
- ).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
+ });
24707
25354
  }
24708
25355
  if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
24709
25356
  const iconSize = 14;
@@ -24712,10 +25359,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24712
25359
  const iconY = iconPad;
24713
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})`);
24714
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;
24715
25363
  const cx = iconSize / 2;
24716
25364
  const cy = iconSize / 2;
24717
- 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);
24718
- 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);
24719
25367
  }
24720
25368
  }
24721
25369
  if (hasAncestorTrail) {
@@ -24847,7 +25495,7 @@ function renderOrgForExport(content, theme, palette) {
24847
25495
  return extractExportSvg(container, theme);
24848
25496
  });
24849
25497
  }
24850
- 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;
24851
25499
  var init_renderer = __esm({
24852
25500
  "src/org/renderer.ts"() {
24853
25501
  "use strict";
@@ -24860,27 +25508,14 @@ var init_renderer = __esm({
24860
25508
  init_layout();
24861
25509
  init_legend_constants();
24862
25510
  init_legend_integration();
25511
+ init_card();
24863
25512
  init_text_measure();
24864
25513
  init_legend_layout();
24865
25514
  init_title_constants();
25515
+ init_visual_conventions();
24866
25516
  DIAGRAM_PADDING = 20;
24867
25517
  MAX_SCALE = 3;
24868
25518
  TITLE_HEIGHT = 30;
24869
- LABEL_FONT_SIZE2 = 13;
24870
- META_FONT_SIZE2 = 11;
24871
- META_LINE_HEIGHT2 = 16;
24872
- HEADER_HEIGHT2 = 28;
24873
- SEPARATOR_GAP2 = 6;
24874
- EDGE_STROKE_WIDTH = 1.5;
24875
- NODE_STROKE_WIDTH = 1.5;
24876
- CARD_RADIUS = 6;
24877
- CONTAINER_RADIUS = 8;
24878
- CONTAINER_LABEL_FONT_SIZE = 13;
24879
- CONTAINER_META_FONT_SIZE = 11;
24880
- CONTAINER_META_LINE_HEIGHT2 = 16;
24881
- CONTAINER_HEADER_HEIGHT = 28;
24882
- COLLAPSE_BAR_HEIGHT = 6;
24883
- COLLAPSE_BAR_INSET = 0;
24884
25519
  ANCESTOR_DOT_R = 4;
24885
25520
  ANCESTOR_LABEL_FONT_SIZE = 11;
24886
25521
  ANCESTOR_ROW_HEIGHT = 22;
@@ -25650,7 +26285,7 @@ function nodeStroke2(_palette, nodeColor2) {
25650
26285
  }
25651
26286
  function containerFill2(palette, isDark, nodeColor2) {
25652
26287
  if (nodeColor2) {
25653
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26288
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25654
26289
  }
25655
26290
  return mix(palette.surface, palette.bg, 40);
25656
26291
  }
@@ -25664,21 +26299,21 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25664
26299
  if (width <= 0 || height <= 0) return;
25665
26300
  const ctx = ScaleContext.identity();
25666
26301
  const sDiagramPadding = ctx.aesthetic(DIAGRAM_PADDING2);
25667
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE4);
25668
- const sMetaFontSize = ctx.text(META_FONT_SIZE4);
25669
- const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT4);
25670
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT4);
25671
- const sSeparatorGap = ctx.structural(SEPARATOR_GAP4);
25672
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH2);
25673
- 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);
25674
26309
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE);
25675
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE3);
25676
- const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE2);
25677
- const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT4);
25678
- 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);
25679
26314
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
25680
26315
  const sTitleHeight = ctx.structural(TITLE_HEIGHT2);
25681
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT2);
26316
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
25682
26317
  const sLegendFixedGap = ctx.aesthetic(LEGEND_FIXED_GAP2);
25683
26318
  const hasLegend = layout.legend.length > 0;
25684
26319
  const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
@@ -25769,7 +26404,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25769
26404
  }
25770
26405
  const fill2 = containerFill2(palette, isDark, c.color);
25771
26406
  const stroke2 = containerStroke2(palette, c.color);
25772
- 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);
25773
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);
25774
26409
  const metaEntries = Object.entries(c.metadata);
25775
26410
  if (metaEntries.length > 0) {
@@ -25794,7 +26429,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25794
26429
  }
25795
26430
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
25796
26431
  const clipId = `clip-${c.nodeId}`;
25797
- 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);
25798
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})`);
25799
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}`);
25800
26435
  }
@@ -25833,13 +26468,23 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25833
26468
  const solid = parsed.options["solid-fill"] === "on";
25834
26469
  const fill2 = nodeFill2(palette, isDark, node.color, solid);
25835
26470
  const stroke2 = nodeStroke2(palette, node.color);
25836
- 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);
25837
26471
  const labelColor = contrastText(
25838
26472
  fill2,
25839
26473
  palette.textOnFillLight,
25840
26474
  palette.textOnFillDark
25841
26475
  );
25842
- 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
+ });
25843
26488
  const metaEntries = Object.entries(node.metadata);
25844
26489
  if (metaEntries.length > 0) {
25845
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);
@@ -25874,7 +26519,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25874
26519
  }
25875
26520
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
25876
26521
  const clipId = `clip-${node.id}`;
25877
- 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);
25878
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})`);
25879
26524
  }
25880
26525
  }
@@ -26004,7 +26649,7 @@ async function renderSitemapForExport(content, theme, palette) {
26004
26649
  document.body.removeChild(container);
26005
26650
  return svgHtml;
26006
26651
  }
26007
- 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;
26008
26653
  var init_renderer2 = __esm({
26009
26654
  "src/sitemap/renderer.ts"() {
26010
26655
  "use strict";
@@ -26017,27 +26662,15 @@ var init_renderer2 = __esm({
26017
26662
  init_legend_integration();
26018
26663
  init_legend_layout();
26019
26664
  init_scaling();
26665
+ init_card();
26020
26666
  init_title_constants();
26667
+ init_visual_conventions();
26021
26668
  DIAGRAM_PADDING2 = 20;
26022
26669
  MAX_SCALE2 = 3;
26023
26670
  TITLE_HEIGHT2 = 30;
26024
- LABEL_FONT_SIZE4 = 13;
26025
- META_FONT_SIZE4 = 11;
26026
- META_LINE_HEIGHT4 = 16;
26027
- HEADER_HEIGHT4 = 28;
26028
- SEPARATOR_GAP4 = 6;
26029
- EDGE_STROKE_WIDTH2 = 1.5;
26030
- NODE_STROKE_WIDTH2 = 1.5;
26031
- CARD_RADIUS2 = 6;
26032
- CONTAINER_RADIUS2 = 8;
26033
- CONTAINER_LABEL_FONT_SIZE3 = 13;
26034
- CONTAINER_META_FONT_SIZE2 = 11;
26035
- CONTAINER_META_LINE_HEIGHT4 = 16;
26036
- CONTAINER_HEADER_HEIGHT2 = 28;
26037
26671
  ARROWHEAD_W = 10;
26038
26672
  ARROWHEAD_H = 7;
26039
26673
  EDGE_LABEL_FONT_SIZE = 11;
26040
- COLLAPSE_BAR_HEIGHT2 = 6;
26041
26674
  LEGEND_FIXED_GAP2 = 8;
26042
26675
  lineGenerator = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveBasis);
26043
26676
  lineGeneratorLinear = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveLinear);
@@ -26205,7 +26838,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
26205
26838
  const sCardMetaLineHeight = ctx.structural(CARD_META_LINE_HEIGHT);
26206
26839
  const sCardSeparatorGap = ctx.structural(CARD_SEPARATOR_GAP);
26207
26840
  const sCardGap = ctx.aesthetic(CARD_GAP);
26208
- const sCardRadius = ctx.structural(CARD_RADIUS3);
26841
+ const sCardRadius = ctx.structural(CARD_RADIUS);
26209
26842
  const sCardPaddingX = ctx.aesthetic(CARD_PADDING_X);
26210
26843
  const sCardPaddingY = ctx.aesthetic(CARD_PADDING_Y);
26211
26844
  const sCardStrokeWidth = ctx.structural(CARD_STROKE_WIDTH);
@@ -26585,7 +27218,7 @@ function computeSwimlaneLayout(parsed, buckets, baseLayout, collapsedLanes, coll
26585
27218
  const totalHeight = laneY - sLaneGap + sColumnPadding + sDiagramPadding;
26586
27219
  return { columnXs, lanes, totalWidth, totalHeight, startY };
26587
27220
  }
26588
- 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) {
26589
27222
  const visibleColumns = parsed.columns.filter((c) => !isArchiveColumn(c.name));
26590
27223
  const buckets = bucketCardsBySwimlane(visibleColumns, swimlaneGroup);
26591
27224
  const grid = computeSwimlaneLayout(
@@ -26725,7 +27358,7 @@ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, is
26725
27358
  }
26726
27359
  }
26727
27360
  }
26728
- 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) {
26729
27362
  const card = cardLayout.card;
26730
27363
  const resolvedColor = resolveCardTagColor(card, tagGroups, activeTagGroup);
26731
27364
  const tagMeta = resolveCardTagMeta(card, tagGroups, hiddenMetaGroups);
@@ -26775,7 +27408,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
26775
27408
  }
26776
27409
  }
26777
27410
  }
26778
- 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;
26779
27412
  var init_renderer3 = __esm({
26780
27413
  "src/kanban/renderer.ts"() {
26781
27414
  "use strict";
@@ -26789,6 +27422,7 @@ var init_renderer3 = __esm({
26789
27422
  init_legend_integration();
26790
27423
  init_scaling();
26791
27424
  init_text_measure();
27425
+ init_visual_conventions();
26792
27426
  init_title_constants();
26793
27427
  DIAGRAM_PADDING3 = 20;
26794
27428
  COLUMN_GAP = 16;
@@ -26799,7 +27433,6 @@ var init_renderer3 = __esm({
26799
27433
  CARD_META_LINE_HEIGHT = 14;
26800
27434
  CARD_SEPARATOR_GAP = 4;
26801
27435
  CARD_GAP = 8;
26802
- CARD_RADIUS3 = 6;
26803
27436
  CARD_PADDING_X = 10;
26804
27437
  CARD_PADDING_Y = 6;
26805
27438
  CARD_STROKE_WIDTH = 1.5;
@@ -27102,8 +27735,8 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
27102
27735
  const sClassFontSize = ctx.text(CLASS_FONT_SIZE2);
27103
27736
  const sMemberFontSize = ctx.text(MEMBER_FONT_SIZE2);
27104
27737
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE2);
27105
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH3);
27106
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH3);
27738
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
27739
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
27107
27740
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT2);
27108
27741
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y2);
27109
27742
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X);
@@ -27362,7 +27995,7 @@ function renderClassDiagramForExport(content, theme, palette) {
27362
27995
  return extractExportSvg(container, theme);
27363
27996
  });
27364
27997
  }
27365
- 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;
27366
27999
  var init_renderer4 = __esm({
27367
28000
  "src/class/renderer.ts"() {
27368
28001
  "use strict";
@@ -27377,13 +28010,12 @@ var init_renderer4 = __esm({
27377
28010
  init_scaling();
27378
28011
  init_text_measure();
27379
28012
  init_note_box();
28013
+ init_visual_conventions();
27380
28014
  DIAGRAM_PADDING4 = 20;
27381
28015
  MAX_SCALE3 = 3;
27382
28016
  CLASS_FONT_SIZE2 = 13;
27383
28017
  MEMBER_FONT_SIZE2 = 11;
27384
28018
  EDGE_LABEL_FONT_SIZE2 = 11;
27385
- EDGE_STROKE_WIDTH3 = 1.5;
27386
- NODE_STROKE_WIDTH3 = 1.5;
27387
28019
  MEMBER_LINE_HEIGHT2 = 18;
27388
28020
  COMPARTMENT_PADDING_Y2 = 8;
27389
28021
  MEMBER_PADDING_X = 10;
@@ -27879,7 +28511,7 @@ function constraintIcon(constraint) {
27879
28511
  return "\u25CB";
27880
28512
  }
27881
28513
  }
27882
- 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) {
27883
28515
  const dx = point.x - prevPoint.x;
27884
28516
  const dy = point.y - prevPoint.y;
27885
28517
  const len = Math.sqrt(dx * dx + dy * dy);
@@ -27939,8 +28571,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
27939
28571
  const sTableFontSize = ctx.text(TABLE_FONT_SIZE2);
27940
28572
  const sColumnFontSize = ctx.text(COLUMN_FONT_SIZE2);
27941
28573
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE4);
27942
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH4);
27943
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH4);
28574
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
28575
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
27944
28576
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT4);
27945
28577
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y4);
27946
28578
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X2);
@@ -28218,7 +28850,7 @@ function renderERDiagramForExport(content, theme, palette) {
28218
28850
  document.body.removeChild(container);
28219
28851
  }
28220
28852
  }
28221
- 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;
28222
28854
  var init_renderer5 = __esm({
28223
28855
  "src/er/renderer.ts"() {
28224
28856
  "use strict";
@@ -28235,13 +28867,12 @@ var init_renderer5 = __esm({
28235
28867
  init_parser9();
28236
28868
  init_layout4();
28237
28869
  init_classify();
28870
+ init_visual_conventions();
28238
28871
  DIAGRAM_PADDING5 = 20;
28239
28872
  MAX_SCALE4 = 3;
28240
28873
  TABLE_FONT_SIZE2 = 13;
28241
28874
  COLUMN_FONT_SIZE2 = 11;
28242
28875
  EDGE_LABEL_FONT_SIZE4 = 11;
28243
- EDGE_STROKE_WIDTH4 = 1.5;
28244
- NODE_STROKE_WIDTH4 = 1.5;
28245
28876
  MEMBER_LINE_HEIGHT4 = 18;
28246
28877
  COMPARTMENT_PADDING_Y4 = 8;
28247
28878
  MEMBER_PADDING_X2 = 10;
@@ -28461,7 +29092,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28461
29092
  onToggleDescriptions,
28462
29093
  onToggleControlsExpand,
28463
29094
  exportMode = false,
28464
- controlsHost
29095
+ controlsHost,
29096
+ rampDomain
28465
29097
  } = options ?? {};
28466
29098
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28467
29099
  const width = exportDims?.width ?? container.clientWidth;
@@ -28471,9 +29103,9 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28471
29103
  const sDiagramPadding = sctx.aesthetic(DIAGRAM_PADDING6);
28472
29104
  const sMinNodeFontSize = sctx.text(MIN_NODE_FONT_SIZE);
28473
29105
  const sEdgeLabelFontSize = sctx.text(EDGE_LABEL_FONT_SIZE5);
28474
- const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH5);
28475
- const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH5);
28476
- 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);
28477
29109
  const sDescFontSize = sctx.text(DESC_FONT_SIZE);
28478
29110
  const sGroupLabelFontSize = sctx.text(GROUP_LABEL_FONT_SIZE);
28479
29111
  const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
@@ -28481,8 +29113,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28481
29113
  const sTitleY = sctx.structural(TITLE_Y);
28482
29114
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28483
29115
  const hasRamp = nodeValues.length > 0;
28484
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28485
- const rampMax = Math.max(...nodeValues);
29116
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29117
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28486
29118
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28487
29119
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28488
29120
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28565,8 +29197,32 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28565
29197
  const scaleX = width / (contentW + sDiagramPadding * 2);
28566
29198
  const scaleY = height / (contentH + sDiagramPadding * 2);
28567
29199
  const scale = Math.min(scaleX, scaleY, 3);
28568
- const offsetX = (width - contentW * scale) / 2;
28569
- 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;
28570
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);
28571
29227
  if (sctx.isBelowFloor) {
28572
29228
  svg.attr("width", "100%");
@@ -28610,7 +29266,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28610
29266
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28611
29267
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28612
29268
  if (group.collapsed) {
28613
- const fillColor = isDark ? palette.surface : palette.bg;
29269
+ const fillColor = themeBaseBg(palette, isDark);
28614
29270
  const strokeColor = palette.border;
28615
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);
28616
29272
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -28702,7 +29358,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28702
29358
  const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
28703
29359
  edgeGroups.set(i, edgeG);
28704
29360
  const markerId = `bl-arrow-${color.replace("#", "")}`;
28705
- const gen = parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
29361
+ const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
28706
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})`);
28707
29363
  if (le.bidirectional) {
28708
29364
  const revId = `bl-arrow-rev-${color.replace("#", "")}`;
@@ -28966,6 +29622,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28966
29622
  });
28967
29623
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
28968
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
+ }
28969
29634
  }
28970
29635
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
28971
29636
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -28983,7 +29648,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
28983
29648
  }
28984
29649
  });
28985
29650
  }
28986
- 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;
28987
29652
  var init_renderer6 = __esm({
28988
29653
  "src/boxes-and-lines/renderer.ts"() {
28989
29654
  "use strict";
@@ -28999,14 +29664,13 @@ var init_renderer6 = __esm({
28999
29664
  init_wrapped_desc();
29000
29665
  init_scaling();
29001
29666
  init_text_measure();
29667
+ init_visual_conventions();
29002
29668
  DIAGRAM_PADDING6 = 20;
29003
29669
  NODE_FONT_SIZE = 11;
29004
29670
  MIN_NODE_FONT_SIZE = 9;
29005
29671
  EDGE_LABEL_FONT_SIZE5 = 11;
29006
- EDGE_STROKE_WIDTH5 = 1.5;
29007
- NODE_STROKE_WIDTH5 = 1.5;
29008
29672
  NODE_RX = 8;
29009
- COLLAPSE_BAR_HEIGHT3 = 4;
29673
+ COLLAPSE_BAR_HEIGHT2 = 4;
29010
29674
  ARROWHEAD_W2 = 5;
29011
29675
  ARROWHEAD_H2 = 4;
29012
29676
  DESC_FONT_SIZE = 10;
@@ -29020,6 +29684,7 @@ var init_renderer6 = __esm({
29020
29684
  VALUE_FONT_SIZE = 11;
29021
29685
  lineGeneratorLR = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29022
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);
29023
29688
  }
29024
29689
  });
29025
29690
 
@@ -29562,7 +30227,7 @@ function shuffle(a, r) {
29562
30227
  return x;
29563
30228
  }
29564
30229
  function flatten(d) {
29565
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30230
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29566
30231
  const pts = [];
29567
30232
  let i = 0, cx = 0, cy = 0, cmd = "";
29568
30233
  const num = () => parseFloat(toks[i++]);
@@ -29606,17 +30271,10 @@ function flatten(d) {
29606
30271
  }
29607
30272
  return pts;
29608
30273
  }
29609
- function segPoint(p1, p2, p3, p4) {
29610
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29611
- if (Math.abs(den) < 1e-9) return null;
29612
- 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;
29613
- 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;
29614
- }
29615
- function countSplineCrossings(layout) {
29616
- const center = /* @__PURE__ */ new Map();
29617
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29618
- for (const g of layout.groups)
29619
- 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;
29620
30278
  const polys = layout.edges.map((e) => {
29621
30279
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29622
30280
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29626,8 +30284,24 @@ function countSplineCrossings(layout) {
29626
30284
  if (p.y < y0) y0 = p.y;
29627
30285
  if (p.y > y1) y1 = p.y;
29628
30286
  }
29629
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30287
+ return { pts, x0, y0, x1, y1 };
29630
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;
29631
30305
  const R = 34;
29632
30306
  let total = 0;
29633
30307
  for (let a = 0; a < polys.length; a++)
@@ -29635,23 +30309,33 @@ function countSplineCrossings(layout) {
29635
30309
  const A = polys[a], B = polys[b];
29636
30310
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29637
30311
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29638
- 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);
29639
30318
  const hits = [];
29640
- for (let i = 1; i < A.pts.length; i++)
29641
- for (let j = 1; j < B.pts.length; j++) {
29642
- const p = segPoint(
29643
- A.pts[i - 1],
29644
- A.pts[i],
29645
- B.pts[j - 1],
29646
- B.pts[j]
29647
- );
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);
29648
30330
  if (!p) continue;
29649
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29650
- 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;
29651
30333
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29652
30334
  hits.push(p);
29653
30335
  }
30336
+ }
29654
30337
  total += hits.length;
30338
+ if (total > floor) return total;
29655
30339
  }
29656
30340
  return total;
29657
30341
  }
@@ -29689,17 +30373,8 @@ function detectEdgeOverlaps(layout, opts) {
29689
30373
  w: g.width,
29690
30374
  h: g.height
29691
30375
  });
29692
- const polys = layout.edges.map((e) => {
29693
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29694
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29695
- for (const p of pts) {
29696
- if (p.x < x0) x0 = p.x;
29697
- if (p.x > x1) x1 = p.x;
29698
- if (p.y < y0) y0 = p.y;
29699
- if (p.y > y1) y1 = p.y;
29700
- }
29701
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29702
- });
30376
+ const polys = flatPolys(layout);
30377
+ const edges = layout.edges;
29703
30378
  const runs = [];
29704
30379
  for (let a = 0; a < polys.length; a++)
29705
30380
  for (let b = a + 1; b < polys.length; b++) {
@@ -29707,7 +30382,12 @@ function detectEdgeOverlaps(layout, opts) {
29707
30382
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29708
30383
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29709
30384
  continue;
29710
- 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);
29711
30391
  let run2 = [];
29712
30392
  let runLen = 0;
29713
30393
  const flush = () => {
@@ -29721,7 +30401,7 @@ function detectEdgeOverlaps(layout, opts) {
29721
30401
  runLen = 0;
29722
30402
  };
29723
30403
  for (const p of A.pts) {
29724
- 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;
29725
30405
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29726
30406
  if (covered) {
29727
30407
  if (run2.length)
@@ -29756,9 +30436,10 @@ function detectEdgeNodePierces(layout, opts) {
29756
30436
  });
29757
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;
29758
30438
  const out = [];
30439
+ const polys = flatPolys(layout);
29759
30440
  layout.edges.forEach((e, idx) => {
29760
30441
  if (e.points.length < 2) return;
29761
- const poly = flatten(splineGen(e.points) ?? "");
30442
+ const poly = polys[idx].pts;
29762
30443
  for (const r of rects) {
29763
30444
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29764
30445
  continue;
@@ -30105,8 +30786,10 @@ function edgeLength(layout) {
30105
30786
  );
30106
30787
  return total;
30107
30788
  }
30108
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30789
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30109
30790
  const hideDescriptions = opts?.hideDescriptions ?? false;
30791
+ const onProgress = opts?.onProgress;
30792
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30110
30793
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30111
30794
  if (collapseInfo) {
30112
30795
  const missing = /* @__PURE__ */ new Set();
@@ -30209,6 +30892,156 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30209
30892
  return { x: rect.x + dx * s, y: rect.y + dy * s };
30210
30893
  };
30211
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
+ }
30212
31045
  function place(cfg) {
30213
31046
  const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
30214
31047
  const ord = (a) => r ? shuffle(a, r) : a.slice();
@@ -30347,6 +31180,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30347
31180
  height: gg.height ?? 600
30348
31181
  };
30349
31182
  }
31183
+ if (allPinned) return placePinned(pinned);
30350
31184
  const n = parsed.nodes.length;
30351
31185
  const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
30352
31186
  const REFINE_K = opts?.refineK ?? 6;
@@ -30369,17 +31203,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30369
31203
  seed: s
30370
31204
  });
30371
31205
  const badness = (lay, floor) => {
30372
- const x = countSplineCrossings(lay);
31206
+ const x = countSplineCrossings(lay, floor);
30373
31207
  if (x > floor) return Infinity;
30374
31208
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30375
31209
  };
30376
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
+ };
30377
31218
  const pool = [];
30378
31219
  for (const cfg of configs) {
30379
31220
  try {
30380
31221
  pool.push(place(cfg));
30381
31222
  } catch {
30382
31223
  }
31224
+ await step("Optimizing layout");
30383
31225
  }
30384
31226
  if (!pool.length)
30385
31227
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30388,9 +31230,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30388
31230
  layered = layeredCandidates(parsed, sizes);
30389
31231
  } catch {
30390
31232
  }
30391
- pool.sort(
30392
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30393
- );
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));
30394
31236
  const refineK = Math.min(REFINE_K, pool.length);
30395
31237
  let best = pool[0];
30396
31238
  let bestObj = Infinity;
@@ -30405,7 +31247,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30405
31247
  best = lay;
30406
31248
  }
30407
31249
  };
30408
- 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
+ }
30409
31254
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30410
31255
  const extra = [];
30411
31256
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30421,9 +31266,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30421
31266
  } catch {
30422
31267
  }
30423
31268
  }
30424
- extra.sort(
30425
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30426
- );
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));
30427
31273
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30428
31274
  }
30429
31275
  for (const lay of layered) {
@@ -30450,7 +31296,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30450
31296
  }
30451
31297
  return best;
30452
31298
  }
30453
- 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;
30454
31300
  var init_layout_search = __esm({
30455
31301
  "src/boxes-and-lines/layout-search.ts"() {
30456
31302
  "use strict";
@@ -30462,6 +31308,8 @@ var init_layout_search = __esm({
30462
31308
  ESCALATE_SEEDS = 18;
30463
31309
  ESCALATE_REFINE = 10;
30464
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();
30465
31313
  GROUP_LABEL_ZONE2 = 32;
30466
31314
  }
30467
31315
  });
@@ -30541,17 +31389,20 @@ function computeNodeSize(node, reserveValueRow) {
30541
31389
  }
30542
31390
  totalRenderedLines = Math.min(totalRenderedLines, MAX_DESC_LINES2);
30543
31391
  const descriptionHeight = totalRenderedLines * DESC_FONT_SIZE2 * DESC_LINE_HEIGHT2;
30544
- 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);
30545
31393
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
30546
31394
  }
30547
31395
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30548
31396
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30549
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31397
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30550
31398
  ...layoutOptions?.hideDescriptions !== void 0 && {
30551
31399
  hideDescriptions: layoutOptions.hideDescriptions
30552
31400
  },
30553
31401
  ...layoutOptions?.previousPositions !== void 0 && {
30554
31402
  previousPositions: layoutOptions.previousPositions
31403
+ },
31404
+ ...layoutOptions?.onProgress !== void 0 && {
31405
+ onProgress: layoutOptions.onProgress
30555
31406
  }
30556
31407
  });
30557
31408
  return attachNotes(
@@ -30670,7 +31521,7 @@ function applyParallelEdgeOffsets(layout) {
30670
31521
  }))
30671
31522
  };
30672
31523
  }
30673
- 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;
30674
31525
  var init_layout5 = __esm({
30675
31526
  "src/boxes-and-lines/layout.ts"() {
30676
31527
  "use strict";
@@ -30686,13 +31537,13 @@ var init_layout5 = __esm({
30686
31537
  DESC_FONT_SIZE2 = 10;
30687
31538
  DESC_LINE_HEIGHT2 = 1.4;
30688
31539
  DESC_PADDING = 8;
30689
- SEPARATOR_GAP5 = 4;
31540
+ SEPARATOR_GAP4 = 4;
30690
31541
  MAX_DESC_LINES2 = 6;
30691
31542
  MAX_LABEL_LINES = 3;
30692
31543
  LABEL_LINE_HEIGHT = 1.3;
30693
31544
  LABEL_PAD = 12;
30694
31545
  VALUE_ROW_FONT = 11;
30695
- 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;
30696
31547
  }
30697
31548
  });
30698
31549
 
@@ -31336,7 +32187,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31336
32187
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31337
32188
  const availWidth = containerWidth;
31338
32189
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31339
- 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
+ );
31340
32196
  let renderLayout = layout;
31341
32197
  if (ctx.factor < 1) {
31342
32198
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31345,17 +32201,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31345
32201
  hiddenCounts.set(n.id, n.hiddenCount);
31346
32202
  }
31347
32203
  }
31348
- renderLayout = layoutMindmap(parsed, palette, {
32204
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31349
32205
  interactive: !isExport,
31350
32206
  ...hiddenCounts.size > 0 && { hiddenCounts },
31351
32207
  activeTagGroup: activeTagGroup ?? null,
31352
32208
  ...hideDescriptions !== void 0 && { hideDescriptions },
31353
- ctx
32209
+ ctx: c
31354
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
+ }
31355
32221
  }
31356
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31357
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31358
- 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
+ );
31359
32235
  if (ctx.isBelowFloor) {
31360
32236
  svg.attr("width", "100%");
31361
32237
  }
@@ -31454,11 +32330,11 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31454
32330
  });
31455
32331
  }
31456
32332
  for (const edge of renderLayout.edges) {
31457
- 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);
31458
32334
  }
31459
32335
  for (const node of renderLayout.nodes) {
31460
32336
  const isRoot = node.radius === 0 && renderLayout.nodes.indexOf(node) === 0;
31461
- const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH6;
32337
+ const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH;
31462
32338
  const effectiveColor = options?.colorByDepth ? depthColor(node.depth, palette) : node.color;
31463
32339
  const fill2 = nodeFill4(
31464
32340
  palette,
@@ -31541,7 +32417,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31541
32417
  const clipId = `collapse-clip-${node.id}`;
31542
32418
  const defs = mainG.append("defs");
31543
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);
31544
- 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})`);
31545
32421
  }
31546
32422
  if (onClickItem) {
31547
32423
  nodeG.style("cursor", "pointer").on("click", (event) => {
@@ -31589,7 +32465,7 @@ function renderMindmapForExport(content, theme, palette) {
31589
32465
  return extractExportSvg(container, theme);
31590
32466
  });
31591
32467
  }
31592
- 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;
31593
32469
  var init_renderer7 = __esm({
31594
32470
  "src/mindmap/renderer.ts"() {
31595
32471
  "use strict";
@@ -31606,6 +32482,7 @@ var init_renderer7 = __esm({
31606
32482
  init_legend_layout();
31607
32483
  init_title_constants();
31608
32484
  init_scaling();
32485
+ init_visual_conventions();
31609
32486
  DIAGRAM_PADDING7 = 20;
31610
32487
  TITLE_HEIGHT4 = 30;
31611
32488
  SINGLE_LABEL_HEIGHT2 = 28;
@@ -31613,9 +32490,6 @@ var init_renderer7 = __esm({
31613
32490
  DESC_LINE_HEIGHT4 = 14;
31614
32491
  NODE_RADIUS = 6;
31615
32492
  ROOT_STROKE_WIDTH = 2.5;
31616
- NODE_STROKE_WIDTH6 = 1.5;
31617
- EDGE_STROKE_WIDTH6 = 1.5;
31618
- COLLAPSE_BAR_HEIGHT4 = 6;
31619
32493
  DEPTH_COLOR_KEYS = [
31620
32494
  "red",
31621
32495
  "orange",
@@ -32844,10 +33718,10 @@ function computeC4NodeDimensions(el, options) {
32844
33718
  const metaEntries = collectCardMetadata(el.metadata);
32845
33719
  if (metaEntries.length > 0) {
32846
33720
  height2 += DIVIDER_GAP;
32847
- height2 += metaEntries.length * META_LINE_HEIGHT5;
33721
+ height2 += metaEntries.length * META_LINE_HEIGHT4;
32848
33722
  const maxMetaWidth = Math.max(
32849
33723
  ...metaEntries.map(
32850
- (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
32851
33725
  )
32852
33726
  );
32853
33727
  if (maxMetaWidth > width) width = Math.min(MAX_NODE_WIDTH, maxMetaWidth);
@@ -34124,7 +34998,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34124
34998
  height: totalHeight
34125
34999
  };
34126
35000
  }
34127
- 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;
34128
35002
  var init_layout8 = __esm({
34129
35003
  "src/c4/layout.ts"() {
34130
35004
  "use strict";
@@ -34142,8 +35016,8 @@ var init_layout8 = __esm({
34142
35016
  DESC_FONT_SIZE4 = 11;
34143
35017
  CARD_V_PAD3 = 14;
34144
35018
  CARD_H_PAD3 = 20;
34145
- META_LINE_HEIGHT5 = 16;
34146
- META_FONT_SIZE5 = 11;
35019
+ META_LINE_HEIGHT4 = 16;
35020
+ META_FONT_SIZE4 = 11;
34147
35021
  MARGIN6 = 40;
34148
35022
  BOUNDARY_PAD = 40;
34149
35023
  GROUP_BOUNDARY_PAD = 24;
@@ -34266,7 +35140,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34266
35140
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34267
35141
  const pathD = lineGenerator4(edge.points);
34268
35142
  if (pathD) {
34269
- 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)");
34270
35144
  if (dashed) {
34271
35145
  pathEl.attr("stroke-dasharray", "6 3");
34272
35146
  }
@@ -34332,7 +35206,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34332
35206
  palette.textOnFillLight,
34333
35207
  palette.textOnFillDark
34334
35208
  );
34335
- 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);
34336
35210
  let yPos = -h / 2 + CARD_V_PAD4;
34337
35211
  const typeLabel = `\xAB${node.type}\xBB`;
34338
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);
@@ -34376,7 +35250,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34376
35250
  }
34377
35251
  if (node.drillable) {
34378
35252
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
34379
- 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);
34380
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");
34381
35255
  }
34382
35256
  }
@@ -34436,14 +35310,14 @@ function drawCylinderCard(nodeG, w, h, fill2, stroke2, dashed) {
34436
35310
  `A ${w / 2} ${ry} 0 0 1 ${-w / 2} ${h / 2 - ry}`,
34437
35311
  "Z"
34438
35312
  ].join(" ");
34439
- 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);
34440
35314
  if (dashed) {
34441
35315
  el.attr("stroke-dasharray", "6 3");
34442
35316
  }
34443
- 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);
34444
35318
  }
34445
35319
  function drawCardRect(nodeG, w, h, fill2, stroke2, dashed) {
34446
- 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);
34447
35321
  if (dashed) {
34448
35322
  el.attr("stroke-dasharray", "6 3");
34449
35323
  }
@@ -34463,7 +35337,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
34463
35337
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34464
35338
  const pathD = lineGenerator4(edge.points);
34465
35339
  if (pathD) {
34466
- 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)");
34467
35341
  if (dashed) {
34468
35342
  pathEl.attr("stroke-dasharray", "6 3");
34469
35343
  }
@@ -34958,13 +35832,13 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
34958
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);
34959
35833
  yPos += DIVIDER_GAP2;
34960
35834
  const maxKeyWidth = Math.max(
34961
- ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE6))
35835
+ ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE2))
34962
35836
  );
34963
35837
  const valueX = -w / 2 + CARD_H_PAD4 + maxKeyWidth;
34964
35838
  for (const entry of metaEntries) {
34965
- 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}:`);
34966
- 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);
34967
- 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;
34968
35842
  }
34969
35843
  }
34970
35844
  } else {
@@ -34991,7 +35865,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
34991
35865
  }
34992
35866
  if (node.drillable) {
34993
35867
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
34994
- 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);
34995
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");
34996
35870
  }
34997
35871
  }
@@ -35120,7 +35994,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
35120
35994
  document.body.removeChild(el);
35121
35995
  }
35122
35996
  }
35123
- 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;
35124
35998
  var init_renderer9 = __esm({
35125
35999
  "src/c4/renderer.ts"() {
35126
36000
  "use strict";
@@ -35134,6 +36008,7 @@ var init_renderer9 = __esm({
35134
36008
  init_legend_constants();
35135
36009
  init_legend_integration();
35136
36010
  init_title_constants();
36011
+ init_visual_conventions();
35137
36012
  DIAGRAM_PADDING8 = 20;
35138
36013
  MAX_SCALE5 = 3;
35139
36014
  TITLE_HEIGHT6 = 30;
@@ -35143,16 +36018,11 @@ var init_renderer9 = __esm({
35143
36018
  DESC_LINE_HEIGHT6 = 16;
35144
36019
  EDGE_LABEL_FONT_SIZE6 = 11;
35145
36020
  TECH_FONT_SIZE = 10;
35146
- EDGE_STROKE_WIDTH7 = 1.5;
35147
- NODE_STROKE_WIDTH7 = 1.5;
35148
- CARD_RADIUS4 = 6;
35149
36021
  CARD_H_PAD4 = 20;
35150
36022
  CARD_V_PAD4 = 14;
35151
36023
  TYPE_LABEL_HEIGHT2 = 18;
35152
36024
  DIVIDER_GAP2 = 6;
35153
36025
  NAME_HEIGHT2 = 20;
35154
- META_FONT_SIZE6 = 11;
35155
- META_LINE_HEIGHT6 = 16;
35156
36026
  BOUNDARY_LABEL_FONT_SIZE = 12;
35157
36027
  BOUNDARY_STROKE_WIDTH = 1.5;
35158
36028
  BOUNDARY_RADIUS = 8;
@@ -35557,7 +36427,7 @@ function nodeFill6(palette, isDark, shape, nodeColor2, isEndTerminal, colorOff,
35557
36427
  function nodeStroke6(palette, shape, nodeColor2, isEndTerminal, colorOff) {
35558
36428
  return nodeColor2 ?? shapeDefaultColor(shape, palette, isEndTerminal, colorOff);
35559
36429
  }
35560
- 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) {
35561
36431
  const w = node.width;
35562
36432
  const h = node.height;
35563
36433
  const rx = h / 2;
@@ -35569,7 +36439,7 @@ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeS
35569
36439
  nodeStroke6(palette, node.shape, node.color, isEnd, colorOff)
35570
36440
  ).attr("stroke-width", sNodeStrokeWidth);
35571
36441
  }
35572
- 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) {
35573
36443
  const w = node.width;
35574
36444
  const h = node.height;
35575
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(
@@ -35588,7 +36458,7 @@ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWid
35588
36458
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35589
36459
  ).attr("stroke-width", sNodeStrokeWidth);
35590
36460
  }
35591
- 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) {
35592
36462
  const w = node.width / 2;
35593
36463
  const h = node.height / 2;
35594
36464
  const points = [`${0},${-h}`, `${w},${0}`, `${0},${h}`, `${-w},${0}`].join(
@@ -35610,7 +36480,7 @@ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35610
36480
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35611
36481
  ).attr("stroke-width", sNodeStrokeWidth);
35612
36482
  }
35613
- 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) {
35614
36484
  const w = node.width / 2;
35615
36485
  const h = node.height / 2;
35616
36486
  const sk = sIoSkew;
@@ -35636,7 +36506,7 @@ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth =
35636
36506
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35637
36507
  ).attr("stroke-width", sNodeStrokeWidth);
35638
36508
  }
35639
- 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) {
35640
36510
  const w = node.width;
35641
36511
  const h = node.height;
35642
36512
  const s = nodeStroke6(palette, node.shape, node.color, void 0, colorOff);
@@ -35654,7 +36524,7 @@ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStroke
35654
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);
35655
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);
35656
36526
  }
35657
- 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) {
35658
36528
  const w = node.width;
35659
36529
  const h = node.height;
35660
36530
  const waveH = sDocWaveHeight;
@@ -35685,7 +36555,7 @@ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35685
36555
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35686
36556
  ).attr("stroke-width", sNodeStrokeWidth);
35687
36557
  }
35688
- 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) {
35689
36559
  switch (node.shape) {
35690
36560
  case "terminal":
35691
36561
  renderTerminal(
@@ -35772,8 +36642,8 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
35772
36642
  const sTitleY = ctx.structural(TITLE_Y);
35773
36643
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
35774
36644
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE7);
35775
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH8);
35776
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH8);
36645
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
36646
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
35777
36647
  const sArrowheadW = ctx.structural(ARROWHEAD_W3);
35778
36648
  const sArrowheadH = ctx.structural(ARROWHEAD_H3);
35779
36649
  const sIoSkew = ctx.structural(IO_SKEW);
@@ -35997,7 +36867,7 @@ function renderFlowchartForExport(content, theme, palette) {
35997
36867
  document.body.removeChild(container);
35998
36868
  }
35999
36869
  }
36000
- 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;
36001
36871
  var init_flowchart_renderer = __esm({
36002
36872
  "src/graph/flowchart-renderer.ts"() {
36003
36873
  "use strict";
@@ -36012,12 +36882,11 @@ var init_flowchart_renderer = __esm({
36012
36882
  init_scaling();
36013
36883
  init_text_measure();
36014
36884
  init_note_box();
36885
+ init_visual_conventions();
36015
36886
  DIAGRAM_PADDING9 = 20;
36016
36887
  MAX_SCALE6 = 3;
36017
36888
  NODE_FONT_SIZE2 = 13;
36018
36889
  EDGE_LABEL_FONT_SIZE7 = 11;
36019
- EDGE_STROKE_WIDTH8 = 1.5;
36020
- NODE_STROKE_WIDTH8 = 1.5;
36021
36890
  ARROWHEAD_W3 = 10;
36022
36891
  ARROWHEAD_H3 = 7;
36023
36892
  IO_SKEW = 15;
@@ -37002,7 +37871,7 @@ function hasRoles(node) {
37002
37871
  }
37003
37872
  function computeNodeWidth2(node, expanded, options) {
37004
37873
  const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
37005
- 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;
37006
37875
  const labelWidth2 = measureText(node.label, NODE_FONT_SIZE3) + badgeWidth + PADDING_X3;
37007
37876
  const allKeys = [];
37008
37877
  if (node.computedRps > 0) allKeys.push("RPS");
@@ -37049,7 +37918,7 @@ function computeNodeWidth2(node, expanded, options) {
37049
37918
  }
37050
37919
  if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
37051
37920
  const keyColWidth = Math.max(
37052
- ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE7))
37921
+ ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE5))
37053
37922
  );
37054
37923
  let maxRowWidth = 0;
37055
37924
  if (node.computedRps > 0) {
@@ -37062,7 +37931,7 @@ function computeNodeWidth2(node, expanded, options) {
37062
37931
  const rpsVal = effectiveCap > 0 && !node.isEdge ? `${formatRpsShort(node.computedRps)} / ${formatRpsShort(effectiveCap)}` : formatRps(node.computedRps);
37063
37932
  maxRowWidth = Math.max(
37064
37933
  maxRowWidth,
37065
- keyColWidth + measureText(rpsVal, META_FONT_SIZE7)
37934
+ keyColWidth + measureText(rpsVal, META_FONT_SIZE5)
37066
37935
  );
37067
37936
  }
37068
37937
  if (expanded) {
@@ -37079,7 +37948,7 @@ function computeNodeWidth2(node, expanded, options) {
37079
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);
37080
37949
  maxRowWidth = Math.max(
37081
37950
  maxRowWidth,
37082
- keyColWidth + measureText(valStr, META_FONT_SIZE7)
37951
+ keyColWidth + measureText(valStr, META_FONT_SIZE5)
37083
37952
  );
37084
37953
  }
37085
37954
  }
@@ -37090,7 +37959,7 @@ function computeNodeWidth2(node, expanded, options) {
37090
37959
  if (ms > 0) {
37091
37960
  maxRowWidth = Math.max(
37092
37961
  maxRowWidth,
37093
- keyColWidth + measureText(formatMs(ms), META_FONT_SIZE7)
37962
+ keyColWidth + measureText(formatMs(ms), META_FONT_SIZE5)
37094
37963
  );
37095
37964
  }
37096
37965
  }
@@ -37101,37 +37970,37 @@ function computeNodeWidth2(node, expanded, options) {
37101
37970
  const combinedVal = `${formatMs(perc.p90)} / ${formatMs(threshold)}`;
37102
37971
  maxRowWidth = Math.max(
37103
37972
  maxRowWidth,
37104
- keyColWidth + measureText(combinedVal, META_FONT_SIZE7)
37973
+ keyColWidth + measureText(combinedVal, META_FONT_SIZE5)
37105
37974
  );
37106
37975
  }
37107
37976
  }
37108
37977
  if (node.computedUptime < 1) {
37109
37978
  maxRowWidth = Math.max(
37110
37979
  maxRowWidth,
37111
- keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE7)
37980
+ keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE5)
37112
37981
  );
37113
37982
  }
37114
37983
  if (node.computedAvailability < 1) {
37115
37984
  maxRowWidth = Math.max(
37116
37985
  maxRowWidth,
37117
- keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE7)
37986
+ keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE5)
37118
37987
  );
37119
37988
  }
37120
37989
  if (node.computedCbState === "open") {
37121
37990
  maxRowWidth = Math.max(
37122
37991
  maxRowWidth,
37123
- measureText("CB: OPEN", META_FONT_SIZE7) + 8
37992
+ measureText("CB: OPEN", META_FONT_SIZE5) + 8
37124
37993
  );
37125
37994
  }
37126
37995
  }
37127
- const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE7;
37996
+ const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE5;
37128
37997
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
37129
37998
  let descWidth = 0;
37130
37999
  for (const dl of descLines) {
37131
- const truncated = truncateText(dl, META_FONT_SIZE7, DESC_MAX_WIDTH2);
38000
+ const truncated = truncateText(dl, META_FONT_SIZE5, DESC_MAX_WIDTH2);
37132
38001
  descWidth = Math.max(
37133
38002
  descWidth,
37134
- measureText(truncated, META_FONT_SIZE7) + PADDING_X3
38003
+ measureText(truncated, META_FONT_SIZE5) + PADDING_X3
37135
38004
  );
37136
38005
  }
37137
38006
  return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
@@ -37141,17 +38010,17 @@ function computeNodeHeight2(node, expanded, options) {
37141
38010
  const computedCount = countComputedRows(node, expanded);
37142
38011
  const hasRps = node.computedRps > 0;
37143
38012
  const descLineCount = expanded && node.description && !node.isEdge ? node.description.length : 0;
37144
- const descH = descLineCount * META_LINE_HEIGHT7;
38013
+ const descH = descLineCount * META_LINE_HEIGHT5;
37145
38014
  if (propCount === 0 && computedCount === 0 && !hasRps)
37146
38015
  return NODE_HEADER_HEIGHT + descH + NODE_PAD_BOTTOM;
37147
38016
  let h = NODE_HEADER_HEIGHT + descH + NODE_SEPARATOR_GAP;
37148
38017
  const computedSectionCount = (hasRps ? 1 : 0) + computedCount;
37149
- h += computedSectionCount * META_LINE_HEIGHT7;
38018
+ h += computedSectionCount * META_LINE_HEIGHT5;
37150
38019
  if (computedSectionCount > 0 && propCount > 0) h += NODE_SEPARATOR_GAP;
37151
- h += propCount * META_LINE_HEIGHT7;
38020
+ h += propCount * META_LINE_HEIGHT5;
37152
38021
  if (hasRoles(node)) h += ROLE_DOT_ROW;
37153
38022
  h += NODE_PAD_BOTTOM;
37154
- if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT5;
38023
+ if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT3;
37155
38024
  return h;
37156
38025
  }
37157
38026
  function formatRps(rps) {
@@ -37486,20 +38355,20 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
37486
38355
  height: totalHeight
37487
38356
  };
37488
38357
  }
37489
- 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;
37490
38359
  var init_layout10 = __esm({
37491
38360
  "src/infra/layout.ts"() {
37492
38361
  "use strict";
37493
38362
  init_text_measure();
37494
38363
  MIN_NODE_WIDTH2 = 140;
37495
38364
  NODE_HEADER_HEIGHT = 28;
37496
- META_LINE_HEIGHT7 = 14;
38365
+ META_LINE_HEIGHT5 = 14;
37497
38366
  NODE_SEPARATOR_GAP = 4;
37498
38367
  NODE_PAD_BOTTOM = 10;
37499
38368
  ROLE_DOT_ROW = 12;
37500
- COLLAPSE_BAR_HEIGHT5 = 6;
38369
+ COLLAPSE_BAR_HEIGHT3 = 6;
37501
38370
  NODE_FONT_SIZE3 = 13;
37502
- META_FONT_SIZE7 = 10;
38371
+ META_FONT_SIZE5 = 10;
37503
38372
  EDGE_LABEL_FONT_SIZE8 = 11;
37504
38373
  PADDING_X3 = 24;
37505
38374
  GROUP_PADDING2 = 20;
@@ -37605,19 +38474,19 @@ import * as d3Shape8 from "d3-shape";
37605
38474
  function buildScaledConstants(ctx) {
37606
38475
  return {
37607
38476
  sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
37608
- sMetaFontSize: ctx.text(META_FONT_SIZE8),
37609
- sMetaLineHeight: ctx.structural(META_LINE_HEIGHT8),
38477
+ sMetaFontSize: ctx.text(META_FONT_SIZE6),
38478
+ sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
37610
38479
  sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE9),
37611
38480
  sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
37612
38481
  sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
37613
- sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH9),
37614
- sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH9),
38482
+ sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
38483
+ sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH),
37615
38484
  sOverloadStrokeWidth: ctx.structural(OVERLOAD_STROKE_WIDTH),
37616
38485
  sRoleDotRadius: ctx.structural(ROLE_DOT_RADIUS),
37617
38486
  sNodeSeparatorGap: ctx.structural(NODE_SEPARATOR_GAP2),
37618
38487
  sNodePadBottom: ctx.structural(NODE_PAD_BOTTOM2),
37619
- sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT6),
37620
- sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET2),
38488
+ sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT),
38489
+ sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET),
37621
38490
  sParticleR: ctx.structural(PARTICLE_R),
37622
38491
  sRejectParticleR: ctx.structural(PARTICLE_R),
37623
38492
  sRejectDropDistance: ctx.structural(REJECT_DROP_DISTANCE),
@@ -37941,7 +38810,7 @@ function isWarning(node) {
37941
38810
  return cap > 0 && node.computedRps / cap > 0.7;
37942
38811
  }
37943
38812
  function truncateDesc(text) {
37944
- return truncateText(text, META_FONT_SIZE8, DESC_MAX_WIDTH);
38813
+ return truncateText(text, META_FONT_SIZE6, DESC_MAX_WIDTH);
37945
38814
  }
37946
38815
  function sloLatencyColor(p90, slo) {
37947
38816
  const t = slo.latencyP90 ?? 0;
@@ -38420,7 +39289,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38420
39289
  if (!isNodeCollapsed) {
38421
39290
  const expanded = expandedNodeIds?.has(node.id) ?? false;
38422
39291
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
38423
- const descH = descLines.length * META_LINE_HEIGHT8;
39292
+ const descH = descLines.length * META_LINE_HEIGHT6;
38424
39293
  for (let di = 0; di < descLines.length; di++) {
38425
39294
  const rawLine = descLines[di];
38426
39295
  const processed = preprocessDescriptionLine(rawLine);
@@ -38428,7 +39297,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38428
39297
  const isTruncated = descTruncated !== processed;
38429
39298
  const textEl = g.append("text").attr("x", node.x).attr(
38430
39299
  "y",
38431
- 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
38432
39301
  ).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sMetaFontSize).attr("fill", textFill);
38433
39302
  renderInlineText(textEl, descTruncated, palette, sc.sMetaFontSize);
38434
39303
  if (isTruncated) textEl.append("title").text(rawLine);
@@ -38946,7 +39815,7 @@ function parseAndLayoutInfra(content) {
38946
39815
  const layout = layoutInfra(computed);
38947
39816
  return { parsed, computed, layout };
38948
39817
  }
38949
- 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;
38950
39819
  var init_renderer10 = __esm({
38951
39820
  "src/infra/renderer.ts"() {
38952
39821
  "use strict";
@@ -38965,21 +39834,18 @@ var init_renderer10 = __esm({
38965
39834
  init_legend_layout();
38966
39835
  init_title_constants();
38967
39836
  init_scaling();
39837
+ init_visual_conventions();
38968
39838
  NODE_FONT_SIZE4 = 13;
38969
- META_FONT_SIZE8 = 10;
38970
- META_LINE_HEIGHT8 = 14;
39839
+ META_FONT_SIZE6 = 10;
39840
+ META_LINE_HEIGHT6 = 14;
38971
39841
  EDGE_LABEL_FONT_SIZE9 = 11;
38972
39842
  GROUP_LABEL_FONT_SIZE2 = 14;
38973
39843
  NODE_BORDER_RADIUS = 8;
38974
- EDGE_STROKE_WIDTH9 = 1.5;
38975
- NODE_STROKE_WIDTH9 = 1.5;
38976
39844
  OVERLOAD_STROKE_WIDTH = 3;
38977
39845
  ROLE_DOT_RADIUS = 3;
38978
39846
  NODE_HEADER_HEIGHT2 = 28;
38979
39847
  NODE_SEPARATOR_GAP2 = 4;
38980
39848
  NODE_PAD_BOTTOM2 = 10;
38981
- COLLAPSE_BAR_HEIGHT6 = 6;
38982
- COLLAPSE_BAR_INSET2 = 0;
38983
39849
  LEGEND_FIXED_GAP3 = 16;
38984
39850
  SPEED_BADGE_H_PAD = 5;
38985
39851
  SPEED_BADGE_V_PAD = 3;
@@ -39018,7 +39884,7 @@ var init_renderer10 = __esm({
39018
39884
  partitions: "partitions"
39019
39885
  };
39020
39886
  DESC_MAX_CHARS = 120;
39021
- 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;
39022
39888
  RPS_FORMAT_KEYS = /* @__PURE__ */ new Set(["max-rps", "ratelimit-rps"]);
39023
39889
  MS_FORMAT_KEYS = /* @__PURE__ */ new Set([
39024
39890
  "latency-ms",
@@ -40709,7 +41575,7 @@ __export(renderer_exports11, {
40709
41575
  import * as d3Selection17 from "d3-selection";
40710
41576
  import * as d3Shape9 from "d3-shape";
40711
41577
  function analysisBlockChrome(palette, isDark) {
40712
- const surfaceBg = isDark ? palette.surface : palette.bg;
41578
+ const surfaceBg = themeBaseBg(palette, isDark);
40713
41579
  return {
40714
41580
  fill: mix(palette.surface, palette.bg, 40),
40715
41581
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -40807,18 +41673,18 @@ function renderPert(container, resolved, layout, palette, isDark, options = {})
40807
41673
  const sLegendPillHeight = ctx.structural(LEGEND_PILL_HEIGHT);
40808
41674
  const sLegendBlockHeight = showTagLegend ? sLegendTopGap + sLegendPillHeight + sLegendBottomGap : 0;
40809
41675
  const sNodeRadius = ctx.structural(NODE_RADIUS2);
40810
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH10);
41676
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
40811
41677
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE5);
40812
41678
  const sNodeCellFontSize = ctx.text(NODE_CELL_FONT_SIZE2);
40813
41679
  const sNodeTopRowHeight = ctx.structural(NODE_TOP_ROW_HEIGHT);
40814
41680
  const sNodeBottomRowHeight = ctx.structural(NODE_BOTTOM_ROW_HEIGHT);
40815
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH10);
41681
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
40816
41682
  const sArrowheadW = ctx.structural(ARROWHEAD_W4);
40817
41683
  const sArrowheadH = ctx.structural(ARROWHEAD_H4);
40818
- const sContainerRadius = ctx.structural(CONTAINER_RADIUS3);
40819
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE4);
40820
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT3);
40821
- 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);
40822
41688
  const sPinIconW = ctx.structural(PIN_ICON_W);
40823
41689
  const sPinIconH = ctx.structural(PIN_ICON_H);
40824
41690
  const scaledWidth = layout.width + sDiagramPad * 2;
@@ -41385,7 +42251,7 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41385
42251
  palette.textOnFillDark
41386
42252
  );
41387
42253
  const sNR = sc.nodeRadius ?? NODE_RADIUS2;
41388
- const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT7;
42254
+ const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT;
41389
42255
  drawTextbookCard(g, {
41390
42256
  width: grp.width,
41391
42257
  height: grp.height,
@@ -41419,10 +42285,10 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41419
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})`);
41420
42286
  continue;
41421
42287
  }
41422
- const sCR = sc.containerRadius ?? CONTAINER_RADIUS3;
41423
- const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE4;
41424
- const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT3;
41425
- 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;
41426
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);
41427
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);
41428
42294
  }
@@ -41475,7 +42341,7 @@ function renderEdges2(root, resolved, layout, palette, collapsedSet, sc = {}) {
41475
42341
  }
41476
42342
  const path = lineGenerator6(e.points);
41477
42343
  if (!path) continue;
41478
- const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH10;
42344
+ const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH;
41479
42345
  const sELFS = sc.edgeLabelFontSize ?? 10;
41480
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 ?? "");
41481
42347
  const parsedEdge = edgeByKey.get(`${e.source}->${e.target}`);
@@ -41657,7 +42523,7 @@ function computeDurationEmphasis(activities) {
41657
42523
  function drawTextbookCard(g, a) {
41658
42524
  const { width: w, height: h, x, y } = a;
41659
42525
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41660
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42526
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41661
42527
  const sTRH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41662
42528
  const sBRH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41663
42529
  const sNFS = a.sNodeFontSize ?? NODE_FONT_SIZE5;
@@ -41756,7 +42622,7 @@ function drawTextbookCard(g, a) {
41756
42622
  function drawMilestonePill(g, a) {
41757
42623
  const { width: w, height: h, x, y } = a;
41758
42624
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41759
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42625
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41760
42626
  const topRowH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41761
42627
  const botRowH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41762
42628
  const sNCFS = a.sNodeCellFontSize ?? NODE_CELL_FONT_SIZE2;
@@ -41889,7 +42755,7 @@ function renderCaptionBlock(svg, bullets, args) {
41889
42755
  palette.textOnFillDark
41890
42756
  );
41891
42757
  const block = svg.append("g").attr("class", "pert-caption-block").attr("data-pert-caption", "");
41892
- 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);
41893
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");
41894
42760
  const textX = x + CAPTION_BOX_PADDING_X;
41895
42761
  const firstBaselineY = y + CAPTION_BOX_PADDING_Y + CAPTION_HEADER_BAND_HEIGHT + CAPTION_FONT_SIZE;
@@ -42006,7 +42872,7 @@ function renderTornadoBlock(svg, rows, args) {
42006
42872
  palette.textOnFillDark
42007
42873
  );
42008
42874
  const block = svg.append("g").attr("class", "pert-tornado-block").attr("data-pert-tornado", "");
42009
- 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);
42010
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");
42011
42877
  const fmt = (v) => {
42012
42878
  const r = Math.round(v * 100) / 100;
@@ -42162,7 +43028,7 @@ function renderScurveBlock(svg, data, args) {
42162
43028
  palette.textOnFillDark
42163
43029
  );
42164
43030
  const block = svg.append("g").attr("class", "pert-scurve-block").attr("data-pert-scurve", "");
42165
- 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);
42166
43032
  const hasTitle = typeof title === "string" && title.length > 0;
42167
43033
  if (hasTitle) {
42168
43034
  const titleText = title.replace(/\.$/, "");
@@ -42346,7 +43212,7 @@ function formatScurveDate(iso) {
42346
43212
  if (month < 0 || month > 11 || isNaN(day)) return iso;
42347
43213
  return `${SCURVE_MONTH_NAMES[month]} ${day}`;
42348
43214
  }
42349
- 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;
42350
43216
  var init_renderer11 = __esm({
42351
43217
  "src/pert/renderer.ts"() {
42352
43218
  "use strict";
@@ -42362,20 +43228,15 @@ var init_renderer11 = __esm({
42362
43228
  init_analyzer();
42363
43229
  init_layout11();
42364
43230
  init_internal();
43231
+ init_visual_conventions();
42365
43232
  DIAGRAM_PADDING11 = 20;
42366
43233
  NODE_FONT_SIZE5 = 13;
42367
43234
  NODE_CELL_FONT_SIZE2 = 11;
42368
43235
  NODE_RADIUS2 = 6;
42369
- NODE_STROKE_WIDTH10 = 1.5;
42370
43236
  NODE_TOP_ROW_HEIGHT = 26;
42371
43237
  NODE_BOTTOM_ROW_HEIGHT = 26;
42372
- EDGE_STROKE_WIDTH10 = 1.5;
42373
43238
  ARROWHEAD_W4 = 10;
42374
43239
  ARROWHEAD_H4 = 7;
42375
- CONTAINER_RADIUS3 = 8;
42376
- CONTAINER_LABEL_FONT_SIZE4 = 13;
42377
- CONTAINER_HEADER_HEIGHT3 = 28;
42378
- COLLAPSE_BAR_HEIGHT7 = 6;
42379
43240
  DURATION_FADE_OPACITY = 0.55;
42380
43241
  PIN_ICON_W = 13;
42381
43242
  PIN_ICON_H = 13;
@@ -45367,8 +46228,8 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45367
46228
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
45368
46229
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE10);
45369
46230
  const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
45370
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH11);
45371
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH11);
46231
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
46232
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
45372
46233
  const sArrowheadW = ctx.structural(ARROWHEAD_W5);
45373
46234
  const sArrowheadH = ctx.structural(ARROWHEAD_H5);
45374
46235
  const sPseudostateRadius = ctx.structural(PSEUDOSTATE_RADIUS);
@@ -45425,7 +46286,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45425
46286
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45426
46287
  const gw = group.width + sGroupExtraPadding * 2;
45427
46288
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45428
- 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);
45429
46290
  const strokeColor = group.color ?? palette.textMuted;
45430
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");
45431
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");
@@ -45645,7 +46506,7 @@ function renderStateForExport(content, theme, palette) {
45645
46506
  document.body.removeChild(container);
45646
46507
  }
45647
46508
  }
45648
- 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;
45649
46510
  var init_state_renderer = __esm({
45650
46511
  "src/graph/state-renderer.ts"() {
45651
46512
  "use strict";
@@ -45660,13 +46521,12 @@ var init_state_renderer = __esm({
45660
46521
  init_scaling();
45661
46522
  init_text_measure();
45662
46523
  init_note_box();
46524
+ init_visual_conventions();
45663
46525
  DIAGRAM_PADDING12 = 20;
45664
46526
  MAX_SCALE7 = 3;
45665
46527
  NODE_FONT_SIZE6 = 13;
45666
46528
  EDGE_LABEL_FONT_SIZE10 = 11;
45667
46529
  GROUP_LABEL_FONT_SIZE3 = 11;
45668
- EDGE_STROKE_WIDTH11 = 1.5;
45669
- NODE_STROKE_WIDTH11 = 1.5;
45670
46530
  ARROWHEAD_W5 = 10;
45671
46531
  ARROWHEAD_H5 = 7;
45672
46532
  PSEUDOSTATE_RADIUS = 10;
@@ -47381,14 +48241,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47381
48241
  const panelY = PADDING2;
47382
48242
  const textX = panelX + CARD_PADDING_X3;
47383
48243
  const clipId = "persona-clip";
47384
- 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);
47385
48245
  const personaFill = shapeFill(palette, personaColor, isDark, { solid });
47386
48246
  const onPersonaText = contrastText(
47387
48247
  personaFill,
47388
48248
  palette.textOnFillLight,
47389
48249
  palette.textOnFillDark
47390
48250
  );
47391
- 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);
47392
48252
  if (descLines.length > 0) {
47393
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);
47394
48254
  }
@@ -47396,7 +48256,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47396
48256
  const silY = panelY + panelHeight / 2 - 6;
47397
48257
  const silClip = personaG.append("g").attr("clip-path", `url(#${clipId})`);
47398
48258
  renderPersonaSilhouette(silClip, silX, silY, personaColor, palette, 1.2);
47399
- 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);
47400
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);
47401
48261
  for (let li = 0; li < descLines.length; li++) {
47402
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);
@@ -47636,7 +48496,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47636
48496
  { solid }
47637
48497
  );
47638
48498
  const rowStroke = stepColor ?? palette.textMuted;
47639
- 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);
47640
48500
  const faceCx = listX + CARD_PADDING_X3 + COLLAPSED_FACE_R;
47641
48501
  const faceCy = itemY + COLLAPSED_CARD_H / 2;
47642
48502
  if (step.score !== void 0) {
@@ -47799,7 +48659,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47799
48659
  const scoreColor = scoreToColor(score, palette);
47800
48660
  const tintedBg = mix(scoreColor, palette.surface, 20);
47801
48661
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
47802
- 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);
47803
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);
47804
48664
  const centerX = bx + bw / 2;
47805
48665
  for (let i = 0; i < lines.length; i++) {
@@ -47956,7 +48816,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
47956
48816
  palette.textOnFillLight,
47957
48817
  palette.textOnFillDark
47958
48818
  );
47959
- 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);
47960
48820
  const titleMaxW = sl.width - CARD_PADDING_X3 * 2;
47961
48821
  const titleLines = wrapTextToWidth(sl.step.title, FONT_SIZE_STEP, titleMaxW);
47962
48822
  for (let i = 0; i < titleLines.length; i++) {
@@ -48015,7 +48875,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
48015
48875
  const stripFill = shapeFill(palette, stripColor, isDark, {
48016
48876
  ...solid !== void 0 && { solid }
48017
48877
  });
48018
- 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);
48019
48879
  const stripTextColor = contrastText(
48020
48880
  stripFill,
48021
48881
  palette.textOnFillLight,
@@ -48131,7 +48991,7 @@ function renderJourneyMapForExport(content, theme, palette) {
48131
48991
  }
48132
48992
  return svgEl.outerHTML;
48133
48993
  }
48134
- 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;
48135
48995
  var init_renderer14 = __esm({
48136
48996
  "src/journey-map/renderer.ts"() {
48137
48997
  "use strict";
@@ -48144,9 +49004,9 @@ var init_renderer14 = __esm({
48144
49004
  init_tag_groups();
48145
49005
  init_scaling();
48146
49006
  init_text_measure();
49007
+ init_visual_conventions();
48147
49008
  DIAGRAM_PADDING13 = 20;
48148
49009
  PADDING2 = DIAGRAM_PADDING13;
48149
- CARD_RADIUS5 = 6;
48150
49010
  CARD_PADDING_X3 = 10;
48151
49011
  CARD_PADDING_Y3 = 6;
48152
49012
  CARD_HEADER_HEIGHT3 = 24;
@@ -48223,7 +49083,7 @@ function computeCycleLayout(parsed, options) {
48223
49083
  const hasDesc = !hideDescriptions && node.description.length > 0;
48224
49084
  const labelWidth2 = Math.max(
48225
49085
  MIN_NODE_WIDTH4,
48226
- measureText(node.label, LABEL_FONT_SIZE5) + NODE_PAD_X * 2
49086
+ measureText(node.label, LABEL_FONT_SIZE4) + NODE_PAD_X * 2
48227
49087
  );
48228
49088
  if (circleNodes) {
48229
49089
  return computeCircleNodeDims(node, hasDesc);
@@ -48440,7 +49300,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48440
49300
  let bestScore = Infinity;
48441
49301
  for (let w = minW; w <= MAX_NODE_WIDTH3; w += DESC_WIDTH_STEP) {
48442
49302
  const wrapped2 = wrapDescForWidth(description, w);
48443
- 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;
48444
49304
  const ratio = w / h;
48445
49305
  const score = Math.abs(Math.log(ratio / DESC_TARGET_RATIO));
48446
49306
  if (score < bestScore) {
@@ -48452,7 +49312,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48452
49312
  const wrapped = wrapDescForWidth(description, minW);
48453
49313
  return {
48454
49314
  width: minW,
48455
- height: HEADER_HEIGHT5 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
49315
+ height: HEADER_HEIGHT4 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
48456
49316
  wrappedDesc: wrapped
48457
49317
  };
48458
49318
  }
@@ -48465,7 +49325,7 @@ function wrapDescForWidth(description, nodeWidth) {
48465
49325
  );
48466
49326
  }
48467
49327
  function renderedDescNodeHeight(numLines, scale) {
48468
- const headerH = HEADER_HEIGHT5 * scale;
49328
+ const headerH = HEADER_HEIGHT4 * scale;
48469
49329
  const descFont = Math.max(
48470
49330
  RENDERER_DESC_FONT_MIN,
48471
49331
  Math.round(RENDERER_DESC_FONT * scale)
@@ -48800,7 +49660,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
48800
49660
  const midAngle = srcExitAngle + dir * visibleSweep / 2;
48801
49661
  return { path, midAngle };
48802
49662
  }
48803
- 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;
48804
49664
  var init_layout14 = __esm({
48805
49665
  "src/cycle/layout.ts"() {
48806
49666
  "use strict";
@@ -48808,7 +49668,7 @@ var init_layout14 = __esm({
48808
49668
  init_wrapped_desc();
48809
49669
  init_text_measure();
48810
49670
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
48811
- LABEL_FONT_SIZE5 = 13;
49671
+ LABEL_FONT_SIZE4 = 13;
48812
49672
  CIRCLE_LABEL_FONT_SIZE = 16;
48813
49673
  DESC_FONT_SIZE6 = 11;
48814
49674
  EDGE_LABEL_FONT_SIZE11 = 11;
@@ -48818,7 +49678,7 @@ var init_layout14 = __esm({
48818
49678
  DESC_WIDTH_STEP = 20;
48819
49679
  DESC_TARGET_RATIO = 1.6;
48820
49680
  PLAIN_NODE_HEIGHT = 50;
48821
- HEADER_HEIGHT5 = 36;
49681
+ HEADER_HEIGHT4 = 36;
48822
49682
  DESC_LINE_HEIGHT7 = 16;
48823
49683
  DESC_PAD_Y = 14;
48824
49684
  NODE_PAD_X = 20;
@@ -49398,6 +50258,16 @@ __export(resolver_exports, {
49398
50258
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49399
50259
  resolveMap: () => resolveMap
49400
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
+ }
49401
50271
  function bboxArea(b) {
49402
50272
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49403
50273
  }
@@ -49894,7 +50764,7 @@ function resolveMap(parsed, data) {
49894
50764
  const containerUnion = unionExtent(containerBoxes, points);
49895
50765
  if (containerUnion)
49896
50766
  extent3 = pad(
49897
- clampContainerToCluster(containerUnion, points),
50767
+ clampContainerToCluster(containerUnion, points, usOriented),
49898
50768
  PAD_FRACTION
49899
50769
  );
49900
50770
  }
@@ -49978,17 +50848,21 @@ function mostCommonCountry(regions, poiCountries) {
49978
50848
  }
49979
50849
  return best;
49980
50850
  }
49981
- function clampContainerToCluster(container, points) {
50851
+ function clampContainerToCluster(container, points, usOriented) {
49982
50852
  const poi = unionExtent([], points);
49983
50853
  if (!poi) return container;
49984
50854
  let [[west, south], [east, north]] = container;
49985
50855
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
49986
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
49987
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
49988
- if (east <= 180 && pEast <= 180) {
49989
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
49990
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
49991
- }
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;
49992
50866
  return [
49993
50867
  [west, south],
49994
50868
  [east, north]
@@ -50006,7 +50880,7 @@ function firstError(diags) {
50006
50880
  const e = diags.find((d) => d.severity === "error");
50007
50881
  return e ? formatDgmoError(e) : null;
50008
50882
  }
50009
- 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;
50010
50884
  var init_resolver2 = __esm({
50011
50885
  "src/map/resolver.ts"() {
50012
50886
  "use strict";
@@ -50019,7 +50893,9 @@ var init_resolver2 = __esm({
50019
50893
  WORLD_LAT_SOUTH = -58;
50020
50894
  WORLD_LAT_NORTH = 78;
50021
50895
  POI_ZOOM_FLOOR_DEG = 7;
50022
- CONTAINER_OVERSHOOT_DEG = 8;
50896
+ CONTAINER_OVERSHOOT_MAX = 8;
50897
+ CONTAINER_OVERSHOOT_MIN = 3;
50898
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50023
50899
  US_NATIONAL_LON_SPAN = 48;
50024
50900
  CONUS_BBOX = [
50025
50901
  [-125, 25],
@@ -50185,12 +51061,12 @@ function tierBand(maxSpanDeg) {
50185
51061
  }
50186
51062
  function labelBudget(width, height, band) {
50187
51063
  const bandCap = {
50188
- world: 7,
50189
- continental: 6,
50190
- regional: 5,
50191
- local: 4
51064
+ world: 10,
51065
+ continental: 9,
51066
+ regional: 7,
51067
+ local: 6
50192
51068
  };
50193
- 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);
50194
51070
  return Math.max(0, Math.min(area2, bandCap[band]));
50195
51071
  }
50196
51072
  function waterEligible(tier, kind, band) {
@@ -50244,6 +51120,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50244
51120
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50245
51121
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50246
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
+ }
50247
51128
  function rectFits(r, width, height) {
50248
51129
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50249
51130
  }
@@ -50262,6 +51143,7 @@ function placeContextLabels(args) {
50262
51143
  palette,
50263
51144
  project,
50264
51145
  collides,
51146
+ contentPoints,
50265
51147
  overLand
50266
51148
  } = args;
50267
51149
  void projection;
@@ -50332,8 +51214,17 @@ function placeContextLabels(args) {
50332
51214
  const [x0, y0, x1, y1] = c.bbox;
50333
51215
  const w = x1 - x0;
50334
51216
  const h = y1 - y0;
50335
- return { c, w, h, area: w * h };
50336
- }).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
+ );
50337
51228
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50338
51229
  let ci = 0;
50339
51230
  for (const r of ranked) {
@@ -50350,7 +51241,7 @@ function placeContextLabels(args) {
50350
51241
  );
50351
51242
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50352
51243
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50353
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51244
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50354
51245
  const text = c.name;
50355
51246
  const tw = labelWidth(text, 0, fontSize);
50356
51247
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50363,6 +51254,9 @@ function placeContextLabels(args) {
50363
51254
  letterSpacing: 0,
50364
51255
  color,
50365
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 } : {},
50366
51260
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50367
51261
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50368
51262
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50377,39 +51271,53 @@ function placeContextLabels(args) {
50377
51271
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50378
51272
  const waterCap = budget - Math.min(2, countryCount);
50379
51273
  let waterPlaced = 0;
50380
- for (const cand of candidates) {
50381
- if (placed.length >= budget) break;
50382
- if (cand.italic && waterPlaced >= waterCap) continue;
51274
+ const gateAt = (cx, cy, cand) => {
50383
51275
  const rect = rectAround(
50384
- cand.cx,
50385
- cand.cy,
51276
+ cx,
51277
+ cy,
50386
51278
  cand.lines,
50387
51279
  cand.letterSpacing,
50388
51280
  cand.fontSize
50389
51281
  );
50390
- if (!rectFits(rect, width, height)) continue;
51282
+ if (!rectFits(rect, width, height)) return null;
50391
51283
  if (cand.italic && overLand) {
50392
51284
  const inset = 2;
50393
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51285
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50394
51286
  const touchesLand = cand.lines.some((line11, li) => {
50395
51287
  const lw = labelWidth(line11, cand.letterSpacing);
50396
- const x0 = cand.cx - lw / 2 + inset;
50397
- const x1 = cand.cx + lw / 2 - inset;
50398
- 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];
50399
51291
  const base = top + li * LINE_HEIGHT;
50400
51292
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50401
51293
  (y) => xs.some((x) => overLand(x, y))
50402
51294
  );
50403
51295
  });
50404
- if (touchesLand) continue;
51296
+ if (touchesLand) return null;
51297
+ }
51298
+ if (collides(rect)) return null;
51299
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51300
+ return null;
51301
+ return rect;
51302
+ };
51303
+ for (const cand of candidates) {
51304
+ if (placed.length >= budget) break;
51305
+ if (cand.italic && waterPlaced >= waterCap) continue;
51306
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51307
+ let chosen = null;
51308
+ for (const [px, py] of positions) {
51309
+ const rect = gateAt(px, py, cand);
51310
+ if (rect) {
51311
+ chosen = { x: px, y: py, rect };
51312
+ break;
51313
+ }
50405
51314
  }
50406
- if (collides(rect)) continue;
50407
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50408
- placedRects.push(rect);
51315
+ if (!chosen) continue;
51316
+ placedRects.push(chosen.rect);
50409
51317
  if (cand.italic) waterPlaced++;
50410
51318
  placed.push({
50411
- x: cand.cx,
50412
- y: cand.cy,
51319
+ x: chosen.x,
51320
+ y: chosen.y,
50413
51321
  text: cand.text,
50414
51322
  anchor: "middle",
50415
51323
  color: cand.color,
@@ -50427,7 +51335,7 @@ function placeContextLabels(args) {
50427
51335
  }
50428
51336
  return placed;
50429
51337
  }
50430
- 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;
50431
51339
  var init_context_labels = __esm({
50432
51340
  "src/map/context-labels.ts"() {
50433
51341
  "use strict";
@@ -50445,6 +51353,9 @@ var init_context_labels = __esm({
50445
51353
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50446
51354
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50447
51355
  COUNTRY_FADE_MAX = 45;
51356
+ MAX_COUNTRY_POSITIONS = 4;
51357
+ COUNTRY_POS_GRID = 5;
51358
+ COUNTRY_POS_TOPN_MARGIN = 3;
50448
51359
  KIND_ORDER = {
50449
51360
  ocean: 0,
50450
51361
  sea: 1,
@@ -50538,6 +51449,84 @@ function decodeLayer(topo) {
50538
51449
  decodeCache.set(topo, out);
50539
51450
  return out;
50540
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
+ }
50541
51530
  function projectionFor(family, extent3) {
50542
51531
  switch (family) {
50543
51532
  case "albers-usa":
@@ -51365,6 +52354,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51365
52354
  if (p[1] < minY) minY = p[1];
51366
52355
  if (p[1] > maxY) maxY = p[1];
51367
52356
  }
52357
+ r.bbox = [minX, minY, maxX, maxY];
52358
+ r.rings = rings;
51368
52359
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51369
52360
  });
51370
52361
  const fillAt = (x, y) => {
@@ -52555,6 +53546,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52555
53546
  for (const box of insets)
52556
53547
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52557
53548
  const countryCandidates = [];
53549
+ const rawCountries = [];
52558
53550
  for (const f of worldLayer.values()) {
52559
53551
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52560
53552
  if (!iso || regionById.has(iso)) continue;
@@ -52571,11 +53563,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52571
53563
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52572
53564
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52573
53565
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52574
- countryCandidates.push({
53566
+ rawCountries.push({
53567
+ f,
53568
+ iso,
52575
53569
  name: f.properties?.name ?? iso,
52576
53570
  bbox: [x0, y0, x1, y1],
52577
53571
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52578
- 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 } : {}
52579
53619
  });
52580
53620
  }
52581
53621
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52609,6 +53649,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52609
53649
  palette,
52610
53650
  project,
52611
53651
  collides,
53652
+ contentPoints,
52612
53653
  // Water labels must stay over open water — `fillAt` returns the ocean
52613
53654
  // backdrop colour off-land and a region fill on-land (lakes/states count
52614
53655
  // as land here, which is the safe side for an ocean name).
@@ -52861,7 +53902,7 @@ function ringToCoastPaths(ring, frame) {
52861
53902
  function coastlineOuterRings(regions, minExtent, frame) {
52862
53903
  const paths = [];
52863
53904
  for (const r of regions) {
52864
- const rings = parsePathRings(r.d);
53905
+ const rings = r.rings ?? parsePathRings(r.d);
52865
53906
  for (let i = 0; i < rings.length; i++) {
52866
53907
  const ring = rings[i];
52867
53908
  if (ring.length < 3) continue;
@@ -52925,6 +53966,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52925
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);
52926
53967
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", layout.background);
52927
53968
  const defs = svg.append("defs");
53969
+ const uid = mapInstanceCounter++;
53970
+ const nid = (base) => `${base}__m${uid}`;
52928
53971
  const arrowSize = (w) => Math.min(15, 7 + w * 0.95);
52929
53972
  const haloColor = palette.bg;
52930
53973
  const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
@@ -52957,8 +54000,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52957
54000
  for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
52958
54001
  if (layout.relief.length && layout.reliefHatch) {
52959
54002
  const h = layout.reliefHatch;
52960
- const rangeClipId = "dgmo-relief-clip";
52961
- const landClipId = "dgmo-relief-land";
54003
+ const rangeClipId = nid("dgmo-relief-clip");
54004
+ const landClipId = nid("dgmo-relief-land");
52962
54005
  const rangeClip = defs.append("clipPath").attr("id", rangeClipId);
52963
54006
  for (const s of layout.relief) rangeClip.append("path").attr("d", s.d);
52964
54007
  const landClip = defs.append("clipPath").attr("id", landClipId);
@@ -52971,7 +54014,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52971
54014
  }
52972
54015
  if (layout.coastlineStyle) {
52973
54016
  const cs = layout.coastlineStyle;
52974
- const maskId = "dgmo-map-water-mask";
54017
+ const maskId = nid("dgmo-map-water-mask");
52975
54018
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
52976
54019
  mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
52977
54020
  const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
@@ -52989,7 +54032,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52989
54032
  appendWaterLines(
52990
54033
  gWater,
52991
54034
  defs,
52992
- "dgmo-map-coast",
54035
+ nid("dgmo-map-coast"),
52993
54036
  // Pass the canvas frame so edges collinear with it (the antimeridian on a
52994
54037
  // world map, regional clipExtent cuts) don't get ringed as fake coast —
52995
54038
  // land runs cleanly to the render-area edge.
@@ -53026,7 +54069,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53026
54069
  (l) => l.poiId !== void 0 && !l.hidden
53027
54070
  );
53028
54071
  if (poiLabels.length) {
53029
- const patchBlurId = "dgmo-map-label-patch-blur";
54072
+ const patchBlurId = nid("dgmo-map-label-patch-blur");
53030
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);
53031
54074
  const PAD = 8;
53032
54075
  const buildPatch = (labels, maskId, decoCluster) => {
@@ -53054,14 +54097,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53054
54097
  if (decoCluster !== void 0)
53055
54098
  gPatch.attr("data-cluster-deco", decoCluster);
53056
54099
  for (const r of layout.regions) {
53057
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53058
- for (const ring of parsePathRings(r.d))
53059
- for (const [px, py] of ring) {
53060
- if (px < minX) minX = px;
53061
- if (px > maxX) maxX = px;
53062
- if (py < minY) minY = py;
53063
- if (py > maxY) maxY = py;
53064
- }
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
+ }
53065
54116
  const hit = blobRects.some(
53066
54117
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53067
54118
  );
@@ -53071,7 +54122,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53071
54122
  if (clusterUi) {
53072
54123
  buildPatch(
53073
54124
  poiLabels.filter((l) => l.clusterMember === void 0),
53074
- "dgmo-map-label-patch"
54125
+ nid("dgmo-map-label-patch")
53075
54126
  );
53076
54127
  const byCluster = /* @__PURE__ */ new Map();
53077
54128
  for (const l of poiLabels) {
@@ -53082,9 +54133,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53082
54133
  }
53083
54134
  let ci = 0;
53084
54135
  for (const [cid, labs] of byCluster)
53085
- buildPatch(labs, `dgmo-map-label-patch-c${ci++}`, cid);
54136
+ buildPatch(labs, nid(`dgmo-map-label-patch-c${ci++}`), cid);
53086
54137
  } else {
53087
- buildPatch(poiLabels, "dgmo-map-label-patch");
54138
+ buildPatch(poiLabels, nid("dgmo-map-label-patch"));
53088
54139
  }
53089
54140
  }
53090
54141
  if (layout.insets.length) {
@@ -53093,7 +54144,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53093
54144
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
53094
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");
53095
54146
  if (box.contextLand) {
53096
- const clipId = `dgmo-map-inset-clip-${bi}`;
54147
+ const clipId = nid(`dgmo-map-inset-clip-${bi}`);
53097
54148
  defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
53098
54149
  insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
53099
54150
  }
@@ -53101,7 +54152,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53101
54152
  for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
53102
54153
  if (layout.coastlineStyle) {
53103
54154
  const cs = layout.coastlineStyle;
53104
- const maskId = "dgmo-map-inset-water-mask";
54155
+ const maskId = nid("dgmo-map-inset-water-mask");
53105
54156
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
53106
54157
  const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
53107
54158
  for (const box of layout.insets) {
@@ -53110,7 +54161,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53110
54161
  }
53111
54162
  layout.insets.forEach((box, bi) => {
53112
54163
  if (box.contextLand)
53113
- 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}`)})`);
53114
54165
  });
53115
54166
  for (const r of layout.insetRegions)
53116
54167
  if (r.id !== "lake")
@@ -53118,7 +54169,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53118
54169
  for (const r of layout.insetRegions)
53119
54170
  if (r.id === "lake")
53120
54171
  mask.append("path").attr("d", r.d).attr("fill", "white");
53121
- const clipId = "dgmo-map-inset-water-clip";
54172
+ const clipId = nid("dgmo-map-inset-water-clip");
53122
54173
  const clip = defs.append("clipPath").attr("id", clipId);
53123
54174
  for (const box of layout.insets) {
53124
54175
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
@@ -53128,7 +54179,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53128
54179
  appendWaterLines(
53129
54180
  gInsetWater,
53130
54181
  defs,
53131
- "dgmo-map-inset-coast",
54182
+ nid("dgmo-map-inset-coast"),
53132
54183
  coastlineOuterRings(layout.insetRegions, cs.minExtent),
53133
54184
  cs,
53134
54185
  layout.background
@@ -53157,7 +54208,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53157
54208
  }
53158
54209
  wireSync(p, leg.lineNumber);
53159
54210
  if (leg.arrow) {
53160
- const id = `dgmo-map-arrow-${i}`;
54211
+ const id = nid(`dgmo-map-arrow-${i}`);
53161
54212
  const s = arrowSize(leg.width);
53162
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);
53163
54214
  p.attr("marker-end", `url(#${id})`);
@@ -53353,7 +54404,7 @@ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic
53353
54404
  }
53354
54405
  return t;
53355
54406
  }
53356
- var LABEL_FONT;
54407
+ var LABEL_FONT, mapInstanceCounter;
53357
54408
  var init_renderer16 = __esm({
53358
54409
  "src/map/renderer.ts"() {
53359
54410
  "use strict";
@@ -53365,6 +54416,7 @@ var init_renderer16 = __esm({
53365
54416
  init_legend_band();
53366
54417
  init_layout15();
53367
54418
  LABEL_FONT = 11;
54419
+ mapInstanceCounter = 0;
53368
54420
  }
53369
54421
  });
53370
54422
 
@@ -54287,7 +55339,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54287
55339
  const tasksAll = [...allTasks(parsed)];
54288
55340
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54289
55341
  const solid = parsed.options["solid-fill"] === "on";
54290
- const surfaceBg = isDark ? palette.surface : palette.bg;
55342
+ const surfaceBg = themeBaseBg(palette, isDark);
54291
55343
  const roleCount = Math.max(1, parsed.roles.length);
54292
55344
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54293
55345
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54296,7 +55348,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54296
55348
  const sHMargin = ctx.aesthetic(H_MARGIN);
54297
55349
  const sVMargin = ctx.aesthetic(V_MARGIN3);
54298
55350
  const sTitleAreaHeight = ctx.structural(TITLE_AREA_HEIGHT4);
54299
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT6);
55351
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT5);
54300
55352
  const sRowHeight = ctx.structural(ROW_HEIGHT);
54301
55353
  const sPhaseHeight = ctx.structural(PHASE_HEIGHT);
54302
55354
  const sTaskLabelMin = ctx.structural(TASK_LABEL_MIN);
@@ -54321,7 +55373,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54321
55373
  const sLegendLetterFont = ctx.text(LEGEND_LETTER_FONT);
54322
55374
  const sViolationLineHeight = ctx.structural(VIOLATION_LINE_HEIGHT);
54323
55375
  const sStackTopGap = ctx.structural(STACK_TOP_GAP);
54324
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH12);
55376
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
54325
55377
  const sNodeRadius = ctx.structural(NODE_RADIUS3);
54326
55378
  const innerWidth = Math.max(0, width - 2 * sHMargin);
54327
55379
  let roleColW = Math.max(
@@ -54465,10 +55517,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54465
55517
  const cx = roleX(i) + sColumnInset;
54466
55518
  const cw = roleColW - 2 * sColumnInset;
54467
55519
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54468
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55520
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54469
55521
  const headerFill = mix(
54470
55522
  roleColor,
54471
- isDark ? palette.surface : palette.bg,
55523
+ themeBaseBg(palette, isDark),
54472
55524
  30
54473
55525
  );
54474
55526
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -54867,7 +55919,7 @@ function parseQuotedSegments(message) {
54867
55919
  out.push({ text: message.slice(last), bold: false });
54868
55920
  return out;
54869
55921
  }
54870
- 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;
54871
55923
  var init_renderer19 = __esm({
54872
55924
  "src/raci/renderer.ts"() {
54873
55925
  "use strict";
@@ -54878,6 +55930,7 @@ var init_renderer19 = __esm({
54878
55930
  init_variants();
54879
55931
  init_scaling();
54880
55932
  init_text_measure();
55933
+ init_visual_conventions();
54881
55934
  MARKER_LABELS = {
54882
55935
  raci: { R: "Responsible", A: "Accountable", C: "Consulted", I: "Informed" },
54883
55936
  rasci: {
@@ -54899,7 +55952,7 @@ var init_renderer19 = __esm({
54899
55952
  TITLE_LEGEND_GAP = 16;
54900
55953
  LEGEND_LABEL_FONT = 12;
54901
55954
  LEGEND_LETTER_FONT = 14;
54902
- HEADER_HEIGHT6 = 36;
55955
+ HEADER_HEIGHT5 = 36;
54903
55956
  ROW_HEIGHT = 36;
54904
55957
  PHASE_HEIGHT = 40;
54905
55958
  TASK_LABEL_MIN = 200;
@@ -54919,7 +55972,6 @@ var init_renderer19 = __esm({
54919
55972
  ROLE_HEADER_FONT = 12;
54920
55973
  PHASE_FONT = 13;
54921
55974
  TINT_PCT = 25;
54922
- NODE_STROKE_WIDTH12 = 1.5;
54923
55975
  NODE_RADIUS3 = 6;
54924
55976
  AUTO_ACCENTS = [
54925
55977
  "blue",
@@ -55520,11 +56572,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55520
56572
  const lines = splitParticipantLabel(
55521
56573
  p.label,
55522
56574
  labelTextWidth(PARTICIPANT_BOX_WIDTH),
55523
- LABEL_FONT_SIZE6
56575
+ LABEL_FONT_SIZE5
55524
56576
  );
55525
56577
  if (lines.length === 0) continue;
55526
56578
  const widest = Math.max(
55527
- ...lines.map((l) => measureText(l, LABEL_FONT_SIZE6))
56579
+ ...lines.map((l) => measureText(l, LABEL_FONT_SIZE5))
55528
56580
  );
55529
56581
  const labelWidth2 = widest + 10;
55530
56582
  uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
@@ -55558,7 +56610,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55558
56610
  const sSelfCallWidth = ctx.structural(SELF_CALL_WIDTH);
55559
56611
  const sNoteTextWidthMax = sNoteMaxW - sNotePadH * 2 - sNoteFold;
55560
56612
  const sNoteLaneMax = sGap - sActivationWidth - sNoteGap;
55561
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE6);
56613
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE5);
55562
56614
  const sLabelTextWidth = labelTextWidth(sBoxW);
55563
56615
  const participantIndexMap = /* @__PURE__ */ new Map();
55564
56616
  participants.forEach((p, i) => participantIndexMap.set(p.id, i));
@@ -56195,7 +57247,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56195
57247
  const groupTagColor = getTagColor(groupTagValue || void 0);
56196
57248
  const fillColor = groupTagColor ? mix(
56197
57249
  groupTagColor,
56198
- isDark ? palette.surface : palette.bg,
57250
+ themeBaseBg(palette, isDark),
56199
57251
  isDark ? 15 : 20
56200
57252
  ) : isDark ? palette.surface : palette.bg;
56201
57253
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56249,7 +57301,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56249
57301
  participantG.append("title").text("Click to expand");
56250
57302
  const pFill = effectiveTagColor ? mix(
56251
57303
  effectiveTagColor,
56252
- isDark ? palette.surface : palette.bg,
57304
+ themeBaseBg(palette, isDark),
56253
57305
  isDark ? 30 : 40
56254
57306
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56255
57307
  const pStroke = effectiveTagColor || palette.border;
@@ -56456,7 +57508,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56456
57508
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56457
57509
  const actTagColor = getTagColor(actTagValue);
56458
57510
  const actBaseColor = actTagColor || palette.primary;
56459
- 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));
56460
57512
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56461
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");
56462
57514
  if (tagKey && actTagValue) {
@@ -56695,7 +57747,7 @@ function buildNoteMessageMap(elements) {
56695
57747
  walk(elements);
56696
57748
  return map;
56697
57749
  }
56698
- 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) {
56699
57751
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
56700
57752
  if (tagAttr) {
56701
57753
  g.attr(`data-tag-${tagAttr.key}`, tagAttr.value);
@@ -56743,7 +57795,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
56743
57795
  });
56744
57796
  }
56745
57797
  }
56746
- 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;
56747
57799
  var init_renderer20 = __esm({
56748
57800
  "src/sequence/renderer.ts"() {
56749
57801
  "use strict";
@@ -56763,7 +57815,7 @@ var init_renderer20 = __esm({
56763
57815
  PARTICIPANT_GAP = 160;
56764
57816
  PARTICIPANT_BOX_WIDTH = 120;
56765
57817
  PARTICIPANT_BOX_HEIGHT = 50;
56766
- LABEL_FONT_SIZE6 = 13;
57818
+ LABEL_FONT_SIZE5 = 13;
56767
57819
  TOP_MARGIN = 20;
56768
57820
  TITLE_HEIGHT8 = 30;
56769
57821
  PARTICIPANT_Y_OFFSET = 10;
@@ -58157,7 +59209,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58157
59209
  const textColor = palette.text;
58158
59210
  const mutedColor = palette.border;
58159
59211
  const bgColor = palette.bg;
58160
- const bg = isDark ? palette.surface : palette.bg;
59212
+ const bg = themeBaseBg(palette, isDark);
58161
59213
  const colors = getSeriesColors(palette);
58162
59214
  const groupColorMap = /* @__PURE__ */ new Map();
58163
59215
  timelineGroups.forEach((grp, i) => {
@@ -58572,9 +59624,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58572
59624
  drawLegend2();
58573
59625
  }
58574
59626
  }
58575
- 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) {
58576
59628
  const {
58577
59629
  width,
59630
+ height,
58578
59631
  tooltip,
58579
59632
  solid,
58580
59633
  textColor,
@@ -58626,9 +59679,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58626
59679
  const rowH = ctx.structural(28);
58627
59680
  const innerHeight = rowH * sorted.length;
58628
59681
  const usedHeight = margin.top + innerHeight + margin.bottom;
59682
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59683
+ const svgHeight = fitToContainer ? height : usedHeight;
58629
59684
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58630
- 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);
58631
- 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) {
58632
59687
  svg.attr("width", "100%");
58633
59688
  }
58634
59689
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60302,7 +61357,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60302
61357
  onClickItem
60303
61358
  );
60304
61359
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60305
- const bg = isDark ? palette.surface : palette.bg;
61360
+ const bg = themeBaseBg(palette, isDark);
60306
61361
  const getQuadrantColor = (label, defaultIdx) => {
60307
61362
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60308
61363
  };
@@ -60639,18 +61694,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60639
61694
  palette,
60640
61695
  viewState,
60641
61696
  options,
60642
- exportMode
61697
+ exportMode,
61698
+ isDark: theme === "dark"
60643
61699
  };
60644
61700
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
60645
61701
  return (handler ?? exportVisualization)(ctx);
60646
61702
  }
61703
+ function ctxTagOverride(ctx) {
61704
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61705
+ }
60647
61706
  async function exportOrg(ctx) {
60648
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61707
+ const { content, theme, palette, viewState, exportMode } = ctx;
60649
61708
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
60650
61709
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
60651
61710
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
60652
61711
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
60653
- const isDark = theme === "dark";
61712
+ const isDark = ctx.isDark;
60654
61713
  const effectivePalette = await resolveExportPalette(theme, palette);
60655
61714
  const orgParsed = parseOrg2(content, effectivePalette);
60656
61715
  if (orgParsed.error) return "";
@@ -60658,7 +61717,7 @@ async function exportOrg(ctx) {
60658
61717
  const activeTagGroup = resolveActiveTagGroup(
60659
61718
  orgParsed.tagGroups,
60660
61719
  orgParsed.options["active-tag"],
60661
- viewState?.tag ?? options?.tagGroup
61720
+ ctxTagOverride(ctx)
60662
61721
  );
60663
61722
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60664
61723
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60691,12 +61750,12 @@ async function exportOrg(ctx) {
60691
61750
  return finalizeSvgExport(container, theme, effectivePalette);
60692
61751
  }
60693
61752
  async function exportSitemap(ctx) {
60694
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61753
+ const { content, theme, palette, viewState, exportMode } = ctx;
60695
61754
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
60696
61755
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
60697
61756
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
60698
61757
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
60699
- const isDark = theme === "dark";
61758
+ const isDark = ctx.isDark;
60700
61759
  const effectivePalette = await resolveExportPalette(theme, palette);
60701
61760
  const sitemapParsed = parseSitemap2(content, effectivePalette);
60702
61761
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -60704,7 +61763,7 @@ async function exportSitemap(ctx) {
60704
61763
  const activeTagGroup = resolveActiveTagGroup(
60705
61764
  sitemapParsed.tagGroups,
60706
61765
  sitemapParsed.options["active-tag"],
60707
- viewState?.tag ?? options?.tagGroup
61766
+ ctxTagOverride(ctx)
60708
61767
  );
60709
61768
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60710
61769
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60735,7 +61794,7 @@ async function exportSitemap(ctx) {
60735
61794
  return finalizeSvgExport(container, theme, effectivePalette);
60736
61795
  }
60737
61796
  async function exportKanban(ctx) {
60738
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61797
+ const { content, theme, palette, viewState, exportMode } = ctx;
60739
61798
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
60740
61799
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
60741
61800
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -60747,11 +61806,11 @@ async function exportKanban(ctx) {
60747
61806
  document.body.appendChild(container);
60748
61807
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
60749
61808
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
60750
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61809
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
60751
61810
  activeTagGroup: resolveActiveTagGroup(
60752
61811
  kanbanParsed.tagGroups,
60753
61812
  kanbanParsed.options["active-tag"],
60754
- viewState?.tag ?? options?.tagGroup
61813
+ ctxTagOverride(ctx)
60755
61814
  ),
60756
61815
  currentSwimlaneGroup: viewState?.swim ?? null,
60757
61816
  ...kanbanCollapsedLanes !== void 0 && {
@@ -60784,7 +61843,7 @@ async function exportClass(ctx) {
60784
61843
  classParsed,
60785
61844
  classLayout,
60786
61845
  effectivePalette,
60787
- theme === "dark",
61846
+ ctx.isDark,
60788
61847
  void 0,
60789
61848
  { width: exportWidth, height: exportHeight },
60790
61849
  void 0,
@@ -60793,7 +61852,7 @@ async function exportClass(ctx) {
60793
61852
  return finalizeSvgExport(container, theme, effectivePalette);
60794
61853
  }
60795
61854
  async function exportEr(ctx) {
60796
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61855
+ const { content, theme, palette, viewState, exportMode } = ctx;
60797
61856
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
60798
61857
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
60799
61858
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -60811,13 +61870,13 @@ async function exportEr(ctx) {
60811
61870
  erParsed,
60812
61871
  erLayout,
60813
61872
  effectivePalette,
60814
- theme === "dark",
61873
+ ctx.isDark,
60815
61874
  void 0,
60816
61875
  { width: exportWidth, height: exportHeight },
60817
61876
  resolveActiveTagGroup(
60818
61877
  erParsed.tagGroups,
60819
61878
  erParsed.options["active-tag"],
60820
- viewState?.tag ?? options?.tagGroup
61879
+ ctxTagOverride(ctx)
60821
61880
  ),
60822
61881
  viewState?.sem,
60823
61882
  exportMode
@@ -60825,7 +61884,7 @@ async function exportEr(ctx) {
60825
61884
  return finalizeSvgExport(container, theme, effectivePalette);
60826
61885
  }
60827
61886
  async function exportBoxesAndLines(ctx) {
60828
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61887
+ const { content, theme, palette, viewState, exportMode } = ctx;
60829
61888
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
60830
61889
  const effectivePalette = await resolveExportPalette(theme, palette);
60831
61890
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -60845,13 +61904,13 @@ async function exportBoxesAndLines(ctx) {
60845
61904
  const exportWidth = blLayout.width + PADDING3 * 2;
60846
61905
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
60847
61906
  const container = createExportContainer(exportWidth, exportHeight);
60848
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61907
+ const blActiveTagGroup = ctxTagOverride(ctx);
60849
61908
  renderBoxesAndLinesForExport2(
60850
61909
  container,
60851
61910
  blParsed,
60852
61911
  blLayout,
60853
61912
  effectivePalette,
60854
- theme === "dark",
61913
+ ctx.isDark,
60855
61914
  {
60856
61915
  exportDims: { width: exportWidth, height: exportHeight },
60857
61916
  ...blActiveTagGroup !== void 0 && {
@@ -60866,12 +61925,12 @@ async function exportBoxesAndLines(ctx) {
60866
61925
  return finalizeSvgExport(container, theme, effectivePalette);
60867
61926
  }
60868
61927
  async function exportMindmap(ctx) {
60869
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61928
+ const { content, theme, palette, viewState, exportMode } = ctx;
60870
61929
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
60871
61930
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
60872
61931
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
60873
61932
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
60874
- const isDark = theme === "dark";
61933
+ const isDark = ctx.isDark;
60875
61934
  const effectivePalette = await resolveExportPalette(theme, palette);
60876
61935
  const mmParsed = parseMindmap2(content, effectivePalette);
60877
61936
  if (mmParsed.error) return "";
@@ -60879,7 +61938,7 @@ async function exportMindmap(ctx) {
60879
61938
  const activeTagGroup = resolveActiveTagGroup(
60880
61939
  mmParsed.tagGroups,
60881
61940
  mmParsed.options["active-tag"],
60882
- viewState?.tag ?? options?.tagGroup
61941
+ ctxTagOverride(ctx)
60883
61942
  );
60884
61943
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
60885
61944
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60929,7 +61988,7 @@ async function exportWireframe(ctx) {
60929
61988
  wireframeParsed,
60930
61989
  wireframeLayout,
60931
61990
  effectivePalette,
60932
- theme === "dark",
61991
+ ctx.isDark,
60933
61992
  void 0,
60934
61993
  { width: exportWidth, height: exportHeight },
60935
61994
  theme
@@ -60937,7 +61996,7 @@ async function exportWireframe(ctx) {
60937
61996
  return finalizeSvgExport(container, theme, effectivePalette);
60938
61997
  }
60939
61998
  async function exportC4(ctx) {
60940
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61999
+ const { content, theme, palette, viewState, exportMode } = ctx;
60941
62000
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
60942
62001
  const {
60943
62002
  layoutC4Context: layoutC4Context2,
@@ -60949,9 +62008,9 @@ async function exportC4(ctx) {
60949
62008
  const effectivePalette = await resolveExportPalette(theme, palette);
60950
62009
  const c4Parsed = parseC42(content, effectivePalette);
60951
62010
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
60952
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
60953
- const c4System = options?.c4System ?? viewState?.c4s;
60954
- 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;
60955
62014
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
60956
62015
  if (c4Layout.nodes.length === 0) return "";
60957
62016
  const PADDING3 = 20;
@@ -60965,13 +62024,13 @@ async function exportC4(ctx) {
60965
62024
  c4Parsed,
60966
62025
  c4Layout,
60967
62026
  effectivePalette,
60968
- theme === "dark",
62027
+ ctx.isDark,
60969
62028
  void 0,
60970
62029
  { width: exportWidth, height: exportHeight },
60971
62030
  resolveActiveTagGroup(
60972
62031
  c4Parsed.tagGroups,
60973
62032
  c4Parsed.options["active-tag"],
60974
- viewState?.tag ?? options?.tagGroup
62033
+ ctxTagOverride(ctx)
60975
62034
  ),
60976
62035
  exportMode
60977
62036
  );
@@ -60992,14 +62051,14 @@ async function exportFlowchart(ctx) {
60992
62051
  fcParsed,
60993
62052
  layout,
60994
62053
  effectivePalette,
60995
- theme === "dark",
62054
+ ctx.isDark,
60996
62055
  void 0,
60997
62056
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
60998
62057
  );
60999
62058
  return finalizeSvgExport(container, theme, effectivePalette);
61000
62059
  }
61001
62060
  async function exportInfra(ctx) {
61002
- const { content, theme, palette, viewState, options } = ctx;
62061
+ const { content, theme, palette, viewState } = ctx;
61003
62062
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
61004
62063
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
61005
62064
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61012,7 +62071,7 @@ async function exportInfra(ctx) {
61012
62071
  const activeTagGroup = resolveActiveTagGroup(
61013
62072
  infraParsed.tagGroups,
61014
62073
  infraParsed.options["active-tag"],
61015
- viewState?.tag ?? options?.tagGroup
62074
+ ctxTagOverride(ctx)
61016
62075
  );
61017
62076
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61018
62077
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61030,7 +62089,7 @@ async function exportInfra(ctx) {
61030
62089
  container,
61031
62090
  infraLayout,
61032
62091
  effectivePalette,
61033
- theme === "dark",
62092
+ ctx.isDark,
61034
62093
  showInfraTitle ? infraParsed.title : null,
61035
62094
  showInfraTitle ? infraParsed.titleLineNumber : null,
61036
62095
  infraTagGroups,
@@ -61077,7 +62136,7 @@ async function exportPert(ctx) {
61077
62136
  pertResolved,
61078
62137
  pertLayout,
61079
62138
  effectivePalette,
61080
- theme === "dark",
62139
+ ctx.isDark,
61081
62140
  {
61082
62141
  title: pertParsed.title,
61083
62142
  exportDims: { width: exportW, height: exportH },
@@ -61090,7 +62149,7 @@ async function exportPert(ctx) {
61090
62149
  return finalizeSvgExport(container, theme, effectivePalette);
61091
62150
  }
61092
62151
  async function exportGantt(ctx) {
61093
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62152
+ const { content, theme, palette, viewState, exportMode } = ctx;
61094
62153
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61095
62154
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61096
62155
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61108,7 +62167,7 @@ async function exportGantt(ctx) {
61108
62167
  container,
61109
62168
  resolved,
61110
62169
  effectivePalette,
61111
- theme === "dark",
62170
+ ctx.isDark,
61112
62171
  {
61113
62172
  ...ganttCollapsedGroups !== void 0 && {
61114
62173
  collapsedGroups: ganttCollapsedGroups
@@ -61122,7 +62181,7 @@ async function exportGantt(ctx) {
61122
62181
  currentActiveGroup: resolveActiveTagGroup(
61123
62182
  resolved.tagGroups,
61124
62183
  resolved.options.activeTag ?? void 0,
61125
- viewState?.tag ?? options?.tagGroup
62184
+ ctxTagOverride(ctx)
61126
62185
  ),
61127
62186
  exportMode
61128
62187
  },
@@ -61145,7 +62204,7 @@ async function exportState(ctx) {
61145
62204
  stateParsed,
61146
62205
  layout,
61147
62206
  effectivePalette,
61148
- theme === "dark",
62207
+ ctx.isDark,
61149
62208
  void 0,
61150
62209
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61151
62210
  );
@@ -61165,7 +62224,7 @@ async function exportTechRadar(ctx) {
61165
62224
  container,
61166
62225
  radarParsed,
61167
62226
  effectivePalette,
61168
- theme === "dark",
62227
+ ctx.isDark,
61169
62228
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61170
62229
  viewState,
61171
62230
  exportMode
@@ -61182,13 +62241,13 @@ async function exportJourneyMap(ctx) {
61182
62241
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61183
62242
  return "";
61184
62243
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61185
- isDark: theme === "dark"
62244
+ isDark: ctx.isDark
61186
62245
  });
61187
62246
  const container = createExportContainer(
61188
62247
  jmLayout.totalWidth,
61189
62248
  jmLayout.totalHeight
61190
62249
  );
61191
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62250
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61192
62251
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61193
62252
  exportMode
61194
62253
  });
@@ -61206,7 +62265,7 @@ async function exportCycle(ctx) {
61206
62265
  container,
61207
62266
  cycleParsed,
61208
62267
  effectivePalette,
61209
- theme === "dark",
62268
+ ctx.isDark,
61210
62269
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61211
62270
  viewState,
61212
62271
  exportMode
@@ -61243,7 +62302,7 @@ async function exportMap(ctx) {
61243
62302
  mapResolved,
61244
62303
  mapData,
61245
62304
  effectivePalette,
61246
- theme === "dark",
62305
+ ctx.isDark,
61247
62306
  dims
61248
62307
  );
61249
62308
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61260,7 +62319,7 @@ async function exportPyramid(ctx) {
61260
62319
  container,
61261
62320
  pyramidParsed,
61262
62321
  effectivePalette,
61263
- theme === "dark",
62322
+ ctx.isDark,
61264
62323
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61265
62324
  );
61266
62325
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61277,7 +62336,7 @@ async function exportRing(ctx) {
61277
62336
  container,
61278
62337
  ringParsed,
61279
62338
  effectivePalette,
61280
- theme === "dark",
62339
+ ctx.isDark,
61281
62340
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61282
62341
  );
61283
62342
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61294,7 +62353,7 @@ async function exportRaci(ctx) {
61294
62353
  container,
61295
62354
  raciParsed,
61296
62355
  effectivePalette,
61297
- theme === "dark",
62356
+ ctx.isDark,
61298
62357
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61299
62358
  );
61300
62359
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61317,7 +62376,7 @@ async function exportSlope(ctx) {
61317
62376
  container,
61318
62377
  parsed,
61319
62378
  effectivePalette,
61320
- theme === "dark",
62379
+ ctx.isDark,
61321
62380
  void 0,
61322
62381
  dims
61323
62382
  );
@@ -61333,14 +62392,14 @@ async function exportArc(ctx) {
61333
62392
  container,
61334
62393
  parsed,
61335
62394
  effectivePalette,
61336
- theme === "dark",
62395
+ ctx.isDark,
61337
62396
  void 0,
61338
62397
  dims
61339
62398
  );
61340
62399
  return finalizeSvgExport(container, theme, effectivePalette);
61341
62400
  }
61342
62401
  async function exportTimeline(ctx) {
61343
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62402
+ const { content, theme, palette, viewState, exportMode } = ctx;
61344
62403
  const parsed = parseTimeline(content, palette);
61345
62404
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61346
62405
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61349,13 +62408,13 @@ async function exportTimeline(ctx) {
61349
62408
  container,
61350
62409
  parsed,
61351
62410
  effectivePalette,
61352
- theme === "dark",
62411
+ ctx.isDark,
61353
62412
  void 0,
61354
62413
  dims,
61355
62414
  resolveActiveTagGroup(
61356
62415
  parsed.timelineTagGroups,
61357
62416
  parsed.timelineActiveTag,
61358
- viewState?.tag ?? options?.tagGroup
62417
+ ctxTagOverride(ctx)
61359
62418
  ),
61360
62419
  viewState?.swim,
61361
62420
  void 0,
@@ -61374,7 +62433,7 @@ async function exportWordcloud(ctx) {
61374
62433
  container,
61375
62434
  parsed,
61376
62435
  effectivePalette,
61377
- theme === "dark",
62436
+ ctx.isDark,
61378
62437
  dims
61379
62438
  );
61380
62439
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61389,7 +62448,7 @@ async function exportVenn(ctx) {
61389
62448
  container,
61390
62449
  parsed,
61391
62450
  effectivePalette,
61392
- theme === "dark",
62451
+ ctx.isDark,
61393
62452
  void 0,
61394
62453
  dims
61395
62454
  );
@@ -61405,14 +62464,14 @@ async function exportQuadrant(ctx) {
61405
62464
  container,
61406
62465
  parsed,
61407
62466
  effectivePalette,
61408
- theme === "dark",
62467
+ ctx.isDark,
61409
62468
  void 0,
61410
62469
  dims
61411
62470
  );
61412
62471
  return finalizeSvgExport(container, theme, effectivePalette);
61413
62472
  }
61414
62473
  async function exportVisualization(ctx) {
61415
- const { content, theme, palette, viewState, options } = ctx;
62474
+ const { content, theme, palette, viewState } = ctx;
61416
62475
  const parsed = parseVisualization(content, palette);
61417
62476
  if (parsed.type !== "sequence") {
61418
62477
  if (parsed.error) {
@@ -61423,7 +62482,7 @@ async function exportVisualization(ctx) {
61423
62482
  }
61424
62483
  }
61425
62484
  const effectivePalette = await resolveExportPalette(theme, palette);
61426
- const isDark = theme === "dark";
62485
+ const isDark = ctx.isDark;
61427
62486
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61428
62487
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61429
62488
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61431,7 +62490,7 @@ async function exportVisualization(ctx) {
61431
62490
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61432
62491
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61433
62492
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61434
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62493
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61435
62494
  renderSequenceDiagram2(
61436
62495
  container,
61437
62496
  seqParsed,
@@ -62205,7 +63264,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
62205
63264
 
62206
63265
  // src/auto/index.ts
62207
63266
  init_safe_href();
62208
- var VERSION = "0.30.0";
63267
+ var VERSION = "0.32.0";
62209
63268
  var DEFAULTS = {
62210
63269
  theme: "auto",
62211
63270
  palette: "slate",